summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bancache.cpp23
-rw-r--r--src/base.cpp69
-rw-r--r--src/channels.cpp332
-rw-r--r--src/cidr.cpp6
-rw-r--r--src/command_parse.cpp88
-rw-r--r--src/commands.cpp2
-rw-r--r--src/configparser.cpp8
-rw-r--r--src/configreader.cpp118
-rw-r--r--src/coremods/core_channel/cmd_invite.cpp95
-rw-r--r--src/coremods/core_channel/cmd_join.cpp2
-rw-r--r--src/coremods/core_channel/cmd_kick.cpp60
-rw-r--r--src/coremods/core_channel/cmd_names.cpp64
-rw-r--r--src/coremods/core_channel/cmd_topic.cpp27
-rw-r--r--src/coremods/core_channel/core_channel.cpp85
-rw-r--r--src/coremods/core_channel/core_channel.h27
-rw-r--r--src/coremods/core_channel/invite.cpp208
-rw-r--r--src/coremods/core_channel/invite.h127
-rw-r--r--src/coremods/core_dns.cpp368
-rw-r--r--src/coremods/core_hostname_lookup.cpp31
-rw-r--r--src/coremods/core_info/cmd_admin.cpp21
-rw-r--r--src/coremods/core_info/cmd_commands.cpp14
-rw-r--r--src/coremods/core_info/cmd_info.cpp37
-rw-r--r--src/coremods/core_info/cmd_modules.cpp26
-rw-r--r--src/coremods/core_info/cmd_motd.cpp27
-rw-r--r--src/coremods/core_info/cmd_time.cpp16
-rw-r--r--src/coremods/core_info/cmd_version.cpp2
-rw-r--r--src/coremods/core_info/core_info.cpp8
-rw-r--r--src/coremods/core_info/core_info.h28
-rw-r--r--src/coremods/core_ison.cpp83
-rw-r--r--src/coremods/core_list.cpp37
-rw-r--r--src/coremods/core_loadmodule.cpp13
-rw-r--r--src/coremods/core_lusers.cpp16
-rw-r--r--src/coremods/core_oper/cmd_die.cpp36
-rw-r--r--src/coremods/core_oper/cmd_kill.cpp10
-rw-r--r--src/coremods/core_oper/cmd_oper.cpp4
-rw-r--r--src/coremods/core_oper/cmd_rehash.cpp4
-rw-r--r--src/coremods/core_oper/cmd_restart.cpp6
-rw-r--r--src/coremods/core_oper/core_oper.cpp2
-rw-r--r--src/coremods/core_oper/core_oper.h5
-rw-r--r--src/coremods/core_privmsg.cpp31
-rw-r--r--src/coremods/core_reloadmodule.cpp593
-rw-r--r--src/coremods/core_stats.cpp250
-rw-r--r--src/coremods/core_stub.cpp16
-rw-r--r--src/coremods/core_user/cmd_away.cpp4
-rw-r--r--src/coremods/core_user/cmd_mode.cpp174
-rw-r--r--src/coremods/core_user/cmd_nick.cpp46
-rw-r--r--src/coremods/core_user/cmd_part.cpp10
-rw-r--r--src/coremods/core_user/cmd_user.cpp15
-rw-r--r--src/coremods/core_user/core_user.cpp43
-rw-r--r--src/coremods/core_user/core_user.h47
-rw-r--r--src/coremods/core_userhost.cpp50
-rw-r--r--src/coremods/core_wallops.cpp3
-rw-r--r--src/coremods/core_who.cpp72
-rw-r--r--src/coremods/core_whois.cpp200
-rw-r--r--src/coremods/core_whowas.cpp245
-rw-r--r--src/coremods/core_xline/cmd_gline.cpp1
-rw-r--r--src/coremods/core_xline/cmd_kline.cpp13
-rw-r--r--src/coremods/core_xline/cmd_qline.cpp1
-rw-r--r--src/coremods/core_xline/cmd_zline.cpp1
-rw-r--r--src/coremods/core_xline/core_xline.cpp21
-rw-r--r--src/coremods/core_xline/core_xline.h1
-rw-r--r--src/cull_list.cpp6
-rw-r--r--src/dynamic.cpp12
-rw-r--r--src/filelogger.cpp7
-rw-r--r--src/hashcomp.cpp95
-rw-r--r--src/helperfuncs.cpp62
-rw-r--r--src/inspircd.cpp93
-rw-r--r--src/inspsocket.cpp414
-rw-r--r--src/inspstring.cpp16
-rw-r--r--src/listensocket.cpp62
-rw-r--r--src/listmode.cpp24
-rw-r--r--src/logger.cpp25
-rw-r--r--src/mode.cpp433
-rw-r--r--src/modes/cmode_k.cpp6
-rw-r--r--src/modes/cmode_l.cpp9
-rw-r--r--src/modes/umode_o.cpp3
-rw-r--r--src/modes/umode_s.cpp7
-rw-r--r--src/modmanager_dynamic.cpp14
-rw-r--r--src/modmanager_static.cpp5
-rw-r--r--src/modules.cpp217
-rw-r--r--src/modules/extra/m_geoip.cpp35
-rw-r--r--src/modules/extra/m_ldap.cpp670
-rw-r--r--src/modules/extra/m_mssql.cpp860
-rw-r--r--src/modules/extra/m_mysql.cpp33
-rw-r--r--src/modules/extra/m_pgsql.cpp44
-rw-r--r--src/modules/extra/m_regex_pcre.cpp10
-rw-r--r--src/modules/extra/m_regex_re2.cpp20
-rw-r--r--src/modules/extra/m_regex_stdlib.cpp5
-rw-r--r--src/modules/extra/m_regex_tre.cpp8
-rw-r--r--src/modules/extra/m_sqlite3.cpp32
-rw-r--r--src/modules/extra/m_ssl_gnutls.cpp575
-rw-r--r--src/modules/extra/m_ssl_mbedtls.cpp935
-rw-r--r--src/modules/extra/m_ssl_openssl.cpp547
-rw-r--r--src/modules/m_abbreviation.cpp7
-rw-r--r--src/modules/m_alias.cpp43
-rw-r--r--src/modules/m_allowinvite.cpp2
-rw-r--r--src/modules/m_alltime.cpp8
-rw-r--r--src/modules/m_auditorium.cpp18
-rw-r--r--src/modules/m_autoop.cpp20
-rw-r--r--src/modules/m_banredirect.cpp54
-rw-r--r--src/modules/m_bcrypt.cpp987
-rw-r--r--src/modules/m_blockamsg.cpp60
-rw-r--r--src/modules/m_blockcaps.cpp2
-rw-r--r--src/modules/m_blockcolor.cpp2
-rw-r--r--src/modules/m_botmode.cpp11
-rw-r--r--src/modules/m_callerid.cpp84
-rw-r--r--src/modules/m_cap.cpp428
-rw-r--r--src/modules/m_cban.cpp20
-rw-r--r--src/modules/m_censor.cpp6
-rw-r--r--src/modules/m_cgiirc.cpp22
-rw-r--r--src/modules/m_chanfilter.cpp12
-rw-r--r--src/modules/m_chanhistory.cpp2
-rw-r--r--src/modules/m_chanlog.cpp4
-rw-r--r--src/modules/m_channames.cpp23
-rw-r--r--src/modules/m_channelban.cpp10
-rw-r--r--src/modules/m_check.cpp256
-rw-r--r--src/modules/m_chghost.cpp10
-rw-r--r--src/modules/m_chgident.cpp7
-rw-r--r--src/modules/m_chgname.cpp7
-rw-r--r--src/modules/m_classban.cpp47
-rw-r--r--src/modules/m_clearchan.cpp17
-rw-r--r--src/modules/m_cloaking.cpp21
-rw-r--r--src/modules/m_clones.cpp8
-rw-r--r--src/modules/m_close.cpp5
-rw-r--r--src/modules/m_commonchans.cpp2
-rw-r--r--src/modules/m_conn_join.cpp5
-rw-r--r--src/modules/m_conn_umodes.cpp2
-rw-r--r--src/modules/m_conn_waitpong.cpp2
-rw-r--r--src/modules/m_customprefix.cpp3
-rw-r--r--src/modules/m_customtitle.cpp16
-rw-r--r--src/modules/m_cycle.cpp4
-rw-r--r--src/modules/m_dccallow.cpp136
-rw-r--r--src/modules/m_deaf.cpp9
-rw-r--r--src/modules/m_delayjoin.cpp23
-rw-r--r--src/modules/m_delaymsg.cpp22
-rw-r--r--src/modules/m_denychans.cpp6
-rw-r--r--src/modules/m_devoice.cpp2
-rw-r--r--src/modules/m_dnsbl.cpp68
-rw-r--r--src/modules/m_exemptchanops.cpp26
-rw-r--r--src/modules/m_filter.cpp30
-rw-r--r--src/modules/m_flashpolicyd.cpp49
-rw-r--r--src/modules/m_globalload.cpp40
-rw-r--r--src/modules/m_helpop.cpp30
-rw-r--r--src/modules/m_hidechans.cpp16
-rw-r--r--src/modules/m_hidelist.cpp87
-rw-r--r--src/modules/m_hideoper.cpp95
-rw-r--r--src/modules/m_hostcycle.cpp24
-rw-r--r--src/modules/m_httpd.cpp109
-rw-r--r--src/modules/m_httpd_acl.cpp27
-rw-r--r--src/modules/m_httpd_config.cpp17
-rw-r--r--src/modules/m_httpd_stats.cpp65
-rw-r--r--src/modules/m_ident.cpp38
-rw-r--r--src/modules/m_ircv3.cpp119
-rw-r--r--src/modules/m_ircv3_capnotify.cpp149
-rw-r--r--src/modules/m_ircv3_chghost.cpp57
-rw-r--r--src/modules/m_ircv3_echomessage.cpp70
-rw-r--r--src/modules/m_ircv3_invitenotify.cpp68
-rw-r--r--src/modules/m_joinflood.cpp19
-rw-r--r--src/modules/m_jumpserver.cpp14
-rw-r--r--src/modules/m_kicknorejoin.cpp91
-rw-r--r--src/modules/m_knock.cpp16
-rw-r--r--src/modules/m_ldapauth.cpp25
-rw-r--r--src/modules/m_ldapoper.cpp6
-rw-r--r--src/modules/m_lockserv.cpp36
-rw-r--r--src/modules/m_md5.cpp46
-rw-r--r--src/modules/m_messageflood.cpp24
-rw-r--r--src/modules/m_mlock.cpp5
-rw-r--r--src/modules/m_modenotice.cpp3
-rw-r--r--src/modules/m_monitor.cpp444
-rw-r--r--src/modules/m_muteban.cpp2
-rw-r--r--src/modules/m_namedmodes.cpp130
-rw-r--r--src/modules/m_namesx.cpp39
-rw-r--r--src/modules/m_nationalchars.cpp51
-rw-r--r--src/modules/m_nickflood.cpp25
-rw-r--r--src/modules/m_nicklock.cpp33
-rw-r--r--src/modules/m_noctcp.cpp2
-rw-r--r--src/modules/m_nokicks.cpp2
-rw-r--r--src/modules/m_nonicks.cpp11
-rw-r--r--src/modules/m_nonotice.cpp2
-rw-r--r--src/modules/m_ojoin.cpp65
-rw-r--r--src/modules/m_operchans.cpp3
-rw-r--r--src/modules/m_operlevels.cpp2
-rw-r--r--src/modules/m_operlog.cpp2
-rw-r--r--src/modules/m_opermodes.cpp2
-rw-r--r--src/modules/m_opermotd.cpp11
-rw-r--r--src/modules/m_operprefix.cpp32
-rw-r--r--src/modules/m_override.cpp48
-rw-r--r--src/modules/m_passforward.cpp2
-rw-r--r--src/modules/m_password_hash.cpp27
-rw-r--r--src/modules/m_pbkdf2.cpp262
-rw-r--r--src/modules/m_permchannels.cpp37
-rw-r--r--src/modules/m_redirect.cpp12
-rw-r--r--src/modules/m_regonlycreate.cpp2
-rw-r--r--src/modules/m_remove.cpp64
-rw-r--r--src/modules/m_repeat.cpp20
-rw-r--r--src/modules/m_restrictchans.cpp4
-rw-r--r--src/modules/m_restrictmsg.cpp5
-rw-r--r--src/modules/m_ripemd160.cpp4
-rw-r--r--src/modules/m_rline.cpp6
-rw-r--r--src/modules/m_rmode.cpp22
-rw-r--r--src/modules/m_sajoin.cpp11
-rw-r--r--src/modules/m_sakick.cpp9
-rw-r--r--src/modules/m_samode.cpp38
-rw-r--r--src/modules/m_sanick.cpp11
-rw-r--r--src/modules/m_sapart.cpp9
-rw-r--r--src/modules/m_saquit.cpp11
-rw-r--r--src/modules/m_sasl.cpp216
-rw-r--r--src/modules/m_satopic.cpp14
-rw-r--r--src/modules/m_securelist.cpp6
-rw-r--r--src/modules/m_services_account.cpp65
-rw-r--r--src/modules/m_servprotect.cpp29
-rw-r--r--src/modules/m_setidle.cpp4
-rw-r--r--src/modules/m_sha1.cpp199
-rw-r--r--src/modules/m_sha256.cpp4
-rw-r--r--src/modules/m_showfile.cpp17
-rw-r--r--src/modules/m_showwhois.cpp12
-rw-r--r--src/modules/m_shun.cpp17
-rw-r--r--src/modules/m_silence.cpp58
-rw-r--r--src/modules/m_spanningtree/addline.cpp2
-rw-r--r--src/modules/m_spanningtree/away.cpp6
-rw-r--r--src/modules/m_spanningtree/cachetimer.h4
-rw-r--r--src/modules/m_spanningtree/capab.cpp70
-rw-r--r--src/modules/m_spanningtree/commandbuilder.h19
-rw-r--r--src/modules/m_spanningtree/commands.h133
-rw-r--r--src/modules/m_spanningtree/compat.cpp308
-rw-r--r--src/modules/m_spanningtree/delline.cpp2
-rw-r--r--src/modules/m_spanningtree/encap.cpp13
-rw-r--r--src/modules/m_spanningtree/fjoin.cpp251
-rw-r--r--src/modules/m_spanningtree/fmode.cpp42
-rw-r--r--src/modules/m_spanningtree/ftopic.cpp14
-rw-r--r--src/modules/m_spanningtree/hmac.cpp40
-rw-r--r--src/modules/m_spanningtree/idle.cpp4
-rw-r--r--src/modules/m_spanningtree/ijoin.cpp15
-rw-r--r--src/modules/m_spanningtree/link.h2
-rw-r--r--src/modules/m_spanningtree/main.cpp295
-rw-r--r--src/modules/m_spanningtree/main.h39
-rw-r--r--src/modules/m_spanningtree/metadata.cpp6
-rw-r--r--src/modules/m_spanningtree/misccommands.cpp6
-rw-r--r--src/modules/m_spanningtree/netburst.cpp63
-rw-r--r--src/modules/m_spanningtree/nick.cpp30
-rw-r--r--src/modules/m_spanningtree/nickcollide.cpp68
-rw-r--r--src/modules/m_spanningtree/num.cpp62
-rw-r--r--src/modules/m_spanningtree/opertype.cpp4
-rw-r--r--src/modules/m_spanningtree/override_map.cpp19
-rw-r--r--src/modules/m_spanningtree/override_squit.cpp5
-rw-r--r--src/modules/m_spanningtree/override_stats.cpp21
-rw-r--r--src/modules/m_spanningtree/override_whois.cpp23
-rw-r--r--src/modules/m_spanningtree/pingtimer.cpp102
-rw-r--r--src/modules/m_spanningtree/pingtimer.h77
-rw-r--r--src/modules/m_spanningtree/pong.cpp6
-rw-r--r--src/modules/m_spanningtree/postcommand.cpp8
-rw-r--r--src/modules/m_spanningtree/protocolinterface.cpp32
-rw-r--r--src/modules/m_spanningtree/protocolinterface.h3
-rw-r--r--src/modules/m_spanningtree/rconnect.cpp2
-rw-r--r--src/modules/m_spanningtree/remoteuser.cpp (renamed from src/modules/m_spanningtree/version.cpp)16
-rw-r--r--src/modules/m_spanningtree/remoteuser.h (renamed from src/modules/m_spanningtree/push.cpp)25
-rw-r--r--src/modules/m_spanningtree/resolvers.cpp30
-rw-r--r--src/modules/m_spanningtree/rsquit.cpp8
-rw-r--r--src/modules/m_spanningtree/save.cpp11
-rw-r--r--src/modules/m_spanningtree/server.cpp163
-rw-r--r--src/modules/m_spanningtree/servercommand.cpp7
-rw-r--r--src/modules/m_spanningtree/servercommand.h4
-rw-r--r--src/modules/m_spanningtree/sinfo.cpp51
-rw-r--r--src/modules/m_spanningtree/svsjoin.cpp19
-rw-r--r--src/modules/m_spanningtree/svsnick.cpp37
-rw-r--r--src/modules/m_spanningtree/svspart.cpp5
-rw-r--r--src/modules/m_spanningtree/translate.cpp (renamed from src/modes/cmode_v.cpp)40
-rw-r--r--src/modules/m_spanningtree/translate.h (renamed from src/modes/cmode_o.cpp)24
-rw-r--r--src/modules/m_spanningtree/treeserver.cpp210
-rw-r--r--src/modules/m_spanningtree/treeserver.h133
-rw-r--r--src/modules/m_spanningtree/treesocket.h70
-rw-r--r--src/modules/m_spanningtree/treesocket1.cpp147
-rw-r--r--src/modules/m_spanningtree/treesocket2.cpp159
-rw-r--r--src/modules/m_spanningtree/uid.cpp47
-rw-r--r--src/modules/m_spanningtree/utils.cpp87
-rw-r--r--src/modules/m_spanningtree/utils.h14
-rw-r--r--src/modules/m_sqlauth.cpp8
-rw-r--r--src/modules/m_sqloper.cpp8
-rw-r--r--src/modules/m_sslinfo.cpp55
-rw-r--r--src/modules/m_sslmodes.cpp8
-rw-r--r--src/modules/m_starttls.cpp20
-rw-r--r--src/modules/m_stripcolor.cpp17
-rw-r--r--src/modules/m_svshold.cpp10
-rw-r--r--src/modules/m_swhois.cpp22
-rw-r--r--src/modules/m_testnet.cpp67
-rw-r--r--src/modules/m_timedbans.cpp92
-rw-r--r--src/modules/m_topiclock.cpp29
-rw-r--r--src/modules/m_uhnames.cpp13
-rw-r--r--src/modules/m_uninvite.cpp34
-rw-r--r--src/modules/m_userip.cpp8
-rw-r--r--src/modules/m_watch.cpp586
-rw-r--r--src/modules/m_websocket.cpp405
-rw-r--r--src/modules/m_xline_db.cpp30
-rw-r--r--src/server.cpp43
-rw-r--r--src/snomasks.cpp1
-rw-r--r--src/socket.cpp53
-rw-r--r--src/socketengine.cpp79
-rw-r--r--src/socketengines/socketengine_epoll.cpp26
-rw-r--r--src/socketengines/socketengine_kqueue.cpp9
-rw-r--r--src/socketengines/socketengine_poll.cpp14
-rw-r--r--src/socketengines/socketengine_ports.cpp10
-rw-r--r--src/socketengines/socketengine_select.cpp9
-rw-r--r--src/testsuite.cpp1
-rw-r--r--src/threadengine.cpp12
-rw-r--r--src/threadengines/threadengine_pthread.cpp76
-rw-r--r--src/threadengines/threadengine_win32.cpp41
-rw-r--r--src/timer.cpp8
-rw-r--r--src/usermanager.cpp167
-rw-r--r--src/users.cpp468
-rwxr-xr-xsrc/version.sh2
-rw-r--r--src/wildcard.cpp2
-rw-r--r--src/xline.cpp40
312 files changed, 13259 insertions, 7580 deletions
diff --git a/src/bancache.cpp b/src/bancache.cpp
index 4bb2fa82c..13e4dc7c7 100644
--- a/src/bancache.cpp
+++ b/src/bancache.cpp
@@ -19,11 +19,17 @@
#include "inspircd.h"
-#include "bancache.h"
+
+BanCacheHit::BanCacheHit(const std::string& type, const std::string& reason, time_t seconds)
+ : Type(type)
+ , Reason(reason)
+ , Expiry(ServerInstance->Time() + seconds)
+{
+}
BanCacheHit *BanCacheManager::AddHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds)
{
- BanCacheHit*& b = (*BanHash)[ip];
+ BanCacheHit*& b = BanHash[ip];
if (b != NULL) // can't have two cache entries on the same IP, sorry..
return NULL;
@@ -33,9 +39,9 @@ BanCacheHit *BanCacheManager::AddHit(const std::string &ip, const std::string &t
BanCacheHit *BanCacheManager::GetHit(const std::string &ip)
{
- BanCacheHash::iterator i = this->BanHash->find(ip);
+ BanCacheHash::iterator i = this->BanHash.find(ip);
- if (i == this->BanHash->end())
+ if (i == this->BanHash.end())
return NULL; // free and safe
if (RemoveIfExpired(i))
@@ -51,7 +57,7 @@ bool BanCacheManager::RemoveIfExpired(BanCacheHash::iterator& it)
ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "Hit on " + it->first + " is out of date, removing!");
delete it->second;
- it = BanHash->erase(it);
+ it = BanHash.erase(it);
return true;
}
@@ -62,7 +68,7 @@ void BanCacheManager::RemoveEntries(const std::string& type, bool positive)
else
ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing all negative hits");
- for (BanCacheHash::iterator i = BanHash->begin(); i != BanHash->end(); )
+ for (BanCacheHash::iterator i = BanHash.begin(); i != BanHash.end(); )
{
if (RemoveIfExpired(i))
continue; // updates the iterator if expired
@@ -86,7 +92,7 @@ void BanCacheManager::RemoveEntries(const std::string& type, bool positive)
/* we need to remove this one. */
ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing a hit on " + i->first);
delete b;
- i = BanHash->erase(i);
+ i = BanHash.erase(i);
}
else
++i;
@@ -95,7 +101,6 @@ void BanCacheManager::RemoveEntries(const std::string& type, bool positive)
BanCacheManager::~BanCacheManager()
{
- for (BanCacheHash::iterator n = BanHash->begin(); n != BanHash->end(); ++n)
+ for (BanCacheHash::iterator n = BanHash.begin(); n != BanHash.end(); ++n)
delete n->second;
- delete BanHash;
}
diff --git a/src/base.cpp b/src/base.cpp
index dc57a8434..0ff3fbe4c 100644
--- a/src/base.cpp
+++ b/src/base.cpp
@@ -23,25 +23,31 @@
#include "inspircd.h"
#include "base.h"
#include <time.h>
+#ifdef INSPIRCD_ENABLE_RTTI
#include <typeinfo>
+#endif
classbase::classbase()
{
- if (ServerInstance && ServerInstance->Logs)
+ if (ServerInstance)
ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::+ @%p", (void*)this);
}
CullResult classbase::cull()
{
- if (ServerInstance && ServerInstance->Logs)
+ if (ServerInstance)
+#ifdef INSPIRCD_ENABLE_RTTI
ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::-%s @%p",
typeid(*this).name(), (void*)this);
+#else
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::- @%p", (void*)this);
+#endif
return CullResult();
}
classbase::~classbase()
{
- if (ServerInstance && ServerInstance->Logs)
+ if (ServerInstance)
ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::~ @%p", (void*)this);
}
@@ -73,14 +79,14 @@ refcountbase::refcountbase() : refcount(0)
refcountbase::~refcountbase()
{
- if (refcount && ServerInstance && ServerInstance->Logs)
+ if (refcount && ServerInstance)
ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "refcountbase::~ @%p with refcount %d",
(void*)this, refcount);
}
usecountbase::~usecountbase()
{
- if (usecount && ServerInstance && ServerInstance->Logs)
+ if (usecount && ServerInstance)
ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "usecountbase::~ @%p with refcount %d",
(void*)this, usecount);
}
@@ -89,7 +95,13 @@ ServiceProvider::~ServiceProvider()
{
}
-ExtensionItem::ExtensionItem(const std::string& Key, Module* mod) : ServiceProvider(mod, Key, SERVICE_METADATA)
+void ServiceProvider::RegisterService()
+{
+}
+
+ExtensionItem::ExtensionItem(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : ServiceProvider(mod, Key, SERVICE_METADATA)
+ , type(exttype)
{
}
@@ -132,6 +144,12 @@ void* ExtensionItem::unset_raw(Extensible* container)
return rv;
}
+void ExtensionItem::RegisterService()
+{
+ if (!ServerInstance->Extensions.Register(this))
+ throw ModuleException("Extension already exists: " + name);
+}
+
bool ExtensionManager::Register(ExtensionItem* item)
{
return types.insert(std::make_pair(item->name, item)).second;
@@ -139,10 +157,10 @@ bool ExtensionManager::Register(ExtensionItem* item)
void ExtensionManager::BeginUnregister(Module* module, std::vector<reference<ExtensionItem> >& list)
{
- std::map<std::string, reference<ExtensionItem> >::iterator i = types.begin();
+ ExtMap::iterator i = types.begin();
while (i != types.end())
{
- std::map<std::string, reference<ExtensionItem> >::iterator me = i++;
+ ExtMap::iterator me = i++;
ExtensionItem* item = me->second;
if (item->creator == module)
{
@@ -154,7 +172,7 @@ void ExtensionManager::BeginUnregister(Module* module, std::vector<reference<Ext
ExtensionItem* ExtensionManager::GetItem(const std::string& name)
{
- std::map<std::string, reference<ExtensionItem> >::iterator i = types.find(name);
+ ExtMap::iterator i = types.find(name);
if (i == types.end())
return NULL;
return i->second;
@@ -197,11 +215,12 @@ void Extensible::FreeAllExtItems()
Extensible::~Extensible()
{
- if ((!extensions.empty() || !culled) && ServerInstance && ServerInstance->Logs)
+ if ((!extensions.empty() || !culled) && ServerInstance)
ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Extensible destructor called without cull @%p", (void*)this);
}
-LocalExtItem::LocalExtItem(const std::string& Key, Module* mod) : ExtensionItem(Key, mod)
+LocalExtItem::LocalExtItem(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : ExtensionItem(Key, exttype, mod)
{
}
@@ -218,8 +237,10 @@ void LocalExtItem::unserialize(SerializeFormat format, Extensible* container, co
{
}
-LocalStringExt::LocalStringExt(const std::string& Key, Module* Owner)
- : SimpleExtItem<std::string>(Key, Owner) { }
+LocalStringExt::LocalStringExt(const std::string& Key, ExtensibleType exttype, Module* Owner)
+ : SimpleExtItem<std::string>(Key, exttype, Owner)
+{
+}
LocalStringExt::~LocalStringExt()
{
@@ -227,12 +248,19 @@ LocalStringExt::~LocalStringExt()
std::string LocalStringExt::serialize(SerializeFormat format, const Extensible* container, void* item) const
{
- if (item && format == FORMAT_USER)
+ if ((item) && (format != FORMAT_NETWORK))
return *static_cast<std::string*>(item);
return "";
}
-LocalIntExt::LocalIntExt(const std::string& Key, Module* mod) : LocalExtItem(Key, mod)
+void LocalStringExt::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+ if (format != FORMAT_NETWORK)
+ set(container, value);
+}
+
+LocalIntExt::LocalIntExt(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : LocalExtItem(Key, exttype, mod)
{
}
@@ -242,11 +270,17 @@ LocalIntExt::~LocalIntExt()
std::string LocalIntExt::serialize(SerializeFormat format, const Extensible* container, void* item) const
{
- if (format != FORMAT_USER)
+ if (format == FORMAT_NETWORK)
return "";
return ConvToStr(reinterpret_cast<intptr_t>(item));
}
+void LocalIntExt::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+ if (format != FORMAT_NETWORK)
+ set(container, ConvToInt(value));
+}
+
intptr_t LocalIntExt::get(const Extensible* container) const
{
return reinterpret_cast<intptr_t>(get_raw(container));
@@ -264,7 +298,8 @@ void LocalIntExt::free(void*)
{
}
-StringExtItem::StringExtItem(const std::string& Key, Module* mod) : ExtensionItem(Key, mod)
+StringExtItem::StringExtItem(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : ExtensionItem(Key, exttype, mod)
{
}
diff --git a/src/channels.cpp b/src/channels.cpp
index 19b1281d5..bc23c680a 100644
--- a/src/channels.cpp
+++ b/src/channels.cpp
@@ -25,8 +25,6 @@
#include "inspircd.h"
#include "listmode.h"
-#include <cstdarg>
-#include "mode.h"
namespace
{
@@ -34,9 +32,6 @@ namespace
ChanModeReference inviteonlymode(NULL, "inviteonly");
ChanModeReference keymode(NULL, "key");
ChanModeReference limitmode(NULL, "limit");
- ChanModeReference secretmode(NULL, "secret");
- ChanModeReference privatemode(NULL, "private");
- UserModeReference invisiblemode(NULL, "invisible");
}
Channel::Channel(const std::string &cname, time_t ts)
@@ -51,29 +46,37 @@ void Channel::SetMode(ModeHandler* mh, bool on)
modes[mh->GetId()] = on;
}
-void Channel::SetTopic(User* u, const std::string& ntopic)
+void Channel::SetTopic(User* u, const std::string& ntopic, time_t topicts, const std::string* setter)
{
- this->topic.assign(ntopic, 0, ServerInstance->Config->Limits.MaxTopic);
- this->setby.assign(ServerInstance->Config->FullHostInTopic ? u->GetFullHost() : u->nick, 0, 128);
- this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
- this->topicset = ServerInstance->Time();
+ // Send a TOPIC message to the channel only if the new topic text differs
+ if (this->topic != ntopic)
+ {
+ this->topic = ntopic;
+ this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
+ }
+
+ // Always update setter and set time
+ if (!setter)
+ setter = ServerInstance->Config->FullHostInTopic ? &u->GetFullHost() : &u->nick;
+ this->setby.assign(*setter, 0, ServerInstance->Config->Limits.GetMaxMask());
+ this->topicset = topicts;
FOREACH_MOD(OnPostTopicChange, (u, this, this->topic));
}
Membership* Channel::AddUser(User* user)
{
- Membership*& memb = userlist[user];
- if (memb)
+ std::pair<MemberMap::iterator, bool> ret = userlist.insert(std::make_pair(user, insp::aligned_storage<Membership>()));
+ if (!ret.second)
return NULL;
- memb = new Membership(user, this);
+ Membership* memb = new(ret.first->second) Membership(user, this);
return memb;
}
void Channel::DelUser(User* user)
{
- UserMembIter it = userlist.find(user);
+ MemberMap::iterator it = userlist.find(user);
if (it != userlist.end())
DelUser(it);
}
@@ -88,23 +91,21 @@ void Channel::CheckDestroy()
if (res == MOD_RES_DENY)
return;
+ // If the channel isn't in chanlist then it is already in the cull list, don't add it again
chan_hash::iterator iter = ServerInstance->chanlist.find(this->name);
- /* kill the record */
- if (iter != ServerInstance->chanlist.end())
- {
- FOREACH_MOD(OnChannelDelete, (this));
- ServerInstance->chanlist.erase(iter);
- }
+ if ((iter == ServerInstance->chanlist.end()) || (iter->second != this))
+ return;
- ClearInvites();
+ FOREACH_MOD(OnChannelDelete, (this));
+ ServerInstance->chanlist.erase(iter);
ServerInstance->GlobalCulls.AddItem(this);
}
-void Channel::DelUser(const UserMembIter& membiter)
+void Channel::DelUser(const MemberMap::iterator& membiter)
{
Membership* memb = membiter->second;
memb->cull();
- delete memb;
+ memb->~Membership();
userlist.erase(membiter);
// If this channel became empty then it should be removed
@@ -113,7 +114,7 @@ void Channel::DelUser(const UserMembIter& membiter)
Membership* Channel::GetUser(User* user)
{
- UserMembIter i = userlist.find(user);
+ MemberMap::iterator i = userlist.find(user);
if (i == userlist.end())
return NULL;
return i->second;
@@ -137,11 +138,19 @@ void Channel::SetDefaultModes()
if (mode->IsPrefixMode())
continue;
- if (mode->GetNumParams(true))
+ if (mode->NeedsParam(true))
+ {
list.GetToken(parameter);
+ // If the parameter begins with a ':' then it's invalid
+ if (parameter.c_str()[0] == ':')
+ continue;
+ }
else
parameter.clear();
+ if ((mode->NeedsParam(true)) && (parameter.empty()))
+ continue;
+
mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, parameter, true);
}
}
@@ -172,14 +181,14 @@ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, co
{
unsigned int opermaxchans = ConvToInt(user->oper->getConfig("maxchans"));
// If not set, use 2.0's <channels:opers>, if that's not set either, use limit from CC
- if (!opermaxchans)
+ if (!opermaxchans && user->HasPrivPermission("channels/high-join-limit"))
opermaxchans = ServerInstance->Config->OperMaxChans;
if (opermaxchans)
maxchans = opermaxchans;
}
if (user->chans.size() >= maxchans)
{
- user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s :You are on too many channels", cname.c_str());
+ user->WriteNumeric(ERR_TOOMANYCHANNELS, cname, "You are on too many channels");
return NULL;
}
}
@@ -233,16 +242,13 @@ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, co
if (MOD_RESULT == MOD_RES_PASSTHRU)
{
std::string ckey = chan->GetModeParameter(keymode);
- bool invited = user->IsInvited(chan);
- bool can_bypass = ServerInstance->Config->InvBypassModes && invited;
-
if (!ckey.empty())
{
FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, chan, key));
- if (!MOD_RESULT.check((ckey == key) || can_bypass))
+ if (!MOD_RESULT.check(InspIRCd::TimingSafeCompare(ckey, key)))
{
// If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
- user->WriteNumeric(ERR_BADCHANNELKEY, "%s :Cannot join channel (Incorrect channel key)", chan->name.c_str());
+ user->WriteNumeric(ERR_BADCHANNELKEY, chan->name, "Cannot join channel (Incorrect channel key)");
return NULL;
}
}
@@ -250,9 +256,9 @@ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, co
if (chan->IsModeSet(inviteonlymode))
{
FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, chan));
- if (!MOD_RESULT.check(invited))
+ if (MOD_RESULT != MOD_RES_ALLOW)
{
- user->WriteNumeric(ERR_INVITEONLYCHAN, "%s :Cannot join channel (Invite only)", chan->name.c_str());
+ user->WriteNumeric(ERR_INVITEONLYCHAN, chan->name, "Cannot join channel (Invite only)");
return NULL;
}
}
@@ -261,27 +267,18 @@ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, co
if (!limit.empty())
{
FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, chan));
- if (!MOD_RESULT.check((chan->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
+ if (!MOD_RESULT.check((chan->GetUserCounter() < atol(limit.c_str()))))
{
- user->WriteNumeric(ERR_CHANNELISFULL, "%s :Cannot join channel (Channel is full)", chan->name.c_str());
+ user->WriteNumeric(ERR_CHANNELISFULL, chan->name, "Cannot join channel (Channel is full)");
return NULL;
}
}
- if (chan->IsBanned(user) && !can_bypass)
+ if (chan->IsBanned(user))
{
- user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Cannot join channel (You're banned)", chan->name.c_str());
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, chan->name, "Cannot join channel (You're banned)");
return NULL;
}
-
- /*
- * If the user has invites for this channel, remove them now
- * after a successful join so they don't build up.
- */
- if (invited)
- {
- user->RemoveInvite(chan);
- }
}
}
}
@@ -292,17 +289,17 @@ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, co
return chan;
}
-void Channel::ForceJoin(User* user, const std::string* privs, bool bursting, bool created_by_local)
+Membership* Channel::ForceJoin(User* user, const std::string* privs, bool bursting, bool created_by_local)
{
if (IS_SERVER(user))
{
ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join server user " + user->uuid + " to channel " + this->name);
- return;
+ return NULL;
}
Membership* memb = this->AddUser(user);
if (!memb)
- return; // Already on the channel
+ return NULL; // Already on the channel
user->chans.push_front(memb);
@@ -339,17 +336,8 @@ void Channel::ForceJoin(User* user, const std::string* privs, bool bursting, boo
this->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", this->name.c_str(), ms.c_str());
}
- if (IS_LOCAL(user))
- {
- if (this->topicset)
- {
- user->WriteNumeric(RPL_TOPIC, "%s :%s", this->name.c_str(), this->topic.c_str());
- user->WriteNumeric(RPL_TOPICTIME, "%s %s %lu", this->name.c_str(), this->setby.c_str(), (unsigned long)this->topicset);
- }
- this->UserList(user);
- }
-
FOREACH_MOD(OnPostJoin, (memb));
+ return memb;
}
bool Channel::IsBanned(User* user)
@@ -389,10 +377,10 @@ bool Channel::CheckBan(User* user, const std::string& mask)
return false;
const std::string nickIdent = user->nick + "!" + user->ident;
- std::string prefix = mask.substr(0, at);
+ std::string prefix(mask, 0, at);
if (InspIRCd::Match(nickIdent, prefix, NULL))
{
- std::string suffix = mask.substr(at + 1);
+ std::string suffix(mask, at + 1);
if (InspIRCd::Match(user->host, suffix, NULL) ||
InspIRCd::Match(user->dhost, suffix, NULL) ||
InspIRCd::MatchCIDR(user->GetIPString(), suffix, NULL))
@@ -425,71 +413,34 @@ ModResult Channel::GetExtBanStatus(User *user, char type)
* Remove a channel from a users record, remove the reference to the Membership object
* from the channel and destroy it.
*/
-void Channel::PartUser(User *user, std::string &reason)
+bool Channel::PartUser(User* user, std::string& reason)
{
- UserMembIter membiter = userlist.find(user);
-
- if (membiter != userlist.end())
- {
- Membership* memb = membiter->second;
- CUList except_list;
- FOREACH_MOD(OnUserPart, (memb, reason, except_list));
-
- WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
-
- // Remove this channel from the user's chanlist
- user->chans.erase(memb);
- // Remove the Membership from this channel's userlist and destroy it
- this->DelUser(membiter);
- }
-}
+ MemberMap::iterator membiter = userlist.find(user);
-void Channel::KickUser(User* src, User* victim, const std::string& reason, Membership* srcmemb)
-{
- UserMembIter victimiter = userlist.find(victim);
- Membership* memb = ((victimiter != userlist.end()) ? victimiter->second : NULL);
+ if (membiter == userlist.end())
+ return false;
- if (!memb)
- {
- src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s :They are not on that channel", victim->nick.c_str(), this->name.c_str());
- return;
- }
+ Membership* memb = membiter->second;
+ CUList except_list;
+ FOREACH_MOD(OnUserPart, (memb, reason, except_list));
- // Do the following checks only if the KICK is done by a local user;
- // each server enforces its own rules.
- if (IS_LOCAL(src))
- {
- // Modules are allowed to explicitly allow or deny kicks done by local users
- ModResult res;
- FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
- if (res == MOD_RES_DENY)
- return;
+ WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
- if (res == MOD_RES_PASSTHRU)
- {
- if (!srcmemb)
- srcmemb = GetUser(src);
- unsigned int them = srcmemb ? srcmemb->getRank() : 0;
- unsigned int req = HALFOP_VALUE;
- for (std::string::size_type i = 0; i < memb->modes.length(); i++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(memb->modes[i], MODETYPE_CHANNEL);
- if (mh && mh->GetLevelRequired() > req)
- req = mh->GetLevelRequired();
- }
+ // Remove this channel from the user's chanlist
+ user->chans.erase(memb);
+ // Remove the Membership from this channel's userlist and destroy it
+ this->DelUser(membiter);
- if (them < req)
- {
- src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be a channel %soperator",
- this->name.c_str(), req > HALFOP_VALUE ? "" : "half-");
- return;
- }
- }
- }
+ return true;
+}
+void Channel::KickUser(User* src, const MemberMap::iterator& victimiter, const std::string& reason)
+{
+ Membership* memb = victimiter->second;
CUList except_list;
FOREACH_MOD(OnUserKick, (src, memb, reason, except_list));
+ User* victim = memb->user;
WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), victim->nick.c_str(), reason.c_str());
victim->chans.erase(memb);
@@ -507,7 +458,7 @@ void Channel::WriteChannel(User* user, const std::string &text)
{
const std::string message = ":" + user->GetFullHost() + " " + text;
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+ for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
{
if (IS_LOCAL(i->first))
i->first->Write(message);
@@ -525,7 +476,7 @@ void Channel::WriteChannelWithServ(const std::string& ServName, const std::strin
{
const std::string message = ":" + (ServName.empty() ? ServerInstance->Config->ServerName : ServName) + " " + text;
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+ for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
{
if (IS_LOCAL(i->first))
i->first->Write(message);
@@ -564,7 +515,7 @@ void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CULi
if (mh)
minrank = mh->GetPrefixRank();
}
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+ for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
{
if (IS_LOCAL(i->first) && (except_list.find(i->first) == except_list.end()))
{
@@ -619,64 +570,11 @@ const char* Channel::ChanModes(bool showkey)
return scratch.c_str();
}
-/* compile a userlist of a channel into a string, each nick seperated by
- * spaces and op, voice etc status shown as @ and +, and send it to 'user'
- */
-void Channel::UserList(User* user, bool has_user)
-{
- bool has_privs = user->HasPrivPermission("channels/auspex");
- std::string list;
- list.push_back(this->IsModeSet(secretmode) ? '@' : this->IsModeSet(privatemode) ? '*' : '=');
- list.push_back(' ');
- list.append(this->name).append(" :");
- std::string::size_type pos = list.size();
-
- const size_t maxlen = ServerInstance->Config->Limits.MaxLine - 10 - ServerInstance->Config->ServerName.size();
- std::string prefixlist;
- std::string nick;
- for (UserMembIter i = userlist.begin(); i != userlist.end(); ++i)
- {
- if ((!has_user) && (i->first->IsModeSet(invisiblemode)) && (!has_privs))
- {
- /*
- * user is +i, and source not on the channel, does not show
- * nick in NAMES list
- */
- continue;
- }
-
- Membership* memb = i->second;
-
- prefixlist.clear();
- char prefix = memb->GetPrefixChar();
- if (prefix)
- prefixlist.push_back(prefix);
- nick = i->first->nick;
-
- ModResult res;
- FIRST_MOD_RESULT(OnNamesListItem, res, (user, memb, prefixlist, nick));
-
- // See if a module wants us to exclude this user from NAMES
- if (res == MOD_RES_DENY)
- continue;
-
- if (list.size() + prefixlist.length() + nick.length() + 1 > maxlen)
- {
- /* list overflowed into multiple numerics */
- user->WriteNumeric(RPL_NAMREPLY, list);
-
- // Erase all nicks, keep the constant part
- list.erase(pos);
- }
-
- list.append(prefixlist).append(nick).push_back(' ');
- }
-
- // Only send the user list numeric if there is at least one user in it
- if (list.size() != pos)
- user->WriteNumeric(RPL_NAMREPLY, list);
-
- user->WriteNumeric(RPL_ENDOFNAMES, "%s :End of /NAMES list.", this->name.c_str());
+void Channel::WriteNotice(const std::string& text)
+{
+ std::string rawmsg = "NOTICE ";
+ rawmsg.append(this->name).append(" :").append(text);
+ WriteChannelWithServ(ServerInstance->Config->ServerName, rawmsg);
}
/* returns the status character for a given user on a channel, e.g. @ for op,
@@ -713,25 +611,22 @@ unsigned int Membership::getRank()
return rv;
}
-const char* Membership::GetAllPrefixChars() const
+std::string Membership::GetAllPrefixChars() const
{
- static char prefix[64];
- int ctr = 0;
-
+ std::string ret;
for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
{
PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
if (mh && mh->GetPrefix())
- prefix[ctr++] = mh->GetPrefix();
+ ret.push_back(mh->GetPrefix());
}
- prefix[ctr] = 0;
- return prefix;
+ return ret;
}
unsigned int Channel::GetPrefixValue(User* user)
{
- UserMembIter m = userlist.find(user);
+ MemberMap::iterator m = userlist.find(user);
if (m == userlist.end())
return 0;
return m->second->getRank();
@@ -756,68 +651,3 @@ bool Membership::SetPrefix(PrefixMode* delta_mh, bool adding)
modes.push_back(prefix);
return adding;
}
-
-void Invitation::Create(Channel* c, LocalUser* u, time_t timeout)
-{
- if ((timeout != 0) && (ServerInstance->Time() >= timeout))
- // Expired, don't bother
- return;
-
- ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Create chan=%s user=%s", c->name.c_str(), u->uuid.c_str());
-
- Invitation* inv = Invitation::Find(c, u, false);
- if (inv)
- {
- if ((inv->expiry == 0) || (inv->expiry > timeout))
- return;
- inv->expiry = timeout;
- ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
- }
- else
- {
- inv = new Invitation(c, u, timeout);
- c->invites.push_front(inv);
- u->invites.push_front(inv);
- ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
- }
-}
-
-Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired)
-{
- ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find chan=%s user=%s check_expired=%d", c ? c->name.c_str() : "NULL", u ? u->uuid.c_str() : "NULL", check_expired);
-
- Invitation* result = NULL;
- for (InviteList::iterator i = u->invites.begin(); i != u->invites.end(); )
- {
- Invitation* inv = *i;
- ++i;
-
- if ((check_expired) && (inv->expiry != 0) && (inv->expiry <= ServerInstance->Time()))
- {
- /* Expired invite, remove it. */
- std::string expiration = InspIRCd::TimeString(inv->expiry);
- ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
- delete inv;
- }
- else
- {
- /* Is it what we're searching for? */
- if (inv->chan == c)
- {
- result = inv;
- break;
- }
- }
- }
-
- ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find result=%p", (void*) result);
- return result;
-}
-
-Invitation::~Invitation()
-{
- // Remove this entry from both lists
- chan->invites.erase(this);
- user->invites.erase(this);
- ServerInstance->Logs->Log("INVITEBASE", LOG_DEBUG, "Invitation::~ %p", (void*) this);
-}
diff --git a/src/cidr.cpp b/src/cidr.cpp
index 875b95304..250ad9c69 100644
--- a/src/cidr.cpp
+++ b/src/cidr.cpp
@@ -53,8 +53,8 @@ bool irc::sockets::MatchCIDR(const std::string &address, const std::string &cidr
}
else
{
- address_copy = address.substr(username_addr_pos + 1);
- cidr_copy = cidr_mask.substr(username_mask_pos + 1);
+ address_copy.assign(address, username_addr_pos + 1, std::string::npos);
+ cidr_copy.assign(cidr_mask, username_mask_pos + 1, std::string::npos);
}
}
else
@@ -66,7 +66,7 @@ bool irc::sockets::MatchCIDR(const std::string &address, const std::string &cidr
const std::string::size_type per_pos = cidr_copy.rfind('/');
if ((per_pos == std::string::npos) || (per_pos == cidr_copy.length()-1)
|| (cidr_copy.find_first_not_of("0123456789", per_pos+1) != std::string::npos)
- || (cidr_copy.find_first_not_of("0123456789abcdef.:") < per_pos))
+ || (cidr_copy.find_first_not_of("0123456789abcdefABCDEF.:") < per_pos))
{
// The CIDR mask is invalid
return false;
diff --git a/src/command_parse.cpp b/src/command_parse.cpp
index d89d7cbb5..f3511b05b 100644
--- a/src/command_parse.cpp
+++ b/src/command_parse.cpp
@@ -40,7 +40,7 @@ bool InspIRCd::PassCompare(Extensible* ex, const std::string& data, const std::s
if (!hashtype.empty() && hashtype != "plaintext")
return false;
- return (data == input);
+ return TimingSafeCompare(data, input);
}
bool CommandParser::LoopCall(User* user, Command* handler, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
@@ -60,7 +60,7 @@ bool CommandParser::LoopCall(User* user, Command* handler, const std::vector<std
*
* Only check for duplicates if there is one list (allow them in JOIN).
*/
- std::set<irc::string> dupes;
+ insp::flat_set<std::string, irc::insensitive_swo> dupes;
bool check_dupes = (extra < 0);
/* Create two sepstreams, if we have only one list, then initialize the second sepstream with
@@ -80,7 +80,7 @@ bool CommandParser::LoopCall(User* user, Command* handler, const std::vector<std
*/
while (items1.GetToken(item) && (!usemax || max++ < ServerInstance->Config->MaxTargets))
{
- if ((!check_dupes) || (dupes.insert(item.c_str()).second))
+ if ((!check_dupes) || (dupes.insert(item).second))
{
std::vector<std::string> new_parameters(parameters);
new_parameters[splithere] = item;
@@ -109,7 +109,7 @@ bool CommandParser::LoopCall(User* user, Command* handler, const std::vector<std
Command* CommandParser::GetHandler(const std::string &commandname)
{
- Commandtable::iterator n = cmdlist.find(commandname);
+ CommandMap::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
return n->second;
@@ -120,7 +120,7 @@ Command* CommandParser::GetHandler(const std::string &commandname)
CmdResult CommandParser::CallHandler(const std::string& commandname, const std::vector<std::string>& parameters, User* user, Command** cmd)
{
- Commandtable::iterator n = cmdlist.find(commandname);
+ CommandMap::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
{
@@ -182,11 +182,21 @@ void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
/* find the command, check it exists */
Command* handler = GetHandler(command);
+ // Penalty to give if the command fails before the handler is executed
+ unsigned int failpenalty = 0;
+
/* Modify the user's penalty regardless of whether or not the command exists */
if (!user->HasPrivPermission("users/flood/no-throttle"))
{
// If it *doesn't* exist, give it a slightly heftier penalty than normal to deter flooding us crap
- user->CommandFloodPenalty += handler ? handler->Penalty * 1000 : 2000;
+ unsigned int penalty = (handler ? handler->Penalty * 1000 : 2000);
+ user->CommandFloodPenalty += penalty;
+
+ // Increase their penalty later if we fail and the command has 0 penalty by default (i.e. in Command::Penalty) to
+ // throttle sending ERR_* from the command parser. If the command does have a non-zero penalty then this is not
+ // needed because we've increased their penalty above.
+ if (penalty == 0)
+ failpenalty = 1000;
}
if (!handler)
@@ -207,8 +217,8 @@ void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
if (!handler)
{
if (user->registered == REG_ALL)
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command",command.c_str());
- ServerInstance->stats->statsUnknown++;
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "Unknown command");
+ ServerInstance->stats.Unknown++;
return;
}
}
@@ -257,14 +267,16 @@ void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
{
if (!user->IsModeSet(handler->flags_needed))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - You do not have the required operator privileges");
+ user->CommandFloodPenalty += failpenalty;
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Permission Denied - You do not have the required operator privileges");
return;
}
if (!user->HasPermission(command))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper type %s does not have access to command %s",
- user->oper->name.c_str(), command.c_str());
+ user->CommandFloodPenalty += failpenalty;
+ user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper type %s does not have access to command %s",
+ user->oper->name.c_str(), command.c_str()));
return;
}
}
@@ -272,13 +284,14 @@ void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
if ((user->registered == REG_ALL) && (!user->IsOper()) && (handler->IsDisabled()))
{
/* command is disabled! */
+ user->CommandFloodPenalty += failpenalty;
if (ServerInstance->Config->DisabledDontExist)
{
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command", command.c_str());
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "Unknown command");
}
else
{
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", command.c_str());
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "This command has been disabled.");
}
ServerInstance->SNO->WriteToSnoMask('a', "%s denied for %s (%s@%s)",
@@ -291,15 +304,17 @@ void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
if (command_p.size() < handler->min_params)
{
- user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s :Not enough parameters.", command.c_str());
+ user->CommandFloodPenalty += failpenalty;
+ user->WriteNumeric(ERR_NEEDMOREPARAMS, command, "Not enough parameters.");
if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (handler->syntax.length()))
- user->WriteNumeric(RPL_SYNTAX, ":SYNTAX %s %s", handler->name.c_str(), handler->syntax.c_str());
+ user->WriteNumeric(RPL_SYNTAX, InspIRCd::Format("SYNTAX %s %s", handler->name.c_str(), handler->syntax.c_str()));
return;
}
if ((user->registered != REG_ALL) && (!handler->WorksBeforeReg()))
{
- user->WriteNumeric(ERR_NOTREGISTERED, "%s :You have not registered",command.c_str());
+ user->CommandFloodPenalty += failpenalty;
+ user->WriteNumeric(ERR_NOTREGISTERED, command, "You have not registered");
}
else
{
@@ -322,18 +337,52 @@ void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
void CommandParser::RemoveCommand(Command* x)
{
- Commandtable::iterator n = cmdlist.find(x->name);
+ CommandMap::iterator n = cmdlist.find(x->name);
if (n != cmdlist.end() && n->second == x)
cmdlist.erase(n);
}
+CommandBase::CommandBase(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
+ : ServiceProvider(mod, cmd, SERVICE_COMMAND)
+ , flags_needed(0)
+ , min_params(minpara)
+ , max_params(maxpara)
+ , use_count(0)
+ , disabled(false)
+ , works_before_reg(false)
+ , allow_empty_last_param(true)
+ , Penalty(1)
+{
+}
+
CommandBase::~CommandBase()
{
}
+void CommandBase::EncodeParameter(std::string& parameter, int index)
+{
+}
+
+RouteDescriptor CommandBase::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ return ROUTE_LOCALONLY;
+}
+
+Command::Command(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
+ : CommandBase(mod, cmd, minpara, maxpara)
+ , force_manual_route(false)
+{
+}
+
Command::~Command()
{
- ServerInstance->Parser->RemoveCommand(this);
+ ServerInstance->Parser.RemoveCommand(this);
+}
+
+void Command::RegisterService()
+{
+ if (!ServerInstance->Parser.AddCommand(this))
+ throw ModuleException("Command already exists: " + name);
}
void CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
@@ -341,8 +390,7 @@ void CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
if (buffer.empty())
return;
- ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I :%s %s",
- user->uuid.c_str(), user->nick.c_str(), buffer.c_str());
+ ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I %s", user->uuid.c_str(), buffer.c_str());
ProcessCommand(user,buffer);
}
diff --git a/src/commands.cpp b/src/commands.cpp
index d5a89c086..c72a5dc73 100644
--- a/src/commands.cpp
+++ b/src/commands.cpp
@@ -22,8 +22,6 @@
#include "inspircd.h"
-#include "xline.h"
-#include "command_parse.h"
CmdResult SplitCommand::Handle(const std::vector<std::string>& parms, User* u)
{
diff --git a/src/configparser.cpp b/src/configparser.cpp
index 60770d16d..8bf9aaec2 100644
--- a/src/configparser.cpp
+++ b/src/configparser.cpp
@@ -155,7 +155,7 @@ struct Parser
}
else
{
- std::map<std::string, std::string>::iterator var = stack.vars.find(varname);
+ insp::flat_map<std::string, std::string>::iterator var = stack.vars.find(varname);
if (var == stack.vars.end())
throw CoreException("Undefined XML entity reference '&" + varname + ";'");
value.append(var->second);
@@ -173,7 +173,7 @@ struct Parser
}
else if (ch == '"')
break;
- else
+ else if (ch != '\r')
value.push_back(ch);
}
@@ -367,7 +367,7 @@ void ParseStack::DoReadFile(const std::string& key, const std::string& name, int
bool ParseStack::ParseFile(const std::string& path, int flags, const std::string& mandatory_tag, bool isexec)
{
ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Reading (isexec=%d) %s", isexec, path.c_str());
- if (std::find(reading.begin(), reading.end(), path) != reading.end())
+ if (stdalgo::isin(reading, path))
throw CoreException((isexec ? "Executable " : "File ") + path + " is included recursively (looped inclusion)");
/* It's not already included, add it to the list of files we've loaded */
@@ -385,8 +385,6 @@ bool ParseStack::ParseFile(const std::string& path, int flags, const std::string
bool ConfigTag::readString(const std::string& key, std::string& value, bool allow_lf)
{
- if (!this)
- return false;
for(std::vector<KeyVal>::iterator j = items.begin(); j != items.end(); ++j)
{
if(j->first != key)
diff --git a/src/configreader.cpp b/src/configreader.cpp
index 15d9298b6..9d327532b 100644
--- a/src/configreader.cpp
+++ b/src/configreader.cpp
@@ -29,10 +29,34 @@
#include "configparser.h"
#include <iostream>
+ServerLimits::ServerLimits(ConfigTag* tag)
+ : NickMax(tag->getInt("maxnick", 32))
+ , ChanMax(tag->getInt("maxchan", 64))
+ , MaxModes(tag->getInt("maxmodes", 20))
+ , IdentMax(tag->getInt("maxident", 11))
+ , MaxQuit(tag->getInt("maxquit", 255))
+ , MaxTopic(tag->getInt("maxtopic", 307))
+ , MaxKick(tag->getInt("maxkick", 255))
+ , MaxGecos(tag->getInt("maxgecos", 128))
+ , MaxAway(tag->getInt("maxaway", 200))
+ , MaxLine(tag->getInt("maxline", 512))
+ , MaxHost(tag->getInt("maxhost", 64))
+{
+}
+
+static ConfigTag* CreateEmptyTag()
+{
+ std::vector<KeyVal>* items;
+ return ConfigTag::create("empty", "<auto>", 0, items);
+}
+
ServerConfig::ServerConfig()
+ : EmptyTag(CreateEmptyTag())
+ , Limits(EmptyTag)
+ , NoSnoticeStack(false)
{
- RawLog = HideBans = HideSplits = UndernetMsgPrefix = false;
- WildcardIPv6 = InvBypassModes = true;
+ RawLog = HideBans = HideSplits = false;
+ WildcardIPv6 = true;
dns_timeout = 5;
MaxTargets = 20;
NetBufferSize = 10240;
@@ -43,6 +67,11 @@ ServerConfig::ServerConfig()
c_ipv6_range = 128;
}
+ServerConfig::~ServerConfig()
+{
+ delete EmptyTag;
+}
+
static void ValidHost(const std::string& p, const std::string& msg)
{
int num_dots = 0;
@@ -69,17 +98,16 @@ bool ServerConfig::ApplyDisabledCommands(const std::string& data)
std::string thiscmd;
/* Enable everything first */
- for (Commandtable::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator x = commands.begin(); x != commands.end(); ++x)
x->second->Disable(false);
/* Now disable all the ones which the user wants disabled */
while (dcmds >> thiscmd)
{
- Commandtable::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
- if (cm != ServerInstance->Parser->cmdlist.end())
- {
- cm->second->Disable(true);
- }
+ Command* handler = ServerInstance->Parser.GetHandler(thiscmd);
+ if (handler)
+ handler->Disable(true);
}
return true;
}
@@ -300,6 +328,14 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
me->limit = tag->getInt("limit", me->limit);
me->resolvehostnames = tag->getBool("resolvehostnames", me->resolvehostnames);
+ std::string ports = tag->getString("port");
+ if (!ports.empty())
+ {
+ irc::portparser portrange(ports, false);
+ while (int port = portrange.GetToken())
+ me->ports.insert(port);
+ }
+
ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
if (oldMask != oldBlocksByMask.end())
{
@@ -334,13 +370,13 @@ struct DeprecatedConfig
static const DeprecatedConfig ChangedConfig[] = {
{ "bind", "transport", "", "has been moved to <bind:ssl> as of 2.0" },
{ "die", "value", "", "you need to reread your config" },
- { "gnutls", "starttls", "", "has been replaced with m_starttls as of 2.2" },
+ { "gnutls", "starttls", "", "has been replaced with m_starttls as of 3.0" },
{ "link", "autoconnect", "", "2.0+ does not use this attribute - define <autoconnect> tags instead" },
{ "link", "transport", "", "has been moved to <link:ssl> as of 2.0" },
- { "module", "name", "m_chanprotect.so", "has been replaced with m_customprefix as of 2.2" },
- { "module", "name", "m_halfop.so", "has been replaced with m_customprefix as of 2.2" },
- { "options", "cyclehosts", "", "has been replaced with m_hostcycle as of 2.2" },
- { "performance", "nouserdns", "", "has been moved to <connect:resolvehostnames> as of 2.2" }
+ { "module", "name", "m_chanprotect.so", "has been replaced with m_customprefix as of 3.0" },
+ { "module", "name", "m_halfop.so", "has been replaced with m_customprefix as of 3.0" },
+ { "options", "cyclehosts", "", "has been replaced with m_hostcycle as of 3.0" },
+ { "performance", "nouserdns", "", "has been moved to <connect:resolvehostnames> as of 3.0" }
};
void ServerConfig::Fill()
@@ -381,11 +417,11 @@ void ServerConfig::Fill()
HideBans = security->getBool("hidebans");
HideWhoisServer = security->getString("hidewhois");
HideKillsServer = security->getString("hidekills");
+ HideULineKills = security->getBool("hideulinekills");
RestrictBannedUsers = security->getBool("restrictbannedusers", true);
GenericOper = security->getBool("genericoper");
SyntaxHints = options->getBool("syntaxhints");
CycleHostsFromUser = options->getBool("cyclehostsfromuser");
- UndernetMsgPrefix = options->getBool("ircumsgprefix");
FullHostInTopic = options->getBool("hostintopic");
MaxTargets = security->getInt("maxtargets", 20, 1, 31);
DefaultModes = options->getString("defaultmodes", "not");
@@ -394,33 +430,22 @@ void ServerConfig::Fill()
OperMaxChans = ConfValue("channels")->getInt("opers");
c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone", 32);
c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone", 128);
- Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
- Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
- Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
- Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
- Limits.MaxHost = ConfValue("limits")->getInt("maxhost", 64);
- Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
- Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
- Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
- Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
- Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
- Limits.MaxLine = ConfValue("limits")->getInt("maxline", 512);
+ Limits = ServerLimits(ConfValue("limits"));
Paths.Config = ConfValue("path")->getString("configdir", INSPIRCD_CONFIG_PATH);
Paths.Data = ConfValue("path")->getString("datadir", INSPIRCD_DATA_PATH);
Paths.Log = ConfValue("path")->getString("logdir", INSPIRCD_LOG_PATH);
Paths.Module = ConfValue("path")->getString("moduledir", INSPIRCD_MODULE_PATH);
- InvBypassModes = options->getBool("invitebypassmodes", true);
NoSnoticeStack = options->getBool("nosnoticestack", false);
if (Network.find(' ') != std::string::npos)
throw CoreException(Network + " is not a valid network name. A network name must not contain spaces.");
std::string defbind = options->getString("defaultbind");
- if (assign(defbind) == "ipv4")
+ if (stdalgo::string::equalsci(defbind, "ipv4"))
{
WildcardIPv6 = false;
}
- else if (assign(defbind) == "ipv6")
+ else if (stdalgo::string::equalsci(defbind, "ipv6"))
{
WildcardIPv6 = true;
}
@@ -458,11 +483,6 @@ void ServerConfig::Fill()
DisabledCModes[*p - 'A'] = 1;
}
- memset(HideModeLists, 0, sizeof(HideModeLists));
- modes = ConfValue("security")->getString("hidemodes");
- for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
- HideModeLists[(unsigned char) *p] = true;
-
std::string v = security->getString("announceinvites");
if (v == "ops")
@@ -556,14 +576,14 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
// write once here, to try it out and make sure its ok
if (valid)
- ServerInstance->WritePID(this->PID);
+ ServerInstance->WritePID(this->PID, !old);
ConfigTagList binds = ConfTags("bind");
if (binds.first == binds.second)
errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl
<< "You will need to do this if you want clients to be able to connect!" << std::endl;
- if (old)
+ if (old && valid)
{
// On first run, ports are bound later on
FailedPortList pl;
@@ -601,7 +621,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
std::cout << line << std::endl;
// If a user is rehashing, tell them directly
if (user)
- user->SendText(":%s NOTICE %s :*** %s", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), line.c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** %s", line.c_str()));
// Also tell opers
ServerInstance->SNO->WriteGlobalSno('a', line);
}
@@ -615,11 +635,11 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
ConfigTag *tag = (*it)->config;
// Make sure our connection class allows motd colors
if(!tag->getBool("allowmotdcolors"))
- continue;
+ continue;
ConfigFileCache::iterator file = this->Files.find(tag->getString("motd", "motd"));
if (file != this->Files.end())
- InspIRCd::ProcessColors(file->second);
+ InspIRCd::ProcessColors(file->second);
}
/* No old configuration -> initial boot, nothing more to do here */
@@ -641,8 +661,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
ApplyModules(user);
if (user)
- user->SendText(":%s NOTICE %s :*** Successfully rehashed server.",
- ServerInstance->Config->ServerName.c_str(), user->nick.c_str());
+ user->WriteRemoteNotice("*** Successfully rehashed server.");
ServerInstance->SNO->WriteGlobalSno('a', "*** Successfully rehashed server.");
}
@@ -658,6 +677,7 @@ void ServerConfig::ApplyModules(User* user)
std::string name;
if (tag->readString("name", name))
{
+ name = ModuleManager::ExpandModName(name);
// if this module is already loaded, the erase will succeed, so we need do nothing
// otherwise, we need to add the module (which will be done later)
if (removed_modules.erase(name) == 0)
@@ -676,33 +696,33 @@ void ServerConfig::ApplyModules(User* user)
ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s", modname.c_str());
if (user)
- user->WriteNumeric(RPL_UNLOADEDMODULE, "%s :Module %s successfully unloaded.", modname.c_str(), modname.c_str());
+ user->WriteNumeric(RPL_UNLOADEDMODULE, modname, InspIRCd::Format("Module %s successfully unloaded.", modname.c_str()));
else
ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", modname.c_str());
}
else
{
if (user)
- user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :Failed to unload module %s: %s", modname.c_str(), modname.c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, modname, InspIRCd::Format("Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str()));
else
- ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str());
+ ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str());
}
}
for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
{
- if (ServerInstance->Modules->Load(adding->c_str()))
+ if (ServerInstance->Modules->Load(*adding))
{
ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
if (user)
- user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %s successfully loaded.", adding->c_str(), adding->c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, *adding, InspIRCd::Format("Module %s successfully loaded.", adding->c_str()));
else
ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
}
else
{
if (user)
- user->WriteNumeric(ERR_CANTLOADMODULE, "%s :Failed to load module %s: %s", adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTLOADMODULE, *adding, InspIRCd::Format("Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str()));
else
ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
}
@@ -713,7 +733,7 @@ ConfigTag* ServerConfig::ConfValue(const std::string &tag)
{
ConfigTagList found = config_data.equal_range(tag);
if (found.first == found.second)
- return NULL;
+ return EmptyTag;
ConfigTag* rv = found.first->second;
found.first++;
if (found.first != found.second)
@@ -772,6 +792,7 @@ void ConfigReaderThread::Finish()
* XXX: The order of these is IMPORTANT, do not reorder them without testing
* thoroughly!!!
*/
+ ServerInstance->Users.RehashCloneCounts();
ServerInstance->XLines->CheckELines();
ServerInstance->XLines->ApplyLines();
ChanModeReference ban(NULL, "ban");
@@ -784,6 +805,9 @@ void ConfigReaderThread::Finish()
for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
i->second->ReadConfig(status);
+ // The description of this server may have changed - update it for WHOIS etc.
+ ServerInstance->FakeClient->server->description = Config->ServerDesc;
+
ServerInstance->ISupport.Build();
ServerInstance->Logs->CloseLogs();
diff --git a/src/coremods/core_channel/cmd_invite.cpp b/src/coremods/core_channel/cmd_invite.cpp
index 3260d7862..a1319ebc0 100644
--- a/src/coremods/core_channel/cmd_invite.cpp
+++ b/src/coremods/core_channel/cmd_invite.cpp
@@ -22,9 +22,11 @@
#include "inspircd.h"
#include "core_channel.h"
+#include "invite.h"
-CommandInvite::CommandInvite(Module* parent)
+CommandInvite::CommandInvite(Module* parent, Invite::APIImpl& invapiimpl)
: Command(parent, "INVITE", 0, 0)
+ , invapi(invapiimpl)
{
Penalty = 4;
syntax = "[<nick> <channel>]";
@@ -36,7 +38,7 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use
{
ModResult MOD_RESULT;
- if (parameters.size() == 2 || parameters.size() == 3)
+ if (parameters.size() >= 2)
{
User* u;
if (IS_LOCAL(user))
@@ -46,29 +48,42 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use
Channel* c = ServerInstance->FindChan(parameters[1]);
time_t timeout = 0;
- if (parameters.size() == 3)
+ if (parameters.size() >= 3)
{
if (IS_LOCAL(user))
timeout = ServerInstance->Time() + InspIRCd::Duration(parameters[2]);
- else
- timeout = ConvToInt(parameters[2]);
+ else if (parameters.size() > 3)
+ timeout = ConvToInt(parameters[3]);
}
if ((!c) || (!u) || (u->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c ? parameters[0].c_str() : parameters[1].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(c ? parameters[0] : parameters[1]));
return CMD_FAILURE;
}
+ // Verify channel timestamp if the INVITE is coming from a remote server
+ if (!IS_LOCAL(user))
+ {
+ // Remote INVITE commands must carry a channel timestamp
+ if (parameters.size() < 3)
+ return CMD_INVALID;
+
+ // Drop the invite if our channel TS is lower
+ time_t RemoteTS = ConvToInt(parameters[2]);
+ if (c->age < RemoteTS)
+ return CMD_FAILURE;
+ }
+
if ((IS_LOCAL(user)) && (!c->HasUser(user)))
{
- user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", c->name.c_str());
+ user->WriteNumeric(ERR_NOTONCHANNEL, c->name, "You're not on that channel!");
return CMD_FAILURE;
}
if (c->HasUser(u))
{
- user->WriteNumeric(ERR_USERONCHANNEL, "%s %s :is already on channel", u->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_USERONCHANNEL, u->nick, c->name, "is already on channel");
return CMD_FAILURE;
}
@@ -87,8 +102,8 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use
{
// Check whether halfop mode is available and phrase error message accordingly
ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be a channel %soperator",
- c->name.c_str(), (mh && mh->name == "halfop" ? "half-" : ""));
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, InspIRCd::Format("You must be a channel %soperator",
+ (mh && mh->name == "halfop" ? "half-" : "")));
return CMD_FAILURE;
}
}
@@ -96,49 +111,59 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use
if (IS_LOCAL(u))
{
- Invitation::Create(c, IS_LOCAL(u), timeout);
+ invapi.Create(IS_LOCAL(u), c, timeout);
u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str());
}
if (IS_LOCAL(user))
- user->WriteNumeric(RPL_INVITING, "%s %s", u->nick.c_str(),c->name.c_str());
+ {
+ user->WriteNumeric(RPL_INVITING, u->nick, c->name);
+ if (u->IsAway())
+ user->WriteNumeric(RPL_AWAY, u->nick, u->awaymsg);
+ }
- if (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE)
+ char prefix = 0;
+ unsigned int minrank = 0;
+ switch (ServerInstance->Config->AnnounceInvites)
{
- char prefix;
- switch (ServerInstance->Config->AnnounceInvites)
+ case ServerConfig::INVITE_ANNOUNCE_OPS:
{
- case ServerConfig::INVITE_ANNOUNCE_OPS:
- {
- prefix = '@';
- break;
- }
- case ServerConfig::INVITE_ANNOUNCE_DYNAMIC:
- {
- PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
- prefix = (mh && mh->name == "halfop" ? mh->GetPrefix() : '@');
- break;
- }
- default:
+ prefix = '@';
+ minrank = OP_VALUE;
+ break;
+ }
+ case ServerConfig::INVITE_ANNOUNCE_DYNAMIC:
+ {
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
+ if ((mh) && (mh->name == "halfop"))
{
- prefix = 0;
- break;
+ prefix = mh->GetPrefix();
+ minrank = mh->GetPrefixRank();
}
+ break;
+ }
+ default:
+ {
}
- c->WriteAllExceptSender(user, true, prefix, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str());
}
- FOREACH_MOD(OnUserInvite, (user,u,c,timeout));
+
+ CUList excepts;
+ FOREACH_MOD(OnUserInvite, (user, u, c, timeout, minrank, excepts));
+
+ if (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE)
+ c->WriteAllExcept(user, true, prefix, excepts, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str());
}
else if (IS_LOCAL(user))
{
// pinched from ircu - invite with not enough parameters shows channels
// youve been invited to but haven't joined yet.
- InviteList& il = IS_LOCAL(user)->GetInviteList();
- for (InviteList::const_iterator i = il.begin(); i != il.end(); ++i)
+ const Invite::List* list = invapi.GetList(IS_LOCAL(user));
+ if (list)
{
- user->WriteNumeric(RPL_INVITELIST, ":%s", (*i)->chan->name.c_str());
+ for (Invite::List::const_iterator i = list->begin(); i != list->end(); ++i)
+ user->WriteNumeric(RPL_INVITELIST, (*i)->chan->name);
}
- user->WriteNumeric(RPL_ENDOFINVITELIST, ":End of INVITE list");
+ user->WriteNumeric(RPL_ENDOFINVITELIST, "End of INVITE list");
}
return CMD_SUCCESS;
}
diff --git a/src/coremods/core_channel/cmd_join.cpp b/src/coremods/core_channel/cmd_join.cpp
index 1945bf52e..06e203ead 100644
--- a/src/coremods/core_channel/cmd_join.cpp
+++ b/src/coremods/core_channel/cmd_join.cpp
@@ -55,6 +55,6 @@ CmdResult CommandJoin::HandleLocal(const std::vector<std::string>& parameters, L
}
}
- user->WriteNumeric(ERR_NOSUCHCHANNEL, "%s :Invalid channel name", parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHCHANNEL, parameters[0], "Invalid channel name");
return CMD_FAILURE;
}
diff --git a/src/coremods/core_channel/cmd_kick.cpp b/src/coremods/core_channel/cmd_kick.cpp
index 715f35d54..7f8a8a329 100644
--- a/src/coremods/core_channel/cmd_kick.cpp
+++ b/src/coremods/core_channel/cmd_kick.cpp
@@ -31,7 +31,6 @@ CommandKick::CommandKick(Module* parent)
*/
CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User *user)
{
- std::string reason;
Channel* c = ServerInstance->FindChan(parameters[0]);
User* u;
@@ -45,7 +44,7 @@ CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User
if ((!u) || (!c) || (u->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c ? parameters[1].c_str() : parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(c ? parameters[1] : parameters[0]));
return CMD_FAILURE;
}
@@ -55,27 +54,70 @@ CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User
srcmemb = c->GetUser(user);
if (!srcmemb)
{
- user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", parameters[0].c_str());
+ user->WriteNumeric(ERR_NOTONCHANNEL, parameters[0], "You're not on that channel!");
return CMD_FAILURE;
}
if (u->server->IsULine())
{
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You may not kick a u-lined client", c->name.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, "You may not kick a u-lined client");
return CMD_FAILURE;
}
}
- if (parameters.size() > 2)
+ const Channel::MemberMap::iterator victimiter = c->userlist.find(u);
+ if (victimiter == c->userlist.end())
{
- reason.assign(parameters[2], 0, ServerInstance->Config->Limits.MaxKick);
+ user->WriteNumeric(ERR_USERNOTINCHANNEL, u->nick, c->name, "They are not on that channel");
+ return CMD_FAILURE;
}
- else
+ Membership* const memb = victimiter->second;
+
+ // KICKs coming from servers can carry a membership id
+ if ((!IS_LOCAL(user)) && (parameters.size() > 3))
{
- reason.assign(user->nick, 0, ServerInstance->Config->Limits.MaxKick);
+ // If the current membership id is not equal to the one in the message then the user rejoined
+ if (memb->id != Membership::IdFromString(parameters[2]))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Dropped KICK due to membership id mismatch: " + ConvToStr(memb->id) + " != " + parameters[2]);
+ return CMD_FAILURE;
+ }
+ }
+
+ const bool has_reason = (parameters.size() > 2);
+ const std::string reason((has_reason ? parameters.back() : user->nick), 0, ServerInstance->Config->Limits.MaxKick);
+
+ // Do the following checks only if the KICK is done by a local user;
+ // each server enforces its own rules.
+ if (srcmemb)
+ {
+ // Modules are allowed to explicitly allow or deny kicks done by local users
+ ModResult res;
+ FIRST_MOD_RESULT(OnUserPreKick, res, (user, memb, reason));
+ if (res == MOD_RES_DENY)
+ return CMD_FAILURE;
+
+ if (res == MOD_RES_PASSTHRU)
+ {
+ unsigned int them = srcmemb->getRank();
+ unsigned int req = HALFOP_VALUE;
+ for (std::string::size_type i = 0; i < memb->modes.length(); i++)
+ {
+ ModeHandler* mh = ServerInstance->Modes->FindMode(memb->modes[i], MODETYPE_CHANNEL);
+ if (mh && mh->GetLevelRequired() > req)
+ req = mh->GetLevelRequired();
+ }
+
+ if (them < req)
+ {
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, InspIRCd::Format("You must be a channel %soperator",
+ req > HALFOP_VALUE ? "" : "half-"));
+ return CMD_FAILURE;
+ }
+ }
}
- c->KickUser(user, u, reason, srcmemb);
+ c->KickUser(user, victimiter, reason);
return CMD_SUCCESS;
}
diff --git a/src/coremods/core_channel/cmd_names.cpp b/src/coremods/core_channel/cmd_names.cpp
index 20faae774..21fe43cca 100644
--- a/src/coremods/core_channel/cmd_names.cpp
+++ b/src/coremods/core_channel/cmd_names.cpp
@@ -22,21 +22,23 @@
#include "core_channel.h"
CommandNames::CommandNames(Module* parent)
- : Command(parent, "NAMES", 0, 0)
+ : SplitCommand(parent, "NAMES", 0, 0)
, secretmode(parent, "secret")
+ , privatemode(parent, "private")
+ , invisiblemode(parent, "invisible")
{
syntax = "{<channel>{,<channel>}}";
}
/** Handle /NAMES
*/
-CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandNames::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
{
Channel* c;
if (!parameters.size())
{
- user->WriteNumeric(RPL_ENDOFNAMES, "* :End of /NAMES list.");
+ user->WriteNumeric(RPL_ENDOFNAMES, '*', "End of /NAMES list.");
return CMD_SUCCESS;
}
@@ -51,14 +53,62 @@ CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User
// - the user doing the /NAMES is inside the channel
// - the user doing the /NAMES has the channels/auspex privilege
- bool has_user = c->HasUser(user);
- if ((!c->IsModeSet(secretmode)) || (has_user) || (user->HasPrivPermission("channels/auspex")))
+ // If the user is inside the channel or has privs, instruct SendNames() to show invisible (+i) members
+ bool show_invisible = ((c->HasUser(user)) || (user->HasPrivPermission("channels/auspex")));
+ if ((show_invisible) || (!c->IsModeSet(secretmode)))
{
- c->UserList(user, has_user);
+ SendNames(user, c, show_invisible);
return CMD_SUCCESS;
}
}
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
+
+void CommandNames::SendNames(LocalUser* user, Channel* chan, bool show_invisible)
+{
+ Numeric::Builder<' '> reply(user, RPL_NAMREPLY, false, chan->name.size() + 3);
+ Numeric::Numeric& numeric = reply.GetNumeric();
+ if (chan->IsModeSet(secretmode))
+ numeric.push(std::string(1, '@'));
+ else if (chan->IsModeSet(privatemode))
+ numeric.push(std::string(1, '*'));
+ else
+ numeric.push(std::string(1, '='));
+
+ numeric.push(chan->name);
+ numeric.push(std::string());
+
+ std::string prefixlist;
+ std::string nick;
+ const Channel::MemberMap& members = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = members.begin(); i != members.end(); ++i)
+ {
+ if ((!show_invisible) && (i->first->IsModeSet(invisiblemode)))
+ {
+ // Member is invisible and we are not supposed to show them
+ continue;
+ }
+
+ Membership* const memb = i->second;
+
+ prefixlist.clear();
+ char prefix = memb->GetPrefixChar();
+ if (prefix)
+ prefixlist.push_back(prefix);
+ nick = i->first->nick;
+
+ ModResult res;
+ FIRST_MOD_RESULT(OnNamesListItem, res, (user, memb, prefixlist, nick));
+
+ // See if a module wants us to exclude this user from NAMES
+ if (res == MOD_RES_DENY)
+ continue;
+
+ reply.Add(prefixlist, nick);
+ }
+
+ reply.Flush();
+ user->WriteNumeric(RPL_ENDOFNAMES, chan->name, "End of /NAMES list.");
+}
diff --git a/src/coremods/core_channel/cmd_topic.cpp b/src/coremods/core_channel/cmd_topic.cpp
index ea723c024..6d99edcc6 100644
--- a/src/coremods/core_channel/cmd_topic.cpp
+++ b/src/coremods/core_channel/cmd_topic.cpp
@@ -37,7 +37,7 @@ CmdResult CommandTopic::HandleLocal(const std::vector<std::string>& parameters,
Channel* c = ServerInstance->FindChan(parameters[0]);
if (!c)
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
@@ -45,18 +45,17 @@ CmdResult CommandTopic::HandleLocal(const std::vector<std::string>& parameters,
{
if ((c->IsModeSet(secretmode)) && (!c->HasUser(user)))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c->name.c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(c->name));
return CMD_FAILURE;
}
if (c->topic.length())
{
- user->WriteNumeric(RPL_TOPIC, "%s :%s", c->name.c_str(), c->topic.c_str());
- user->WriteNumeric(RPL_TOPICTIME, "%s %s %lu", c->name.c_str(), c->setby.c_str(), (unsigned long)c->topicset);
+ Topic::ShowTopic(user, c);
}
else
{
- user->WriteNumeric(RPL_NOTOPICSET, "%s :No topic is set.", c->name.c_str());
+ user->WriteNumeric(RPL_NOTOPICSET, c->name, "No topic is set.");
}
return CMD_SUCCESS;
}
@@ -71,16 +70,28 @@ CmdResult CommandTopic::HandleLocal(const std::vector<std::string>& parameters,
{
if (!c->HasUser(user))
{
- user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", c->name.c_str());
+ user->WriteNumeric(ERR_NOTONCHANNEL, c->name, "You're not on that channel!");
return CMD_FAILURE;
}
if (c->IsModeSet(topiclockmode) && !ServerInstance->OnCheckExemption(user, c, "topiclock").check(c->GetPrefixValue(user) >= HALFOP_VALUE))
{
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You do not have access to change the topic on this channel", c->name.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, "You do not have access to change the topic on this channel");
return CMD_FAILURE;
}
}
- c->SetTopic(user, t);
+ // Make sure the topic is not longer than the limit in the config
+ if (t.length() > ServerInstance->Config->Limits.MaxTopic)
+ t.erase(ServerInstance->Config->Limits.MaxTopic);
+
+ // Only change if the new topic is different than the current one
+ if (c->topic != t)
+ c->SetTopic(user, t, ServerInstance->Time());
return CMD_SUCCESS;
}
+
+void Topic::ShowTopic(LocalUser* user, Channel* chan)
+{
+ user->WriteNumeric(RPL_TOPIC, chan->name, chan->topic);
+ user->WriteNumeric(RPL_TOPICTIME, chan->name, chan->setby, (unsigned long)chan->topicset);
+}
diff --git a/src/coremods/core_channel/core_channel.cpp b/src/coremods/core_channel/core_channel.cpp
index 47f722e1e..aba4d9769 100644
--- a/src/coremods/core_channel/core_channel.cpp
+++ b/src/coremods/core_channel/core_channel.cpp
@@ -1,7 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2014-2015 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -19,19 +19,100 @@
#include "inspircd.h"
#include "core_channel.h"
+#include "invite.h"
class CoreModChannel : public Module
{
+ Invite::APIImpl invapi;
CommandInvite cmdinvite;
CommandJoin cmdjoin;
CommandKick cmdkick;
CommandNames cmdnames;
CommandTopic cmdtopic;
+ ModResult IsInvited(User* user, Channel* chan)
+ {
+ LocalUser* localuser = IS_LOCAL(user);
+ if ((localuser) && (invapi.IsInvited(localuser, chan)))
+ return MOD_RES_ALLOW;
+ return MOD_RES_PASSTHRU;
+ }
+
public:
CoreModChannel()
- : cmdinvite(this), cmdjoin(this), cmdkick(this), cmdnames(this), cmdtopic(this)
+ : invapi(this)
+ , cmdinvite(this, invapi), cmdjoin(this), cmdkick(this), cmdnames(this), cmdtopic(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* optionstag = ServerInstance->Config->ConfValue("options");
+ Implementation events[] = { I_OnCheckKey, I_OnCheckLimit, I_OnCheckChannelBan };
+ if (optionstag->getBool("invitebypassmodes", true))
+ ServerInstance->Modules.Attach(events, this, sizeof(events)/sizeof(Implementation));
+ else
+ {
+ for (unsigned int i = 0; i < sizeof(events)/sizeof(Implementation); i++)
+ ServerInstance->Modules.Detach(events[i], this);
+ }
+ }
+
+ void OnPostJoin(Membership* memb) CXX11_OVERRIDE
+ {
+ Channel* const chan = memb->chan;
+ LocalUser* const localuser = IS_LOCAL(memb->user);
+ if (localuser)
+ {
+ // Remove existing invite, if any
+ invapi.Remove(localuser, chan);
+
+ if (chan->topicset)
+ Topic::ShowTopic(localuser, chan);
+
+ // Show all members of the channel, including invisible (+i) users
+ cmdnames.SendNames(localuser, chan, true);
+ }
+ }
+
+ ModResult OnCheckKey(User* user, Channel* chan, const std::string& keygiven) CXX11_OVERRIDE
+ {
+ // Hook only runs when being invited bypasses +bkl
+ return IsInvited(user, chan);
+ }
+
+ ModResult OnCheckChannelBan(User* user, Channel* chan) CXX11_OVERRIDE
+ {
+ // Hook only runs when being invited bypasses +bkl
+ return IsInvited(user, chan);
+ }
+
+ ModResult OnCheckLimit(User* user, Channel* chan) CXX11_OVERRIDE
+ {
+ // Hook only runs when being invited bypasses +bkl
+ return IsInvited(user, chan);
+ }
+
+ ModResult OnCheckInvite(User* user, Channel* chan) CXX11_OVERRIDE
+ {
+ // Hook always runs
+ return IsInvited(user, chan);
+ }
+
+ void OnUserDisconnect(LocalUser* user) CXX11_OVERRIDE
+ {
+ invapi.RemoveAll(user);
+ }
+
+ void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
+ {
+ // Make sure the channel won't appear in invite lists from now on, don't wait for cull to unset the ext
+ invapi.RemoveAll(chan);
+ }
+
+ void Prioritize() CXX11_OVERRIDE
{
+ ServerInstance->Modules.SetPriority(this, I_OnPostJoin, PRIORITY_FIRST);
}
Version GetVersion() CXX11_OVERRIDE
diff --git a/src/coremods/core_channel/core_channel.h b/src/coremods/core_channel/core_channel.h
index d3adbc9c9..0dafde8cb 100644
--- a/src/coremods/core_channel/core_channel.h
+++ b/src/coremods/core_channel/core_channel.h
@@ -21,14 +21,26 @@
#include "inspircd.h"
+namespace Topic
+{
+ void ShowTopic(LocalUser* user, Channel* chan);
+}
+
+namespace Invite
+{
+ class APIImpl;
+}
+
/** Handle /INVITE.
*/
class CommandInvite : public Command
{
+ Invite::APIImpl& invapi;
+
public:
/** Constructor for invite.
*/
- CommandInvite (Module* parent);
+ CommandInvite(Module* parent, Invite::APIImpl& invapiimpl);
/** Handle command.
* @param parameters The parameters to the command
@@ -78,9 +90,11 @@ class CommandTopic : public SplitCommand
/** Handle /NAMES.
*/
-class CommandNames : public Command
+class CommandNames : public SplitCommand
{
ChanModeReference secretmode;
+ ChanModeReference privatemode;
+ UserModeReference invisiblemode;
public:
/** Constructor for names.
@@ -92,7 +106,14 @@ class CommandNames : public Command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
+
+ /** Spool the NAMES list for a given channel to the given user
+ * @param user User to spool the NAMES list to
+ * @param chan Channel whose nicklist to send
+ * @param show_invisible True to show invisible (+i) members to the user, false to omit them from the list
+ */
+ void SendNames(LocalUser* user, Channel* chan, bool show_invisible);
};
/** Handle /KICK.
diff --git a/src/coremods/core_channel/invite.cpp b/src/coremods/core_channel/invite.cpp
new file mode 100644
index 000000000..7ac662edc
--- /dev/null
+++ b/src/coremods/core_channel/invite.cpp
@@ -0,0 +1,208 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2012, 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "invite.h"
+
+class InviteExpireTimer : public Timer
+{
+ Invite::Invite* const inv;
+
+ bool Tick(time_t currtime) CXX11_OVERRIDE;
+
+ public:
+ InviteExpireTimer(Invite::Invite* invite, time_t timeout);
+};
+
+static Invite::APIImpl* apiimpl;
+
+void RemoveInvite(Invite::Invite* inv, bool remove_user, bool remove_chan)
+{
+ apiimpl->Destruct(inv, remove_user, remove_chan);
+}
+
+void UnserializeInvite(LocalUser* user, const std::string& str)
+{
+ apiimpl->Unserialize(user, str);
+}
+
+Invite::APIBase::APIBase(Module* parent)
+ : DataProvider(parent, "core_channel_invite")
+{
+}
+
+Invite::APIImpl::APIImpl(Module* parent)
+ : APIBase(parent)
+ , userext(parent, "invite_user")
+ , chanext(parent, "invite_chan")
+{
+ apiimpl = this;
+}
+
+void Invite::APIImpl::Destruct(Invite* inv, bool remove_user, bool remove_chan)
+{
+ Store<LocalUser>* ustore = userext.get(inv->user);
+ if (ustore)
+ {
+ ustore->invites.erase(inv);
+ if ((remove_user) && (ustore->invites.empty()))
+ userext.unset(inv->user);
+ }
+
+ Store<Channel>* cstore = chanext.get(inv->chan);
+ if (cstore)
+ {
+ cstore->invites.erase(inv);
+ if ((remove_chan) && (cstore->invites.empty()))
+ chanext.unset(inv->chan);
+ }
+
+ delete inv;
+}
+
+bool Invite::APIImpl::Remove(LocalUser* user, Channel* chan)
+{
+ Invite* inv = Find(user, chan);
+ if (inv)
+ {
+ Destruct(inv);
+ return true;
+ }
+ return false;
+}
+
+void Invite::APIImpl::Create(LocalUser* user, Channel* chan, time_t timeout)
+{
+ if ((timeout != 0) && (ServerInstance->Time() >= timeout))
+ // Expired, don't bother
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::APIImpl::Create(): user=%s chan=%s timeout=%lu", user->uuid.c_str(), chan->name.c_str(), (unsigned long)timeout);
+
+ Invite* inv = Find(user, chan);
+ if (inv)
+ {
+ // We only ever extend invites, so nothing to do if the existing one is not timed
+ if (!inv->IsTimed())
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::APIImpl::Create(): changing expiration in %p", (void*) inv);
+ if (timeout == 0)
+ {
+ // Convert timed invite to non-expiring
+ delete inv->expiretimer;
+ inv->expiretimer = NULL;
+ }
+ else if (inv->expiretimer->GetTrigger() >= ServerInstance->Time() + timeout)
+ {
+ // New expiration time is further than the current, extend the expiration
+ inv->expiretimer->SetInterval(timeout - ServerInstance->Time());
+ }
+ }
+ else
+ {
+ inv = new Invite(user, chan);
+ if (timeout)
+ {
+ inv->expiretimer = new InviteExpireTimer(inv, timeout - ServerInstance->Time());
+ ServerInstance->Timers.AddTimer(inv->expiretimer);
+ }
+
+ userext.get(user, true)->invites.push_front(inv);
+ chanext.get(chan, true)->invites.push_front(inv);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::APIImpl::Create(): created new Invite %p", (void*) inv);
+ }
+}
+
+Invite::Invite* Invite::APIImpl::Find(LocalUser* user, Channel* chan)
+{
+ const List* list = APIImpl::GetList(user);
+ if (!list)
+ return NULL;
+
+ for (List::iterator i = list->begin(); i != list->end(); ++i)
+ {
+ Invite* inv = *i;
+ if (inv->chan == chan)
+ return inv;
+ }
+
+ return NULL;
+}
+
+const Invite::List* Invite::APIImpl::GetList(LocalUser* user)
+{
+ Store<LocalUser>* list = userext.get(user);
+ if (list)
+ return &list->invites;
+ return NULL;
+}
+
+void Invite::APIImpl::Unserialize(LocalUser* user, const std::string& value)
+{
+ irc::spacesepstream ss(value);
+ for (std::string channame, exptime; (ss.GetToken(channame) && ss.GetToken(exptime)); )
+ {
+ Channel* chan = ServerInstance->FindChan(channame);
+ if (chan)
+ Create(user, chan, ConvToInt(exptime));
+ }
+}
+
+Invite::Invite::Invite(LocalUser* u, Channel* c)
+ : user(u)
+ , chan(c)
+ , expiretimer(NULL)
+{
+}
+
+Invite::Invite::~Invite()
+{
+ delete expiretimer;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::~ %p", (void*) this);
+}
+
+void Invite::Invite::Serialize(SerializeFormat format, bool show_chans, std::string& out)
+{
+ if (show_chans)
+ out.append(this->chan->name);
+ else
+ out.append((format == FORMAT_USER) ? user->nick : user->uuid);
+ out.push_back(' ');
+
+ if (expiretimer)
+ out.append(ConvToStr(expiretimer->GetTrigger()));
+ else
+ out.push_back('0');
+ out.push_back(' ');
+}
+
+InviteExpireTimer::InviteExpireTimer(Invite::Invite* invite, time_t timeout)
+ : Timer(timeout)
+ , inv(invite)
+{
+}
+
+bool InviteExpireTimer::Tick(time_t currtime)
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "InviteExpireTimer::Tick(): expired %p", (void*) inv);
+ apiimpl->Destruct(inv);
+ return false;
+}
diff --git a/src/coremods/core_channel/invite.h b/src/coremods/core_channel/invite.h
new file mode 100644
index 000000000..2a99ec2df
--- /dev/null
+++ b/src/coremods/core_channel/invite.h
@@ -0,0 +1,127 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "modules/invite.h"
+
+namespace Invite
+{
+ template<typename T>
+ struct Store
+ {
+ typedef insp::intrusive_list<Invite, T> List;
+
+ /** List of pending Invites
+ */
+ List invites;
+ };
+
+ template<typename T, ExtensionItem::ExtensibleType ExtType>
+ class ExtItem;
+
+ class APIImpl;
+}
+
+extern void RemoveInvite(Invite::Invite* inv, bool remove_user, bool remove_chan);
+extern void UnserializeInvite(LocalUser* user, const std::string& value);
+
+template<typename T, ExtensionItem::ExtensibleType ExtType>
+class Invite::ExtItem : public ExtensionItem
+{
+ public:
+ ExtItem(Module* owner, const char* extname)
+ : ExtensionItem(extname, ExtType, owner)
+ {
+ }
+
+ Store<T>* get(Extensible* ext, bool create = false)
+ {
+ Store<T>* store = static_cast<Store<T>*>(get_raw(ext));
+ if ((create) && (!store))
+ {
+ store = new Store<T>;
+ set_raw(ext, store);
+ }
+ return store;
+ }
+
+ void unset(Extensible* ext)
+ {
+ void* store = unset_raw(ext);
+ if (store)
+ free(store);
+ }
+
+ void free(void* item) CXX11_OVERRIDE
+ {
+ Store<T>* store = static_cast<Store<T>*>(item);
+ for (typename Store<T>::List::iterator i = store->invites.begin(); i != store->invites.end(); )
+ {
+ Invite* inv = *i;
+ // Destructing the Invite invalidates the iterator, so move it now
+ ++i;
+ RemoveInvite(inv, (ExtType != ExtensionItem::EXT_USER), (ExtType == ExtensionItem::EXT_USER));
+ }
+
+ delete store;
+ }
+
+ std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
+ {
+ if (format == FORMAT_NETWORK)
+ return std::string();
+
+ std::string ret;
+ Store<T>* store = static_cast<Store<T>*>(item);
+ for (typename insp::intrusive_list<Invite, T>::iterator i = store->invites.begin(); i != store->invites.end(); ++i)
+ {
+ Invite* inv = *i;
+ inv->Serialize(format, (ExtType == ExtensionItem::EXT_USER), ret);
+ }
+ if (!ret.empty())
+ ret.erase(ret.length()-1);
+ return ret;
+ }
+
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
+ {
+ if ((ExtType != ExtensionItem::EXT_CHANNEL) && (format != FORMAT_NETWORK))
+ UnserializeInvite(static_cast<LocalUser*>(container), value);
+ }
+};
+
+class Invite::APIImpl : public APIBase
+{
+ ExtItem<LocalUser, ExtensionItem::EXT_USER> userext;
+ ExtItem<Channel, ExtensionItem::EXT_CHANNEL> chanext;
+
+ public:
+ APIImpl(Module* owner);
+
+ void Create(LocalUser* user, Channel* chan, time_t timeout) CXX11_OVERRIDE;
+ Invite* Find(LocalUser* user, Channel* chan) CXX11_OVERRIDE;
+ bool Remove(LocalUser* user, Channel* chan) CXX11_OVERRIDE;
+ const List* GetList(LocalUser* user) CXX11_OVERRIDE;
+
+ void RemoveAll(LocalUser* user) { userext.unset(user); }
+ void RemoveAll(Channel* chan) { chanext.unset(chan); }
+ void Destruct(Invite* inv, bool remove_chan = true, bool remove_user = true);
+ void Unserialize(LocalUser* user, const std::string& value);
+};
diff --git a/src/coremods/core_dns.cpp b/src/coremods/core_dns.cpp
index 30c736757..7ee406a24 100644
--- a/src/coremods/core_dns.cpp
+++ b/src/coremods/core_dns.cpp
@@ -27,18 +27,30 @@
#pragma comment(lib, "Iphlpapi.lib")
#endif
+namespace DNS
+{
+ /** Maximum value of a dns request id, 16 bits wide, 0xFFFF.
+ */
+ const unsigned int MAX_REQUEST_ID = 0xFFFF;
+}
+
using namespace DNS;
/** A full packet sent or recieved to/from the nameserver
*/
class Packet : public Query
{
+ static bool IsValidName(const std::string& name)
+ {
+ return (name.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-") == std::string::npos);
+ }
+
void PackName(unsigned char* output, unsigned short output_size, unsigned short& pos, const std::string& name)
{
if (pos + name.length() + 2 > output_size)
throw Exception("Unable to pack name");
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Packing name " + name);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Packing name " + name);
irc::sepstream sep(name, '.');
std::string token;
@@ -109,27 +121,27 @@ class Packet : public Query
if (name.empty())
throw Exception("Unable to unpack name - no name");
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Unpack name " + name);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unpack name " + name);
return name;
}
Question UnpackQuestion(const unsigned char* input, unsigned short input_size, unsigned short& pos)
{
- Question question;
+ Question q;
- question.name = this->UnpackName(input, input_size, pos);
+ q.name = this->UnpackName(input, input_size, pos);
if (pos + 4 > input_size)
throw Exception("Unable to unpack question");
- question.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]);
+ q.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]);
pos += 2;
- question.qclass = input[pos] << 8 | input[pos + 1];
+ // Skip over query class code
pos += 2;
- return question;
+ return q;
}
ResourceRecord UnpackResourceRecord(const unsigned char* input, unsigned short input_size, unsigned short& pos)
@@ -142,7 +154,7 @@ class Packet : public Query
record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3];
pos += 4;
- //record.rdlength = input[pos] << 8 | input[pos + 1];
+ uint16_t rdlength = input[pos] << 8 | input[pos + 1];
pos += 2;
switch (record.type)
@@ -183,6 +195,22 @@ class Packet : public Query
case QUERY_PTR:
{
record.rdata = this->UnpackName(input, input_size, pos);
+ if (!IsValidName(record.rdata))
+ throw Exception("Invalid name"); // XXX: Causes the request to time out
+
+ break;
+ }
+ case QUERY_TXT:
+ {
+ if (pos + rdlength > input_size)
+ throw Exception("Unable to unpack txt resource record");
+
+ record.rdata = std::string(reinterpret_cast<const char *>(input + pos), rdlength);
+ pos += rdlength;
+
+ if (record.rdata.find_first_of("\r\n\0", 0, 3) != std::string::npos)
+ throw Exception("Invalid character in txt record");
+
break;
}
default:
@@ -190,7 +218,7 @@ class Packet : public Query
}
if (!record.name.empty() && !record.rdata.empty())
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: " + record.name + " -> " + record.rdata);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, record.name + " -> " + record.rdata);
return record;
}
@@ -201,7 +229,7 @@ class Packet : public Query
static const int HEADER_LENGTH = 12;
/* ID for this packet */
- unsigned short id;
+ RequestId id;
/* Flags on the packet */
unsigned short flags;
@@ -219,9 +247,6 @@ class Packet : public Query
this->id = (input[packet_pos] << 8) | input[packet_pos + 1];
packet_pos += 2;
- if (this->id >= MAX_REQUEST_ID)
- throw Exception("Query ID too large?");
-
this->flags = (input[packet_pos] << 8) | input[packet_pos + 1];
packet_pos += 2;
@@ -237,10 +262,12 @@ class Packet : public Query
unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1];
packet_pos += 2;
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: qdcount: " + ConvToStr(qdcount) + " ancount: " + ConvToStr(ancount) + " nscount: " + ConvToStr(nscount) + " arcount: " + ConvToStr(arcount));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "qdcount: " + ConvToStr(qdcount) + " ancount: " + ConvToStr(ancount) + " nscount: " + ConvToStr(nscount) + " arcount: " + ConvToStr(arcount));
+
+ if (qdcount != 1)
+ throw Exception("Question count != 1 in incoming packet");
- for (unsigned i = 0; i < qdcount; ++i)
- this->questions.push_back(this->UnpackQuestion(input, len, packet_pos));
+ this->question = this->UnpackQuestion(input, len, packet_pos);
for (unsigned i = 0; i < ancount; ++i)
this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos));
@@ -257,18 +284,17 @@ class Packet : public Query
output[pos++] = this->id & 0xFF;
output[pos++] = this->flags >> 8;
output[pos++] = this->flags & 0xFF;
- output[pos++] = this->questions.size() >> 8;
- output[pos++] = this->questions.size() & 0xFF;
- output[pos++] = this->answers.size() >> 8;
- output[pos++] = this->answers.size() & 0xFF;
+ output[pos++] = 0; // Question count, high byte
+ output[pos++] = 1; // Question count, low byte
+ output[pos++] = 0; // Answer count, high byte
+ output[pos++] = 0; // Answer count, low byte
output[pos++] = 0;
output[pos++] = 0;
output[pos++] = 0;
output[pos++] = 0;
- for (unsigned i = 0; i < this->questions.size(); ++i)
{
- Question& q = this->questions[i];
+ Question& q = this->question;
if (q.type == QUERY_PTR)
{
@@ -310,84 +336,9 @@ class Packet : public Query
memcpy(&output[pos], &s, 2);
pos += 2;
- s = htons(q.qclass);
- memcpy(&output[pos], &s, 2);
- pos += 2;
- }
-
- for (unsigned int i = 0; i < answers.size(); i++)
- {
- ResourceRecord& rr = answers[i];
-
- this->PackName(output, output_size, pos, rr.name);
-
- if (pos + 8 >= output_size)
- throw Exception("Unable to pack packet");
-
- short s = htons(rr.type);
- memcpy(&output[pos], &s, 2);
- pos += 2;
-
- s = htons(rr.qclass);
- memcpy(&output[pos], &s, 2);
- pos += 2;
-
- long l = htonl(rr.ttl);
- memcpy(&output[pos], &l, 4);
- pos += 4;
-
- switch (rr.type)
- {
- case QUERY_A:
- {
- if (pos + 6 > output_size)
- throw Exception("Unable to pack packet");
-
- irc::sockets::sockaddrs a;
- irc::sockets::aptosa(rr.rdata, 0, a);
-
- s = htons(4);
- memcpy(&output[pos], &s, 2);
- pos += 2;
-
- memcpy(&output[pos], &a.in4.sin_addr, 4);
- pos += 4;
- break;
- }
- case QUERY_AAAA:
- {
- if (pos + 18 > output_size)
- throw Exception("Unable to pack packet");
-
- irc::sockets::sockaddrs a;
- irc::sockets::aptosa(rr.rdata, 0, a);
-
- s = htons(16);
- memcpy(&output[pos], &s, 2);
- pos += 2;
-
- memcpy(&output[pos], &a.in6.sin6_addr, 16);
- pos += 16;
- break;
- }
- case QUERY_CNAME:
- case QUERY_PTR:
- {
- if (pos + 2 >= output_size)
- throw Exception("Unable to pack packet");
-
- unsigned short packet_pos_save = pos;
- pos += 2;
-
- this->PackName(output, output_size, pos, rr.rdata);
-
- s = htons(pos - packet_pos_save - 2);
- memcpy(&output[packet_pos_save], &s, 2);
- break;
- }
- default:
- break;
- }
+ // Query class, always IN
+ output[pos++] = 0;
+ output[pos++] = 1;
}
return pos;
@@ -400,6 +351,11 @@ class MyManager : public Manager, public Timer, public EventHandler
cache_map cache;
irc::sockets::sockaddrs myserver;
+ bool unloading;
+
+ /** Maximum number of entries in cache
+ */
+ static const unsigned int MAX_CACHE_SIZE = 1000;
static bool IsExpired(const Query& record, time_t now = ServerInstance->Time())
{
@@ -412,7 +368,7 @@ class MyManager : public Manager, public Timer, public EventHandler
*/
bool CheckCache(DNS::Request* req, const DNS::Question& question)
{
- ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: cache: Checking cache for " + question.name);
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "cache: Checking cache for " + question.name);
cache_map::iterator it = this->cache.find(question);
if (it == this->cache.end())
@@ -425,7 +381,7 @@ class MyManager : public Manager, public Timer, public EventHandler
return false;
}
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: cache: Using cached result for " + question.name);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: Using cached result for " + question.name);
record.cached = true;
req->OnLookupComplete(&record);
return true;
@@ -436,30 +392,49 @@ class MyManager : public Manager, public Timer, public EventHandler
*/
void AddCache(Query& r)
{
- const ResourceRecord& rr = r.answers[0];
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: cache: added cache for " + rr.name + " -> " + rr.rdata + " ttl: " + ConvToStr(rr.ttl));
- this->cache[r.questions[0]] = r;
+ if (cache.size() >= MAX_CACHE_SIZE)
+ cache.clear();
+
+ // Determine the lowest TTL value and use that as the TTL of the cache entry
+ unsigned int cachettl = UINT_MAX;
+ for (std::vector<ResourceRecord>::const_iterator i = r.answers.begin(); i != r.answers.end(); ++i)
+ {
+ const ResourceRecord& rr = *i;
+ if (rr.ttl < cachettl)
+ cachettl = rr.ttl;
+ }
+
+ cachettl = std::min(cachettl, (unsigned int)5*60);
+ ResourceRecord& rr = r.answers.front();
+ // Set TTL to what we've determined to be the lowest
+ rr.ttl = cachettl;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: added cache for " + rr.name + " -> " + rr.rdata + " ttl: " + ConvToStr(rr.ttl));
+ this->cache[r.question] = r;
}
public:
- DNS::Request* requests[MAX_REQUEST_ID];
+ DNS::Request* requests[MAX_REQUEST_ID+1];
- MyManager(Module* c) : Manager(c), Timer(3600, ServerInstance->Time(), true)
+ MyManager(Module* c) : Manager(c), Timer(5*60, true)
+ , unloading(false)
{
- for (int i = 0; i < MAX_REQUEST_ID; ++i)
+ for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
requests[i] = NULL;
ServerInstance->Timers.AddTimer(this);
}
~MyManager()
{
- for (int i = 0; i < MAX_REQUEST_ID; ++i)
+ // Ensure Process() will fail for new requests
+ unloading = true;
+
+ for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
{
DNS::Request* request = requests[i];
if (!request)
continue;
- Query rr(*request);
+ Query rr(request->question);
rr.error = ERROR_UNKNOWN;
request->OnError(&rr);
@@ -469,63 +444,75 @@ class MyManager : public Manager, public Timer, public EventHandler
void Process(DNS::Request* req)
{
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Processing request to lookup " + req->name + " of type " + ConvToStr(req->type) + " to " + this->myserver.addr());
+ if ((unloading) || (req->creator->dying))
+ throw Exception("Module is being unloaded");
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Processing request to lookup " + req->question.name + " of type " + ConvToStr(req->question.type) + " to " + this->myserver.addr());
/* Create an id */
unsigned int tries = 0;
+ int id;
do
{
- req->id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
+ id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID+1);
if (++tries == DNS::MAX_REQUEST_ID*5)
{
// If we couldn't find an empty slot this many times, do a sequential scan as a last
// resort. If an empty slot is found that way, go on, otherwise throw an exception
- req->id = 0;
- for (int i = 1; i < DNS::MAX_REQUEST_ID; i++)
+ id = -1;
+ for (unsigned int i = 0; i <= DNS::MAX_REQUEST_ID; i++)
{
if (!this->requests[i])
{
- req->id = i;
+ id = i;
break;
}
}
- if (req->id == 0)
+ if (id == -1)
throw Exception("DNS: All ids are in use");
break;
}
}
- while (!req->id || this->requests[req->id]);
+ while (this->requests[id]);
+ req->id = id;
this->requests[req->id] = req;
Packet p;
p.flags = QUERYFLAGS_RD;
p.id = req->id;
- p.questions.push_back(*req);
+ p.question = req->question;
unsigned char buffer[524];
unsigned short len = p.Pack(buffer, sizeof(buffer));
- /* Note that calling Pack() above can actually change the contents of p.questions[0].name, if the query is a PTR,
+ /* Note that calling Pack() above can actually change the contents of p.question.name, if the query is a PTR,
* to contain the value that would be in the DNS cache, which is why this is here.
*/
- if (req->use_cache && this->CheckCache(req, p.questions[0]))
+ if (req->use_cache && this->CheckCache(req, p.question))
{
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Using cached result");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Using cached result");
delete req;
return;
}
+ // Update name in the original request so question checking works for PTR queries
+ req->question.name = p.question.name;
+
if (SocketEngine::SendTo(this, buffer, len, 0, &this->myserver.sa, this->myserver.sa_size()) != len)
throw Exception("DNS: Unable to send query");
+
+ // Add timer for timeout
+ ServerInstance->Timers.AddTimer(req);
}
void RemoveRequest(DNS::Request* req)
{
- this->requests[req->id] = NULL;
+ if (requests[req->id] == req)
+ requests[req->id] = NULL;
}
std::string GetErrorStr(Error e)
@@ -539,6 +526,7 @@ class MyManager : public Manager, public Timer, public EventHandler
case ERROR_NOT_AN_ANSWER:
case ERROR_NONSTANDARD_QUERY:
case ERROR_FORMAT_ERROR:
+ case ERROR_MALFORMED:
return "Malformed answer";
case ERROR_SERVER_FAILURE:
case ERROR_NOT_IMPLEMENTED:
@@ -555,14 +543,13 @@ class MyManager : public Manager, public Timer, public EventHandler
}
}
- void HandleEvent(EventType et, int)
+ void OnEventHandlerError(int errcode) CXX11_OVERRIDE
{
- if (et == EVENT_ERROR)
- {
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: UDP socket got an error event");
- return;
- }
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "UDP socket got an error event");
+ }
+ void OnEventHandlerRead() CXX11_OVERRIDE
+ {
unsigned char buffer[524];
irc::sockets::sockaddrs from;
socklen_t x = sizeof(from);
@@ -572,91 +559,106 @@ class MyManager : public Manager, public Timer, public EventHandler
if (length < Packet::HEADER_LENGTH)
return;
+ if (myserver != from)
+ {
+ std::string server1 = from.str();
+ std::string server2 = myserver.str();
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
+ server1.c_str(), server2.c_str());
+ return;
+ }
+
Packet recv_packet;
+ bool valid = false;
try
{
recv_packet.Fill(buffer, length);
+ valid = true;
}
catch (Exception& ex)
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, ex.GetReason());
- return;
}
- if (myserver != from)
+ // recv_packet.id must be filled in here
+ DNS::Request* request = this->requests[recv_packet.id];
+ if (request == NULL)
{
- std::string server1 = from.str();
- std::string server2 = myserver.str();
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
- server1.c_str(), server2.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received an answer for something we didn't request");
return;
}
- DNS::Request* request = this->requests[recv_packet.id];
- if (request == NULL)
+ if (request->question != recv_packet.question)
{
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Received an answer for something we didn't request");
+ // This can happen under high latency, drop it silently, do not fail the request
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received an answer that isn't for a question we asked");
return;
}
- if (recv_packet.flags & QUERYFLAGS_OPCODE)
+ if (!valid)
+ {
+ ServerInstance->stats.DnsBad++;
+ recv_packet.error = ERROR_MALFORMED;
+ request->OnError(&recv_packet);
+ }
+ else if (recv_packet.flags & QUERYFLAGS_OPCODE)
{
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Received a nonstandard query");
- ServerInstance->stats->statsDnsBad++;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received a nonstandard query");
+ ServerInstance->stats.DnsBad++;
recv_packet.error = ERROR_NONSTANDARD_QUERY;
request->OnError(&recv_packet);
}
- else if (recv_packet.flags & QUERYFLAGS_RCODE)
+ else if (!(recv_packet.flags & QUERYFLAGS_QR) || (recv_packet.flags & QUERYFLAGS_RCODE))
{
Error error = ERROR_UNKNOWN;
switch (recv_packet.flags & QUERYFLAGS_RCODE)
{
case 1:
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: format error");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "format error");
error = ERROR_FORMAT_ERROR;
break;
case 2:
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: server error");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "server error");
error = ERROR_SERVER_FAILURE;
break;
case 3:
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: domain not found");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "domain not found");
error = ERROR_DOMAIN_NOT_FOUND;
break;
case 4:
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: not implemented");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "not implemented");
error = ERROR_NOT_IMPLEMENTED;
break;
case 5:
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: refused");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "refused");
error = ERROR_REFUSED;
break;
default:
break;
}
- ServerInstance->stats->statsDnsBad++;
+ ServerInstance->stats.DnsBad++;
recv_packet.error = error;
request->OnError(&recv_packet);
}
- else if (recv_packet.questions.empty() || recv_packet.answers.empty())
+ else if (recv_packet.answers.empty())
{
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: No resource records returned");
- ServerInstance->stats->statsDnsBad++;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "No resource records returned");
+ ServerInstance->stats.DnsBad++;
recv_packet.error = ERROR_NO_RECORDS;
request->OnError(&recv_packet);
}
else
{
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Lookup complete for " + request->name);
- ServerInstance->stats->statsDnsGood++;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Lookup complete for " + request->question.name);
+ ServerInstance->stats.DnsGood++;
request->OnLookupComplete(&recv_packet);
this->AddCache(recv_packet);
}
- ServerInstance->stats->statsDns++;
+ ServerInstance->stats.Dns++;
/* Request's destructor removes it from the request map */
delete request;
@@ -664,7 +666,7 @@ class MyManager : public Manager, public Timer, public EventHandler
bool Tick(time_t now)
{
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: cache: purging DNS cache");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: purging DNS cache");
for (cache_map::iterator it = this->cache.begin(); it != this->cache.end(); )
{
@@ -677,7 +679,7 @@ class MyManager : public Manager, public Timer, public EventHandler
return true;
}
- void Rehash(const std::string& dnsserver)
+ void Rehash(const std::string& dnsserver, std::string sourceaddr, unsigned int sourceport)
{
if (this->GetFd() > -1)
{
@@ -701,26 +703,36 @@ class MyManager : public Manager, public Timer, public EventHandler
SocketEngine::NonBlocking(s);
irc::sockets::sockaddrs bindto;
- memset(&bindto, 0, sizeof(bindto));
- bindto.sa.sa_family = myserver.sa.sa_family;
+ if (sourceaddr.empty())
+ {
+ // set a sourceaddr for irc::sockets::aptosa() based on the servers af type
+ if (myserver.sa.sa_family == AF_INET)
+ sourceaddr = "0.0.0.0";
+ else if (myserver.sa.sa_family == AF_INET6)
+ sourceaddr = "::";
+ }
+ irc::sockets::aptosa(sourceaddr, sourceport, bindto);
if (SocketEngine::Bind(this->GetFd(), bindto) < 0)
{
/* Failed to bind */
- ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: Error binding dns socket - hostnames will NOT resolve");
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error binding dns socket - hostnames will NOT resolve");
SocketEngine::Close(this->GetFd());
this->SetFd(-1);
}
else if (!SocketEngine::AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
{
- ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: Internal error starting DNS - hostnames will NOT resolve.");
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Internal error starting DNS - hostnames will NOT resolve.");
SocketEngine::Close(this->GetFd());
this->SetFd(-1);
}
+
+ if (bindto.sa.sa_family != myserver.sa.sa_family)
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Nameserver address family differs from source address family - hostnames might not resolve");
}
else
{
- ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: Error creating DNS socket - hostnames will NOT resolve");
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error creating DNS socket - hostnames will NOT resolve");
}
}
};
@@ -729,12 +741,14 @@ class ModuleDNS : public Module
{
MyManager manager;
std::string DNSServer;
+ std::string SourceIP;
+ unsigned int SourcePort;
void FindDNSServer()
{
#ifdef _WIN32
// attempt to look up their nameserver from the system
- ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
PFIXED_INFO pFixedInfo;
DWORD dwBufferSize = sizeof(FIXED_INFO);
@@ -758,15 +772,15 @@ class ModuleDNS : public Module
if (!DNSServer.empty())
{
- ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first active resolver in the system settings.", DNSServer.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "<dns:server> set to '%s' as first active resolver in the system settings.", DNSServer.c_str());
return;
}
}
- ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
#else
// attempt to look up their nameserver from /etc/resolv.conf
- ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
std::ifstream resolv("/etc/resolv.conf");
@@ -775,38 +789,46 @@ class ModuleDNS : public Module
if (DNSServer == "nameserver")
{
resolv >> DNSServer;
- if (DNSServer.find_first_not_of("0123456789.") == std::string::npos)
+ if (DNSServer.find_first_not_of("0123456789.") == std::string::npos || DNSServer.find_first_not_of("0123456789ABCDEFabcdef:") == std::string::npos)
{
- ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",DNSServer.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",DNSServer.c_str());
return;
}
}
}
- ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
#endif
DNSServer = "127.0.0.1";
}
public:
- ModuleDNS() : manager(this)
+ ModuleDNS() : manager(this)
+ , SourcePort(0)
{
}
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
std::string oldserver = DNSServer;
- DNSServer = ServerInstance->Config->ConfValue("dns")->getString("server");
+ const std::string oldip = SourceIP;
+ const unsigned int oldport = SourcePort;
+
+ ConfigTag* tag = ServerInstance->Config->ConfValue("dns");
+ DNSServer = tag->getString("server");
+ SourceIP = tag->getString("sourceip");
+ SourcePort = tag->getInt("sourceport", 0, 0, 65535);
+
if (DNSServer.empty())
FindDNSServer();
- if (oldserver != DNSServer)
- this->manager.Rehash(DNSServer);
+ if (oldserver != DNSServer || oldip != SourceIP || oldport != SourcePort)
+ this->manager.Rehash(DNSServer, SourceIP, SourcePort);
}
void OnUnloadModule(Module* mod)
{
- for (int i = 0; i < MAX_REQUEST_ID; ++i)
+ for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
{
DNS::Request* req = this->manager.requests[i];
if (!req)
@@ -814,7 +836,7 @@ class ModuleDNS : public Module
if (req->creator == mod)
{
- Query rr(*req);
+ Query rr(req->question);
rr.error = ERROR_UNLOADED;
req->OnError(&rr);
diff --git a/src/coremods/core_hostname_lookup.cpp b/src/coremods/core_hostname_lookup.cpp
index 3d3e9703a..b9adc9c2e 100644
--- a/src/coremods/core_hostname_lookup.cpp
+++ b/src/coremods/core_hostname_lookup.cpp
@@ -1,7 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2013-2016 Adam <Adam@anope.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -61,29 +61,34 @@ class UserResolver : public DNS::Request
LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
if (!bound_user)
{
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
return;
}
- const DNS::ResourceRecord& ans_record = r->answers[0];
+ const DNS::ResourceRecord* ans_record = r->FindAnswerOfType(this->question.type);
+ if (ans_record == NULL)
+ {
+ OnError(r);
+ return;
+ }
- ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), ans_record.name.c_str(), ans_record.rdata.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), ans_record->name.c_str(), ans_record->rdata.c_str());
if (!fwd)
{
// first half of resolution is done. We now need to verify that the host matches.
- ph->set(bound_user, ans_record.rdata);
+ ph->set(bound_user, ans_record->rdata);
UserResolver* res_forward;
if (bound_user->client_sa.sa.sa_family == AF_INET6)
{
/* IPV6 forward lookup */
- res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record.rdata, DNS::QUERY_AAAA);
+ res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_AAAA);
}
else
{
/* IPV4 lookup */
- res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record.rdata, DNS::QUERY_A);
+ res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_A);
}
try
{
@@ -107,7 +112,7 @@ class UserResolver : public DNS::Request
if (user_ip->sa.sa_family == AF_INET6)
{
struct in6_addr res_bin;
- if (inet_pton(AF_INET6, ans_record.rdata.c_str(), &res_bin))
+ if (inet_pton(AF_INET6, ans_record->rdata.c_str(), &res_bin))
{
rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
}
@@ -115,7 +120,7 @@ class UserResolver : public DNS::Request
else
{
struct in_addr res_bin;
- if (inet_pton(AF_INET, ans_record.rdata.c_str(), &res_bin))
+ if (inet_pton(AF_INET, ans_record->rdata.c_str(), &res_bin))
{
rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
}
@@ -129,7 +134,7 @@ class UserResolver : public DNS::Request
if (hostname == NULL)
{
- ServerInstance->Logs->Log("RESOLVER", LOG_DEFAULT, "ERROR: User has no hostname attached when doing a forward lookup");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: User has no hostname attached when doing a forward lookup");
bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
return;
}
@@ -170,7 +175,6 @@ class UserResolver : public DNS::Request
{
bound_user->WriteNotice("*** Could not resolve your hostname: " + this->manager->GetErrorStr(query->error) + "; using your IP address (" + bound_user->GetIPString() + ") instead.");
dl->set(bound_user, 0);
- ServerInstance->stats->statsDnsBad++;
}
}
};
@@ -183,8 +187,8 @@ class ModuleHostnameLookup : public Module
public:
ModuleHostnameLookup()
- : dnsLookup("dnsLookup", this)
- , ptrHosts("ptrHosts", this)
+ : dnsLookup("dnsLookup", ExtensionItem::EXT_USER, this)
+ , ptrHosts("ptrHosts", ExtensionItem::EXT_USER, this)
, DNS(this, "DNS")
{
dl = &dnsLookup;
@@ -215,7 +219,6 @@ class ModuleHostnameLookup : public Module
this->dnsLookup.set(user, 0);
delete res_reverse;
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
- ServerInstance->stats->statsDnsBad++;
}
}
diff --git a/src/coremods/core_info/cmd_admin.cpp b/src/coremods/core_info/cmd_admin.cpp
index 722ef8668..f79ebc036 100644
--- a/src/coremods/core_info/cmd_admin.cpp
+++ b/src/coremods/core_info/cmd_admin.cpp
@@ -22,7 +22,7 @@
#include "core_info.h"
CommandAdmin::CommandAdmin(Module* parent)
- : Command(parent, "ADMIN", 0, 0)
+ : ServerTargetCommand(parent, "ADMIN")
{
Penalty = 2;
syntax = "[<servername>]";
@@ -34,21 +34,10 @@ CmdResult CommandAdmin::Handle (const std::vector<std::string>& parameters, User
{
if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
return CMD_SUCCESS;
- user->SendText(":%s %03d %s :Administrative info for %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINME, user->nick.c_str(),ServerInstance->Config->ServerName.c_str());
+ user->WriteRemoteNumeric(RPL_ADMINME, InspIRCd::Format("Administrative info for %s", ServerInstance->Config->ServerName.c_str()));
if (!AdminName.empty())
- user->SendText(":%s %03d %s :Name - %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINLOC1, user->nick.c_str(), AdminName.c_str());
- user->SendText(":%s %03d %s :Nickname - %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINLOC2, user->nick.c_str(), AdminNick.c_str());
- user->SendText(":%s %03d %s :E-Mail - %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINEMAIL, user->nick.c_str(), AdminEmail.c_str());
+ user->WriteRemoteNumeric(RPL_ADMINLOC1, InspIRCd::Format("Name - %s", AdminName.c_str()));
+ user->WriteRemoteNumeric(RPL_ADMINLOC2, InspIRCd::Format("Nickname - %s", AdminNick.c_str()));
+ user->WriteRemoteNumeric(RPL_ADMINEMAIL, InspIRCd::Format("E-Mail - %s", AdminEmail.c_str()));
return CMD_SUCCESS;
}
-
-RouteDescriptor CommandAdmin::GetRouting(User* user, const std::vector<std::string>& parameters)
-{
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
-}
diff --git a/src/coremods/core_info/cmd_commands.cpp b/src/coremods/core_info/cmd_commands.cpp
index 9ae258a9c..b6cc8e34d 100644
--- a/src/coremods/core_info/cmd_commands.cpp
+++ b/src/coremods/core_info/cmd_commands.cpp
@@ -31,22 +31,22 @@ CommandCommands::CommandCommands(Module* parent)
*/
CmdResult CommandCommands::Handle (const std::vector<std::string>&, User *user)
{
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
std::vector<std::string> list;
- list.reserve(ServerInstance->Parser->cmdlist.size());
- for (Commandtable::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++)
+ list.reserve(commands.size());
+ for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
{
// Don't show S2S commands to users
if (i->second->flags_needed == FLAG_SERVERONLY)
continue;
Module* src = i->second->creator;
- list.push_back(InspIRCd::Format(":%s %03d %s :%s %s %d %d", ServerInstance->Config->ServerName.c_str(),
- RPL_COMMANDS, user->nick.c_str(), i->second->name.c_str(), src->ModuleSourceFile.c_str(),
+ list.push_back(InspIRCd::Format("%s %s %d %d", i->second->name.c_str(), src->ModuleSourceFile.c_str(),
i->second->min_params, i->second->Penalty));
}
- sort(list.begin(), list.end());
+ std::sort(list.begin(), list.end());
for(unsigned int i=0; i < list.size(); i++)
- user->Write(list[i]);
- user->WriteNumeric(RPL_COMMANDSEND, ":End of COMMANDS list");
+ user->WriteNumeric(RPL_COMMANDS, list[i]);
+ user->WriteNumeric(RPL_COMMANDSEND, "End of COMMANDS list");
return CMD_SUCCESS;
}
diff --git a/src/coremods/core_info/cmd_info.cpp b/src/coremods/core_info/cmd_info.cpp
index 0d8c1a45a..3bf9db893 100644
--- a/src/coremods/core_info/cmd_info.cpp
+++ b/src/coremods/core_info/cmd_info.cpp
@@ -3,7 +3,7 @@
*
* Copyright (C) 2011 Jackmcbarn <jackmcbarn@jackmcbarn.no-ip.org>
* Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007-2015 Robin Burchell <robin+git@viroteck.net>
* Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
@@ -24,7 +24,7 @@
#include "core_info.h"
CommandInfo::CommandInfo(Module* parent)
- : Command(parent, "INFO")
+ : ServerTargetCommand(parent, "INFO")
{
Penalty = 4;
syntax = "[<servername>]";
@@ -35,9 +35,10 @@ static const char* const lines[] = {
" November 2002 - Present",
" ",
"\2Core Developers\2:",
- " Craig Edwards, Brain, <brain@inspircd.org>",
- " Craig McLure, Craig, <craig@inspircd.org>",
- " Robin Burchell, w00t, <w00t@inspircd.org>",
+ " Attila Molnar, Attila, <attilamolnar@hush.com>",
+ " Peter Powell, SaberUK, <petpow@saberuk.com>",
+ " ",
+ "\2Former Developers\2:",
" Oliver Lupton, Om, <om@inspircd.org>",
" John Brooks, Special, <special@inspircd.org>",
" Dennis Friis, peavey, <peavey@inspircd.org>",
@@ -45,14 +46,14 @@ static const char* const lines[] = {
" Uli Schlachter, psychon, <psychon@inspircd.org>",
" Matt Smith, dz, <dz@inspircd.org>",
" Daniel De Graaf, danieldg, <danieldg@inspircd.org>",
- " jackmcbarn, <jackmcbarn@inspircd.org>",
- " Attila Molnar, Attila, <attilamolnar@hush.com>",
" ",
- "\2Regular Contributors\2:",
- " Adam SaberUK",
+ "\2Founding Developers\2:",
+ " Craig Edwards, Brain, <brain@inspircd.org>",
+ " Craig McLure, Craig, <craig@inspircd.org>",
+ " Robin Burchell, w00t, <w00t@inspircd.org>",
" ",
- "\2Other Contributors\2:",
- " ChrisTX Shawn Shutter",
+ "\2Active Contributors\2:",
+ " Adam Shutter",
" ",
"\2Former Contributors\2:",
" dmb Zaba skenmy GreenReaper",
@@ -64,9 +65,10 @@ static const char* const lines[] = {
" Stskeeps ThaPrince BuildSmart Thunderhacker",
" Skip LeaChim Majic MacGyver",
" Namegduf Ankit Phoenix Taros",
+ " jackmcbarn ChrisTX Shawn",
" ",
"\2Thanks To\2:",
- " searchirc.com irc-junkie.org Brik fraggeln",
+ " Asmo Brik fraggeln",
" ",
" Best experienced with: \2An IRC client\2",
NULL
@@ -81,15 +83,8 @@ CmdResult CommandInfo::Handle (const std::vector<std::string>& parameters, User
int i=0;
while (lines[i])
- user->SendText(":%s %03d %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_INFO, user->nick.c_str(), lines[i++]);
+ user->WriteRemoteNumeric(RPL_INFO, lines[i++]);
FOREACH_MOD(OnInfo, (user));
- user->SendText(":%s %03d %s :End of /INFO list", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFINFO, user->nick.c_str());
+ user->WriteRemoteNumeric(RPL_ENDOFINFO, "End of /INFO list");
return CMD_SUCCESS;
}
-
-RouteDescriptor CommandInfo::GetRouting(User* user, const std::vector<std::string>& parameters)
-{
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
-}
diff --git a/src/coremods/core_info/cmd_modules.cpp b/src/coremods/core_info/cmd_modules.cpp
index cee370870..ef1ee7dbe 100644
--- a/src/coremods/core_info/cmd_modules.cpp
+++ b/src/coremods/core_info/cmd_modules.cpp
@@ -23,7 +23,7 @@
#include "core_info.h"
CommandModules::CommandModules(Module* parent)
- : Command(parent, "MODULES", 0, 0)
+ : ServerTargetCommand(parent, "MODULES")
{
Penalty = 4;
syntax = "[<servername>]";
@@ -58,35 +58,25 @@ CmdResult CommandModules::Handle (const std::vector<std::string>& parameters, Us
if (IS_LOCAL(user) && user->HasPrivPermission("servers/auspex"))
{
- std::string flags("SvcC");
+ std::string flags("vcC");
int pos = 0;
- for (int mult = 1; mult <= VF_OPTCOMMON; mult *= 2, ++pos)
+ for (int mult = 2; mult <= VF_OPTCOMMON; mult *= 2, ++pos)
if (!(V.Flags & mult))
flags[pos] = '-';
-#ifdef PURE_STATIC
- user->SendText(":%s 702 %s :%p %s %s :%s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), (void*)m, m->ModuleSourceFile.c_str(), flags.c_str(), V.description.c_str());
+#ifdef INSPIRCD_STATIC
+ user->WriteRemoteNumeric(702, InspIRCd::Format("%s %s :%s", m->ModuleSourceFile.c_str(), flags.c_str(), V.description.c_str()));
#else
std::string srcrev = m->ModuleDLLManager->GetVersion();
- user->SendText(":%s 702 %s :%p %s %s :%s - %s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), (void*)m, m->ModuleSourceFile.c_str(), flags.c_str(), V.description.c_str(), srcrev.c_str());
+ user->WriteRemoteNumeric(702, InspIRCd::Format("%s %s :%s - %s", m->ModuleSourceFile.c_str(), flags.c_str(), V.description.c_str(), srcrev.c_str()));
#endif
}
else
{
- user->SendText(":%s 702 %s :%s %s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), m->ModuleSourceFile.c_str(), V.description.c_str());
+ user->WriteRemoteNumeric(702, InspIRCd::Format("%s %s", m->ModuleSourceFile.c_str(), V.description.c_str()));
}
}
- user->SendText(":%s 703 %s :End of MODULES list", ServerInstance->Config->ServerName.c_str(), user->nick.c_str());
+ user->WriteRemoteNumeric(703, "End of MODULES list");
return CMD_SUCCESS;
}
-
-RouteDescriptor CommandModules::GetRouting(User* user, const std::vector<std::string>& parameters)
-{
- if (parameters.size() >= 1)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
-}
diff --git a/src/coremods/core_info/cmd_motd.cpp b/src/coremods/core_info/cmd_motd.cpp
index 4481e2d53..cfb083eed 100644
--- a/src/coremods/core_info/cmd_motd.cpp
+++ b/src/coremods/core_info/cmd_motd.cpp
@@ -22,7 +22,7 @@
#include "core_info.h"
CommandMotd::CommandMotd(Module* parent)
- : Command(parent, "MOTD", 0, 1)
+ : ServerTargetCommand(parent, "MOTD")
{
syntax = "[<servername>]";
}
@@ -32,9 +32,15 @@ CommandMotd::CommandMotd(Module* parent)
CmdResult CommandMotd::Handle (const std::vector<std::string>& parameters, User *user)
{
if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
+ {
+ // Give extra penalty if a non-oper queries the /MOTD of a remote server
+ LocalUser* localuser = IS_LOCAL(user);
+ if ((localuser) && (!user->IsOper()))
+ localuser->CommandFloodPenalty += 2000;
return CMD_SUCCESS;
+ }
- ConfigTag* tag = NULL;
+ ConfigTag* tag = ServerInstance->Config->EmptyTag;
LocalUser* localuser = IS_LOCAL(user);
if (localuser)
tag = localuser->GetClass()->config;
@@ -42,25 +48,16 @@ CmdResult CommandMotd::Handle (const std::vector<std::string>& parameters, User
ConfigFileCache::iterator motd = ServerInstance->Config->Files.find(motd_name);
if (motd == ServerInstance->Config->Files.end())
{
- user->SendText(":%s %03d %s :Message of the day file is missing.",
- ServerInstance->Config->ServerName.c_str(), ERR_NOMOTD, user->nick.c_str());
+ user->WriteRemoteNumeric(ERR_NOMOTD, "Message of the day file is missing.");
return CMD_SUCCESS;
}
- user->SendText(":%s %03d %s :%s message of the day", ServerInstance->Config->ServerName.c_str(),
- RPL_MOTDSTART, user->nick.c_str(), ServerInstance->Config->ServerName.c_str());
+ user->WriteRemoteNumeric(RPL_MOTDSTART, InspIRCd::Format("%s message of the day", ServerInstance->Config->ServerName.c_str()));
for (file_cache::iterator i = motd->second.begin(); i != motd->second.end(); i++)
- user->SendText(":%s %03d %s :- %s", ServerInstance->Config->ServerName.c_str(), RPL_MOTD, user->nick.c_str(), i->c_str());
+ user->WriteRemoteNumeric(RPL_MOTD, InspIRCd::Format("- %s", i->c_str()));
- user->SendText(":%s %03d %s :End of message of the day.", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFMOTD, user->nick.c_str());
+ user->WriteRemoteNumeric(RPL_ENDOFMOTD, "End of message of the day.");
return CMD_SUCCESS;
}
-
-RouteDescriptor CommandMotd::GetRouting(User* user, const std::vector<std::string>& parameters)
-{
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
-}
diff --git a/src/coremods/core_info/cmd_time.cpp b/src/coremods/core_info/cmd_time.cpp
index 6a10dc327..6755e5837 100644
--- a/src/coremods/core_info/cmd_time.cpp
+++ b/src/coremods/core_info/cmd_time.cpp
@@ -22,7 +22,7 @@
#include "core_info.h"
CommandTime::CommandTime(Module* parent)
- : Command(parent, "TIME", 0, 0)
+ : ServerTargetCommand(parent, "TIME")
{
syntax = "[<servername>]";
}
@@ -32,19 +32,7 @@ CmdResult CommandTime::Handle (const std::vector<std::string>& parameters, User
if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
return CMD_SUCCESS;
- time_t local = ServerInstance->Time();
- struct tm* timeinfo = localtime(&local);
- const std::string& humanTime = asctime(timeinfo);
-
- user->SendText(":%s %03d %s %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_TIME, user->nick.c_str(),
- ServerInstance->Config->ServerName.c_str(), humanTime.c_str());
+ user->WriteRemoteNumeric(RPL_TIME, ServerInstance->Config->ServerName, InspIRCd::TimeString(ServerInstance->Time()));
return CMD_SUCCESS;
}
-
-RouteDescriptor CommandTime::GetRouting(User* user, const std::vector<std::string>& parameters)
-{
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
-}
diff --git a/src/coremods/core_info/cmd_version.cpp b/src/coremods/core_info/cmd_version.cpp
index eb3ab2c4e..9ec0108b1 100644
--- a/src/coremods/core_info/cmd_version.cpp
+++ b/src/coremods/core_info/cmd_version.cpp
@@ -30,7 +30,7 @@ CommandVersion::CommandVersion(Module* parent)
CmdResult CommandVersion::Handle (const std::vector<std::string>&, User *user)
{
std::string version = ServerInstance->GetVersionString((user->IsOper()));
- user->WriteNumeric(RPL_VERSION, ":%s", version.c_str());
+ user->WriteNumeric(RPL_VERSION, version);
LocalUser *lu = IS_LOCAL(user);
if (lu != NULL)
{
diff --git a/src/coremods/core_info/core_info.cpp b/src/coremods/core_info/core_info.cpp
index 56c3c956d..bd519076d 100644
--- a/src/coremods/core_info/core_info.cpp
+++ b/src/coremods/core_info/core_info.cpp
@@ -20,6 +20,14 @@
#include "inspircd.h"
#include "core_info.h"
+RouteDescriptor ServerTargetCommand::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ // Parameter must be a server name, not a nickname or uuid
+ if ((!parameters.empty()) && (parameters[0].find('.') != std::string::npos))
+ return ROUTE_UNICAST(parameters[0]);
+ return ROUTE_LOCALONLY;
+}
+
class CoreModInfo : public Module
{
CommandAdmin cmdadmin;
diff --git a/src/coremods/core_info/core_info.h b/src/coremods/core_info/core_info.h
index f5dd9e648..ecfeb4f36 100644
--- a/src/coremods/core_info/core_info.h
+++ b/src/coremods/core_info/core_info.h
@@ -21,9 +21,22 @@
#include "inspircd.h"
+/** These commands require no parameters, but if there is a parameter it is a server name where the command will be routed to.
+ */
+class ServerTargetCommand : public Command
+{
+ public:
+ ServerTargetCommand(Module* mod, const std::string& Name)
+ : Command(mod, Name)
+ {
+ }
+
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
/** Handle /ADMIN.
*/
-class CommandAdmin : public Command
+class CommandAdmin : public ServerTargetCommand
{
public:
/** Holds the admin's name, for output in
@@ -51,7 +64,6 @@ class CommandAdmin : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User* user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
/** Handle /COMMANDS.
@@ -73,7 +85,7 @@ class CommandCommands : public Command
/** Handle /INFO.
*/
-class CommandInfo : public Command
+class CommandInfo : public ServerTargetCommand
{
public:
/** Constructor for info.
@@ -86,12 +98,11 @@ class CommandInfo : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User* user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
/** Handle /MODULES.
*/
-class CommandModules : public Command
+class CommandModules : public ServerTargetCommand
{
public:
/** Constructor for modules.
@@ -104,12 +115,11 @@ class CommandModules : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User* user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
/** Handle /MOTD.
*/
-class CommandMotd : public Command
+class CommandMotd : public ServerTargetCommand
{
public:
/** Constructor for motd.
@@ -122,12 +132,11 @@ class CommandMotd : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User* user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
/** Handle /TIME.
*/
-class CommandTime : public Command
+class CommandTime : public ServerTargetCommand
{
public:
/** Constructor for time.
@@ -140,7 +149,6 @@ class CommandTime : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User* user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
/** Handle /VERSION.
diff --git a/src/coremods/core_ison.cpp b/src/coremods/core_ison.cpp
index 53d2e1c49..f1733ba88 100644
--- a/src/coremods/core_ison.cpp
+++ b/src/coremods/core_ison.cpp
@@ -22,12 +22,14 @@
/** Handle /ISON.
*/
-class CommandIson : public Command
+class CommandIson : public SplitCommand
{
public:
/** Constructor for ison.
*/
- CommandIson ( Module* parent) : Command(parent,"ISON", 1) {
+ CommandIson(Module* parent)
+ : SplitCommand(parent, "ISON", 1)
+ {
syntax = "<nick> {nick}";
}
/** Handle command.
@@ -35,66 +37,43 @@ class CommandIson : public Command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
};
-/** Handle /ISON
- */
-CmdResult CommandIson::Handle (const std::vector<std::string>& parameters, User *user)
+class IsonReplyBuilder : public Numeric::Builder<' ', true>
{
- std::map<User*,User*> ison_already;
- User *u;
- std::string reply = "303 " + user->nick + " :";
-
- for (unsigned int i = 0; i < parameters.size(); i++)
+ public:
+ IsonReplyBuilder(LocalUser* user)
+ : Numeric::Builder<' ', true>(user, RPL_ISON)
{
- u = ServerInstance->FindNickOnly(parameters[i]);
- if (ison_already.find(u) != ison_already.end())
- continue;
+ }
- if ((u) && (u->registered == REG_ALL))
- {
- reply.append(u->nick).append(" ");
- if (reply.length() > 450)
- {
- user->WriteServ(reply);
- reply = "303 " + user->nick + " :";
- }
- ison_already[u] = u;
- }
- else
- {
- if ((i == parameters.size() - 1) && (parameters[i].find(' ') != std::string::npos))
- {
- /* Its a space seperated list of nicks (RFC1459 says to support this)
- */
- irc::spacesepstream list(parameters[i]);
- std::string item;
+ void AddNick(const std::string& nickname)
+ {
+ User* const user = ServerInstance->FindNickOnly(nickname);
+ if ((user) && (user->registered == REG_ALL))
+ Add(user->nick);
+ }
+};
- while (list.GetToken(item))
- {
- u = ServerInstance->FindNickOnly(item);
- if (ison_already.find(u) != ison_already.end())
- continue;
+/** Handle /ISON
+ */
+CmdResult CommandIson::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+{
+ IsonReplyBuilder reply(user);
- if ((u) && (u->registered == REG_ALL))
- {
- reply.append(u->nick).append(" ");
- if (reply.length() > 450)
- {
- user->WriteServ(reply);
- reply = "303 " + user->nick + " :";
- }
- ison_already[u] = u;
- }
- }
- }
- }
+ for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end()-1; ++i)
+ {
+ const std::string& targetstr = *i;
+ reply.AddNick(targetstr);
}
- if (!reply.empty())
- user->WriteServ(reply);
+ // Last parameter can be a space separated list
+ irc::spacesepstream ss(parameters.back());
+ for (std::string token; ss.GetToken(token); )
+ reply.AddNick(token);
+ reply.Flush();
return CMD_SUCCESS;
}
diff --git a/src/coremods/core_list.cpp b/src/coremods/core_list.cpp
index 505b0764c..6a62d122f 100644
--- a/src/coremods/core_list.cpp
+++ b/src/coremods/core_list.cpp
@@ -53,10 +53,9 @@ CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User
{
int minusers = 0, maxusers = 0;
- user->WriteNumeric(RPL_LISTSTART, "Channel :Users Name");
+ user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
- /* Work around mIRC suckyness. YOU SUCK, KHALED! */
- if (parameters.size() == 1)
+ if ((parameters.size() == 1) && (!parameters[0].empty()))
{
if (parameters[0][0] == '<')
{
@@ -68,11 +67,16 @@ CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User
}
}
+ const bool has_privs = user->HasPrivPermission("channels/auspex");
+ const bool match_name_topic = ((!parameters.empty()) && (!parameters[0].empty()) && (parameters[0][0] != '<') && (parameters[0][0] != '>'));
+
const chan_hash& chans = ServerInstance->GetChans();
for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
{
+ Channel* const chan = i->second;
+
// attempt to match a glob pattern
- long users = i->second->GetUserCounter();
+ long users = chan->GetUserCounter();
bool too_few = (minusers && (users <= minusers));
bool too_many = (maxusers && (users >= maxusers));
@@ -80,30 +84,31 @@ CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User
if (too_many || too_few)
continue;
- if (parameters.size() && !parameters[0].empty() && (parameters[0][0] != '<' && parameters[0][0] != '>'))
+ if (match_name_topic)
{
- if (!InspIRCd::Match(i->second->name, parameters[0]) && !InspIRCd::Match(i->second->topic, parameters[0]))
+ if (!InspIRCd::Match(chan->name, parameters[0]) && !InspIRCd::Match(chan->topic, parameters[0]))
continue;
}
// if the channel is not private/secret, OR the user is on the channel anyway
- bool n = (i->second->HasUser(user) || user->HasPrivPermission("channels/auspex"));
+ bool n = (has_privs || chan->HasUser(user));
- if (!n && i->second->IsModeSet(privatemode))
- {
- /* Channel is +p and user is outside/not privileged */
- user->WriteNumeric(RPL_LIST, "* %ld :", users);
- }
- else
+ // If we're not in the channel and +s is set on it, we want to ignore it
+ if ((n) || (!chan->IsModeSet(secretmode)))
{
- if (n || !i->second->IsModeSet(secretmode))
+ if ((!n) && (chan->IsModeSet(privatemode)))
+ {
+ // Channel is private (+p) and user is outside/not privileged
+ user->WriteNumeric(RPL_LIST, '*', users, "");
+ }
+ else
{
/* User is in the channel/privileged, channel is not +s */
- user->WriteNumeric(RPL_LIST, "%s %ld :[+%s] %s",i->second->name.c_str(),users,i->second->ChanModes(n),i->second->topic.c_str());
+ user->WriteNumeric(RPL_LIST, chan->name, users, InspIRCd::Format("[+%s] %s", chan->ChanModes(n), chan->topic.c_str()));
}
}
}
- user->WriteNumeric(RPL_LISTEND, ":End of channel list.");
+ user->WriteNumeric(RPL_LISTEND, "End of channel list.");
return CMD_SUCCESS;
}
diff --git a/src/coremods/core_loadmodule.cpp b/src/coremods/core_loadmodule.cpp
index 1d49d89d0..09c044198 100644
--- a/src/coremods/core_loadmodule.cpp
+++ b/src/coremods/core_loadmodule.cpp
@@ -43,12 +43,12 @@ CmdResult CommandLoadmodule::Handle (const std::vector<std::string>& parameters,
if (ServerInstance->Modules->Load(parameters[0]))
{
ServerInstance->SNO->WriteGlobalSno('a', "NEW MODULE: %s loaded %s",user->nick.c_str(), parameters[0].c_str());
- user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module successfully loaded.", parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Module successfully loaded.");
return CMD_SUCCESS;
}
else
{
- user->WriteNumeric(ERR_CANTLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTLOADMODULE, parameters[0], ServerInstance->Modules->LastError());
return CMD_FAILURE;
}
}
@@ -80,26 +80,25 @@ CmdResult CommandUnloadmodule::Handle(const std::vector<std::string>& parameters
if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") &&
InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map))
{
- user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :You cannot unload core commands!", parameters[0].c_str());
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "You cannot unload core commands!");
return CMD_FAILURE;
}
Module* m = ServerInstance->Modules->Find(parameters[0]);
if (m == creator)
{
- user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :You cannot unload module loading commands!", parameters[0].c_str());
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "You cannot unload module loading commands!");
return CMD_FAILURE;
}
if (m && ServerInstance->Modules->Unload(m))
{
ServerInstance->SNO->WriteGlobalSno('a', "MODULE UNLOADED: %s unloaded %s", user->nick.c_str(), parameters[0].c_str());
- user->WriteNumeric(RPL_UNLOADEDMODULE, "%s :Module successfully unloaded.", parameters[0].c_str());
+ user->WriteNumeric(RPL_UNLOADEDMODULE, parameters[0], "Module successfully unloaded.");
}
else
{
- user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :%s", parameters[0].c_str(),
- m ? ServerInstance->Modules->LastError().c_str() : "No such module");
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], (m ? ServerInstance->Modules->LastError() : "No such module"));
return CMD_FAILURE;
}
diff --git a/src/coremods/core_lusers.cpp b/src/coremods/core_lusers.cpp
index 2529ca42b..a0d0d0205 100644
--- a/src/coremods/core_lusers.cpp
+++ b/src/coremods/core_lusers.cpp
@@ -84,19 +84,19 @@ CmdResult CommandLusers::Handle (const std::vector<std::string>&, User *user)
counters.UpdateMaxUsers();
- user->WriteNumeric(RPL_LUSERCLIENT, ":There are %d users and %d invisible on %d servers",
- n_users - counters.invisible, counters.invisible, n_serv);
+ user->WriteNumeric(RPL_LUSERCLIENT, InspIRCd::Format("There are %d users and %d invisible on %d servers",
+ n_users - counters.invisible, counters.invisible, n_serv));
if (ServerInstance->Users->OperCount())
- user->WriteNumeric(RPL_LUSEROP, "%d :operator(s) online", ServerInstance->Users->OperCount());
+ user->WriteNumeric(RPL_LUSEROP, ServerInstance->Users.OperCount(), "operator(s) online");
if (ServerInstance->Users->UnregisteredUserCount())
- user->WriteNumeric(RPL_LUSERUNKNOWN, "%d :unknown connections", ServerInstance->Users->UnregisteredUserCount());
+ user->WriteNumeric(RPL_LUSERUNKNOWN, ServerInstance->Users.UnregisteredUserCount(), "unknown connections");
- user->WriteNumeric(RPL_LUSERCHANNELS, "%lu :channels formed", (unsigned long)ServerInstance->GetChans().size());
- user->WriteNumeric(RPL_LUSERME, ":I have %d clients and %d servers", ServerInstance->Users->LocalUserCount(),n_local_servs);
- user->WriteNumeric(RPL_LOCALUSERS, ":Current local users: %d Max: %d", ServerInstance->Users->LocalUserCount(), counters.max_local);
- user->WriteNumeric(RPL_GLOBALUSERS, ":Current global users: %d Max: %d", n_users, counters.max_global);
+ user->WriteNumeric(RPL_LUSERCHANNELS, ServerInstance->GetChans().size(), "channels formed");
+ user->WriteNumeric(RPL_LUSERME, InspIRCd::Format("I have %d clients and %d servers", ServerInstance->Users.LocalUserCount(), n_local_servs));
+ user->WriteNumeric(RPL_LOCALUSERS, InspIRCd::Format("Current local users: %d Max: %d", ServerInstance->Users.LocalUserCount(), counters.max_local));
+ user->WriteNumeric(RPL_GLOBALUSERS, InspIRCd::Format("Current global users: %d Max: %d", n_users, counters.max_global));
return CMD_SUCCESS;
}
diff --git a/src/coremods/core_oper/cmd_die.cpp b/src/coremods/core_oper/cmd_die.cpp
index 5a9415915..5fe643520 100644
--- a/src/coremods/core_oper/cmd_die.cpp
+++ b/src/coremods/core_oper/cmd_die.cpp
@@ -26,7 +26,34 @@ CommandDie::CommandDie(Module* parent)
: Command(parent, "DIE", 1)
{
flags_needed = 'o';
- syntax = "<password>";
+ syntax = "<server>";
+}
+
+static void QuitAll()
+{
+ const std::string quitmsg = "Server shutdown";
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ while (!list.empty())
+ ServerInstance->Users.QuitUser(list.front(), quitmsg);
+}
+
+void DieRestart::SendError(const std::string& message)
+{
+ const std::string unregline = "ERROR :" + message;
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* user = *i;
+ if (user->registered == REG_ALL)
+ {
+ user->WriteNotice(message);
+ }
+ else
+ {
+ // Unregistered connections receive ERROR, not a NOTICE
+ user->Write(unregline);
+ }
+ }
}
/** Handle /DIE
@@ -37,15 +64,16 @@ CmdResult CommandDie::Handle (const std::vector<std::string>& parameters, User *
{
{
std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating.";
- ServerInstance->Logs->Log("COMMAND", LOG_SPARSE, diebuf);
- ServerInstance->SendError(diebuf);
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, diebuf);
+ DieRestart::SendError(diebuf);
}
+ QuitAll();
ServerInstance->Exit(EXIT_STATUS_DIE);
}
else
{
- ServerInstance->Logs->Log("COMMAND", LOG_SPARSE, "Failed /DIE command from %s", user->GetFullRealHost().c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Failed /DIE command from %s", user->GetFullRealHost().c_str());
ServerInstance->SNO->WriteGlobalSno('a', "Failed DIE Command from %s.", user->GetFullRealHost().c_str());
return CMD_FAILURE;
}
diff --git a/src/coremods/core_oper/cmd_kill.cpp b/src/coremods/core_oper/cmd_kill.cpp
index b60885c64..e75566e2f 100644
--- a/src/coremods/core_oper/cmd_kill.cpp
+++ b/src/coremods/core_oper/cmd_kill.cpp
@@ -44,7 +44,7 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User
}
User *u = ServerInstance->FindNick(parameters[0]);
- if ((u) && (!IS_SERVER(u)))
+ if (u)
{
/*
* Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
@@ -93,7 +93,7 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User
if (!IS_LOCAL(u))
{
// remote kill
- if (!user->server->IsULine())
+ if ((!ServerInstance->Config->HideULineKills) || (!user->server->IsULine()))
ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
this->lastuuid = u->uuid;
}
@@ -104,7 +104,7 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User
* XXX - this isn't entirely correct, servers A - B - C, oper on A, client on C. Oper kills client, A and B will get remote kill
* snotices, C will get a local kill snotice. this isn't accurate, and needs fixing at some stage. -- w00t
*/
- if (!user->server->IsULine())
+ if ((!ServerInstance->Config->HideULineKills) || (!user->server->IsULine()))
{
if (IS_LOCAL(user))
ServerInstance->SNO->WriteGlobalSno('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
@@ -117,7 +117,7 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User
u->Write(":%s KILL %s :%s!%s!%s (%s)", ServerInstance->Config->HideKillsServer.empty() ? user->GetFullHost().c_str() : ServerInstance->Config->HideKillsServer.c_str(),
u->nick.c_str(),
ServerInstance->Config->ServerName.c_str(),
- user->dhost.c_str(),
+ ServerInstance->Config->HideKillsServer.empty() ? user->dhost.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
ServerInstance->Config->HideKillsServer.empty() ? user->nick.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
parameters[1].c_str());
@@ -129,7 +129,7 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
diff --git a/src/coremods/core_oper/cmd_oper.cpp b/src/coremods/core_oper/cmd_oper.cpp
index 8c0d05ce2..9c06583a8 100644
--- a/src/coremods/core_oper/cmd_oper.cpp
+++ b/src/coremods/core_oper/cmd_oper.cpp
@@ -37,7 +37,7 @@ CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, L
const std::string userHost = user->ident + "@" + user->host;
const std::string userIP = user->ident + "@" + user->GetIPString();
- OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
+ ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
if (i != ServerInstance->Config->oper_blocks.end())
{
OperInfo* ifo = i->second;
@@ -63,7 +63,7 @@ CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, L
fields.append("hosts");
// tell them they suck, and lag them up to help prevent brute-force attacks
- user->WriteNumeric(ERR_NOOPERHOST, ":Invalid oper credentials");
+ user->WriteNumeric(ERR_NOOPERHOST, "Invalid oper credentials");
user->CommandFloodPenalty += 10000;
ServerInstance->SNO->WriteGlobalSno('o', "WARNING! Failed oper attempt by %s using login '%s': The following fields do not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
diff --git a/src/coremods/core_oper/cmd_rehash.cpp b/src/coremods/core_oper/cmd_rehash.cpp
index 48dfa6fb1..5ce38eb2c 100644
--- a/src/coremods/core_oper/cmd_rehash.cpp
+++ b/src/coremods/core_oper/cmd_rehash.cpp
@@ -55,7 +55,7 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use
// the leading "-" is optional; remove it if present.
if (param[0] == '-')
- param = param.substr(1);
+ param.erase(param.begin());
FOREACH_MOD(OnModuleRehash, (user, param));
return CMD_SUCCESS;
@@ -68,7 +68,7 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use
ServerInstance->SNO->WriteGlobalSno('a', m);
if (IS_LOCAL(user))
- user->WriteNumeric(RPL_REHASHING, "%s :Rehashing", FileSystem::GetFileName(ServerInstance->ConfigFileName).c_str());
+ user->WriteNumeric(RPL_REHASHING, FileSystem::GetFileName(ServerInstance->ConfigFileName), "Rehashing");
else
ServerInstance->PI->SendUserNotice(user, "*** Rehashing server " + FileSystem::GetFileName(ServerInstance->ConfigFileName));
diff --git a/src/coremods/core_oper/cmd_restart.cpp b/src/coremods/core_oper/cmd_restart.cpp
index 4fad752a2..f76fd098d 100644
--- a/src/coremods/core_oper/cmd_restart.cpp
+++ b/src/coremods/core_oper/cmd_restart.cpp
@@ -25,17 +25,17 @@ CommandRestart::CommandRestart(Module* parent)
: Command(parent, "RESTART", 1, 1)
{
flags_needed = 'o';
- syntax = "<password>";
+ syntax = "<server>";
}
CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, User *user)
{
- ServerInstance->Logs->Log("COMMAND", LOG_DEFAULT, "Restart: %s",user->nick.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Restart: %s", user->nick.c_str());
if (DieRestart::CheckPass(user, parameters[0], "restartpass"))
{
ServerInstance->SNO->WriteGlobalSno('a', "RESTART command from %s, restarting server.", user->GetFullRealHost().c_str());
- ServerInstance->SendError("Server restarting.");
+ DieRestart::SendError("Server restarting.");
#ifndef _WIN32
/* XXX: This hack sets FD_CLOEXEC on all possible file descriptors, so they're closed if the execv() below succeeds.
diff --git a/src/coremods/core_oper/core_oper.cpp b/src/coremods/core_oper/core_oper.cpp
index 0fc82df8f..a6b2abd81 100644
--- a/src/coremods/core_oper/core_oper.cpp
+++ b/src/coremods/core_oper/core_oper.cpp
@@ -27,7 +27,7 @@ namespace DieRestart
ConfigTag* tag = ServerInstance->Config->ConfValue("power");
// The hash method for *BOTH* the die and restart passwords
const std::string hash = tag->getString("hash");
- const std::string correctpass = tag->getString(confentry);
+ const std::string correctpass = tag->getString(confentry, ServerInstance->Config->ServerName);
return ServerInstance->PassCompare(user, correctpass, inputpass, hash);
}
}
diff --git a/src/coremods/core_oper/core_oper.h b/src/coremods/core_oper/core_oper.h
index 3b3dfd4b2..338a369f5 100644
--- a/src/coremods/core_oper/core_oper.h
+++ b/src/coremods/core_oper/core_oper.h
@@ -30,6 +30,11 @@ namespace DieRestart
* @return True if the given password was correct, false if it was not
*/
bool CheckPass(User* user, const std::string& inputpass, const char* confkey);
+
+ /** Send an ERROR to unregistered users and a NOTICE to all registered local users
+ * @param message Message to send
+ */
+ void SendError(const std::string& message);
}
/** Handle /DIE.
diff --git a/src/coremods/core_privmsg.cpp b/src/coremods/core_privmsg.cpp
index 34953bbe8..cccba0850 100644
--- a/src/coremods/core_privmsg.cpp
+++ b/src/coremods/core_privmsg.cpp
@@ -67,8 +67,8 @@ class MessageCommandBase : public Command
void MessageCommandBase::SendAll(User* user, const std::string& msg, MessageType mt)
{
const std::string message = ":" + user->GetFullHost() + " " + MessageTypeString[mt] + " $* :" + msg;
- const LocalUserList& list = ServerInstance->Users->local_users;
- for (LocalUserList::const_iterator i = list.begin(); i != list.end(); ++i)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
if ((*i)->registered == REG_ALL)
(*i)->Write(message);
@@ -130,13 +130,13 @@ CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& para
{
if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(user))
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (no external messages)", chan->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (no external messages)");
return CMD_FAILURE;
}
if (chan->IsModeSet(moderatedmode))
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (+m)", chan->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (+m)");
return CMD_FAILURE;
}
@@ -144,7 +144,7 @@ CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& para
{
if (chan->IsBanned(user))
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (you're banned)", chan->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)");
return CMD_FAILURE;
}
}
@@ -161,7 +161,7 @@ CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& para
/* Check again, a module may have zapped the input string */
if (temp.empty())
{
- user->WriteNumeric(ERR_NOTEXTTOSEND, ":No text to send");
+ user->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
return CMD_FAILURE;
}
@@ -169,14 +169,7 @@ CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& para
if (status)
{
- if (ServerInstance->Config->UndernetMsgPrefix)
- {
- chan->WriteAllExcept(user, false, status, except_list, "%s %c%s :%c %s", MessageTypeString[mt], status, chan->name.c_str(), status, text);
- }
- else
- {
- chan->WriteAllExcept(user, false, status, except_list, "%s %c%s :%s", MessageTypeString[mt], status, chan->name.c_str(), text);
- }
+ chan->WriteAllExcept(user, false, status, except_list, "%s %c%s :%s", MessageTypeString[mt], status, chan->name.c_str(), text);
}
else
{
@@ -188,7 +181,7 @@ CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& para
else
{
/* no such nick/channel */
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", target);
+ user->WriteNumeric(Numerics::NoSuchNick(target));
return CMD_FAILURE;
}
return CMD_SUCCESS;
@@ -209,7 +202,7 @@ CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& para
if (dest && strcasecmp(dest->server->GetName().c_str(), targetserver + 1))
{
/* Incorrect server for user */
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
}
@@ -223,14 +216,14 @@ CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& para
{
if (parameters[1].empty())
{
- user->WriteNumeric(ERR_NOTEXTTOSEND, ":No text to send");
+ user->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
return CMD_FAILURE;
}
if ((dest->IsAway()) && (mt == MSG_PRIVMSG))
{
/* auto respond with aweh msg */
- user->WriteNumeric(RPL_AWAY, "%s :%s", dest->nick.c_str(), dest->awaymsg.c_str());
+ user->WriteNumeric(RPL_AWAY, dest->nick, dest->awaymsg);
}
ModResult MOD_RESULT;
@@ -255,7 +248,7 @@ CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& para
else
{
/* no such nick/channel */
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
return CMD_SUCCESS;
diff --git a/src/coremods/core_reloadmodule.cpp b/src/coremods/core_reloadmodule.cpp
index 1561131dc..68db9e25a 100644
--- a/src/coremods/core_reloadmodule.cpp
+++ b/src/coremods/core_reloadmodule.cpp
@@ -1,6 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
*
@@ -19,13 +20,26 @@
#include "inspircd.h"
+#include "listmode.h"
+#include "modules/reload.h"
+
+static Events::ModuleEventProvider* reloadevprov;
class CommandReloadmodule : public Command
{
+ Events::ModuleEventProvider evprov;
public:
/** Constructor for reloadmodule.
*/
- CommandReloadmodule ( Module* parent) : Command( parent, "RELOADMODULE",1) { flags_needed = 'o'; syntax = "<modulename>"; }
+ CommandReloadmodule(Module* parent)
+ : Command(parent, "RELOADMODULE", 1)
+ , evprov(parent, "event/reloadmodule")
+ {
+ reloadevprov = &evprov;
+ flags_needed = 'o';
+ syntax = "<modulename>";
+ }
+
/** Handle command.
* @param parameters The parameters to the command
* @param user The user issuing the command
@@ -34,21 +48,562 @@ class CommandReloadmodule : public Command
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
};
-class ReloadModuleWorker : public HandlerBase1<void, bool>
+namespace ReloadModule
+{
+
+class DataKeeper
+{
+ /** Data we save for each mode and extension provided by the module
+ */
+ struct ProviderInfo
+ {
+ std::string itemname;
+ union
+ {
+ ModeHandler* mh;
+ ExtensionItem* extitem;
+ };
+
+ ProviderInfo(ModeHandler* mode)
+ : itemname(mode->name)
+ , mh(mode)
+ {
+ }
+
+ ProviderInfo(ExtensionItem* ei)
+ : itemname(ei->name)
+ , extitem(ei)
+ {
+ }
+ };
+
+ struct InstanceData
+ {
+ /** Position of the ModeHandler or ExtensionItem that the serialized data belongs to
+ */
+ size_t index;
+
+ /** Serialized data
+ */
+ std::string serialized;
+
+ InstanceData(size_t Index, const std::string& Serialized)
+ : index(Index)
+ , serialized(Serialized)
+ {
+ }
+ };
+
+ struct ModesExts
+ {
+ /** Mode data for the object, one entry per mode set by the module being reloaded
+ */
+ std::vector<InstanceData> modelist;
+
+ /** Extensions for the object, one entry per extension set by the module being reloaded
+ */
+ std::vector<InstanceData> extlist;
+
+ bool empty() const { return ((modelist.empty()) && (extlist.empty())); }
+
+ void swap(ModesExts& other)
+ {
+ modelist.swap(other.modelist);
+ extlist.swap(other.extlist);
+ }
+ };
+
+ struct OwnedModesExts : public ModesExts
+ {
+ /** User uuid or channel name
+ */
+ std::string owner;
+
+ OwnedModesExts(const std::string& Owner)
+ : owner(Owner)
+ {
+ }
+ };
+
+ // Data saved for each channel
+ struct ChanData : public OwnedModesExts
+ {
+ /** Type of data stored for each member who has any affected modes or extensions set
+ */
+ typedef OwnedModesExts MemberData;
+
+ /** List of data (modes and extensions) about each member
+ */
+ std::vector<MemberData> memberdatalist;
+
+ ChanData(Channel* chan)
+ : OwnedModesExts(chan->name)
+ {
+ }
+ };
+
+ // Data saved for each user
+ typedef OwnedModesExts UserData;
+
+ /** Module being reloaded
+ */
+ Module* mod;
+
+ /** Stores all user and channel modes provided by the module
+ */
+ std::vector<ProviderInfo> handledmodes[2];
+
+ /** Stores all extensions provided by the module
+ */
+ std::vector<ProviderInfo> handledexts;
+
+ /** Stores all of the module data related to users
+ */
+ std::vector<UserData> userdatalist;
+
+ /** Stores all of the module data related to channels and memberships
+ */
+ std::vector<ChanData> chandatalist;
+
+ /** Data attached by modules
+ */
+ ReloadModule::CustomData moddata;
+
+ void SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdatalist);
+ void SaveMemberData(Channel* chan, std::vector<ChanData::MemberData>& memberdatalist);
+ static void SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata);
+
+ void CreateModeList(ModeType modetype);
+ void DoSaveUsers();
+ void DoSaveChans();
+
+ /** Link previously saved extension names to currently available ExtensionItems
+ */
+ void LinkExtensions();
+
+ /** Link previously saved mode names to currently available ModeHandlers
+ * @param modetype Type of the modes to look for
+ */
+ void LinkModes(ModeType modetype);
+
+ void DoRestoreUsers();
+ void DoRestoreChans();
+ void DoRestoreModules();
+
+ /** Restore previously saved modes and extensions on an Extensible.
+ * The extensions are set directly on the extensible, the modes are added into the provided mode change list.
+ * @param data Data to unserialize from
+ * @param extensible Object to restore
+ * @param modetype MODETYPE_USER if the object being restored is a User, MODETYPE_CHANNEL otherwise
+ * (for Channels and Memberships).
+ * @param modechange Mode change to populate with the modes
+ */
+ void RestoreObj(const OwnedModesExts& data, Extensible* extensible, ModeType modetype, Modes::ChangeList& modechange);
+
+ /** Restore all previously saved extensions on an Extensible
+ * @param list List of extensions and their serialized data to restore
+ * @param extensible Target Extensible
+ */
+ void RestoreExtensions(const std::vector<InstanceData>& list, Extensible* extensible);
+
+ /** Restore all previously saved modes on a User, Channel or Membership
+ * @param list List of modes to restore
+ * @param modetype MODETYPE_USER if the object being restored is a User, MODETYPE_CHANNEL otherwise
+ * @param modechange Mode change to populate with the modes
+ */
+ void RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange);
+
+ /** Restore all modes and extensions of all members on a channel
+ * @param chan Channel whose members are being restored
+ * @param memberdata Data to restore
+ * @param modechange Mode change to populate with prefix modes
+ */
+ void RestoreMemberData(Channel* chan, const std::vector<ChanData::MemberData>& memberdatalist, Modes::ChangeList& modechange);
+
+ /** Verify that a service which had its data saved is available and owned by the module that owned it previously
+ * @param service Service descriptor
+ * @param type Human-readable type of the service for log messages
+ */
+ void VerifyServiceProvider(const ProviderInfo& service, const char* type);
+
+ public:
+ /** Save module state
+ * @param currmod Module whose data to save
+ */
+ void Save(Module* currmod);
+
+ /** Restore module state
+ * @param newmod Newly loaded instance of the module which had its data saved
+ */
+ void Restore(Module* newmod);
+
+ /** Handle reload failure
+ */
+ void Fail();
+};
+
+void DataKeeper::DoSaveUsers()
+{
+ ModesExts currdata;
+
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ User* const user = i->second;
+
+ // Serialize user modes
+ for (size_t j = 0; j < handledmodes[MODETYPE_USER].size(); j++)
+ {
+ ModeHandler* mh = handledmodes[MODETYPE_USER][j].mh;
+ if (user->IsModeSet(mh))
+ currdata.modelist.push_back(InstanceData(j, mh->GetUserParameter(user)));
+ }
+
+ // Serialize all extensions attached to the User
+ SaveExtensions(user, currdata.extlist);
+
+ // Add to list if the user has any modes or extensions set that we are interested in, otherwise we don't
+ // have to do anything with this user when restoring
+ if (!currdata.empty())
+ {
+ userdatalist.push_back(UserData(user->uuid));
+ userdatalist.back().swap(currdata);
+ }
+ }
+}
+
+void DataKeeper::SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdata)
+{
+ const Extensible::ExtensibleStore& setexts = extensible->GetExtList();
+
+ // Position of the extension saved in the handledexts list
+ size_t index = 0;
+ for (std::vector<ProviderInfo>::const_iterator i = handledexts.begin(); i != handledexts.end(); ++i, index++)
+ {
+ ExtensionItem* const item = i->extitem;
+ Extensible::ExtensibleStore::const_iterator it = setexts.find(item);
+ if (it == setexts.end())
+ continue;
+
+ std::string value = item->serialize(FORMAT_INTERNAL, extensible, it->second);
+ // If the serialized value is empty the extension won't be saved and restored
+ if (!value.empty())
+ extdata.push_back(InstanceData(index, value));
+ }
+}
+
+void DataKeeper::SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata)
+{
+ const ListModeBase::ModeList* list = lm->GetList(chan);
+ if (!list)
+ return;
+
+ for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+ {
+ const ListModeBase::ListItem& listitem = *i;
+ currdata.modelist.push_back(InstanceData(index, listitem.mask));
+ }
+}
+
+void DataKeeper::DoSaveChans()
+{
+ ModesExts currdata;
+ std::vector<OwnedModesExts> currmemberdata;
+
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+ {
+ Channel* const chan = i->second;
+
+ // Serialize channel modes
+ for (size_t j = 0; j < handledmodes[MODETYPE_CHANNEL].size(); j++)
+ {
+ ModeHandler* mh = handledmodes[MODETYPE_CHANNEL][j].mh;
+ ListModeBase* lm = mh->IsListModeBase();
+ if (lm)
+ SaveListModes(chan, lm, j, currdata);
+ else if (chan->IsModeSet(mh))
+ currdata.modelist.push_back(InstanceData(j, chan->GetModeParameter(mh)));
+ }
+
+ // Serialize all extensions attached to the Channel
+ SaveExtensions(chan, currdata.extlist);
+
+ // Serialize all extensions attached to and all modes set on all members of the channel
+ SaveMemberData(chan, currmemberdata);
+
+ // Same logic as in DoSaveUsers() plus we consider the modes and extensions of all members
+ if ((!currdata.empty()) || (!currmemberdata.empty()))
+ {
+ chandatalist.push_back(ChanData(chan));
+ chandatalist.back().swap(currdata);
+ chandatalist.back().memberdatalist.swap(currmemberdata);
+ }
+ }
+}
+
+void DataKeeper::SaveMemberData(Channel* chan, std::vector<OwnedModesExts>& memberdatalist)
{
+ ModesExts currdata;
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ Membership* const memb = i->second;
+
+ for (size_t j = 0; j < handledmodes[MODETYPE_CHANNEL].size(); j++)
+ {
+ ModeHandler* mh = handledmodes[MODETYPE_CHANNEL][j].mh;
+ const PrefixMode* const pm = mh->IsPrefixMode();
+ if ((pm) && (memb->HasMode(pm)))
+ currdata.modelist.push_back(InstanceData(j, memb->user->uuid)); // Need to pass the user's uuid to the mode parser to set the mode later
+ }
+
+ SaveExtensions(memb, currdata.extlist);
+
+ // Same logic as in DoSaveUsers()
+ if (!currdata.empty())
+ {
+ memberdatalist.push_back(OwnedModesExts(memb->user->uuid));
+ memberdatalist.back().swap(currdata);
+ }
+ }
+}
+
+void DataKeeper::RestoreMemberData(Channel* chan, const std::vector<ChanData::MemberData>& memberdatalist, Modes::ChangeList& modechange)
+{
+ for (std::vector<ChanData::MemberData>::const_iterator i = memberdatalist.begin(); i != memberdatalist.end(); ++i)
+ {
+ const ChanData::MemberData& md = *i;
+ User* const user = ServerInstance->FindUUID(md.owner);
+ if (!user)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone (while processing %s)", md.owner.c_str(), chan->name.c_str());
+ continue;
+ }
+
+ Membership* const memb = chan->GetUser(user);
+ if (!memb)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Member %s is no longer on channel %s", md.owner.c_str(), chan->name.c_str());
+ continue;
+ }
+
+ RestoreObj(md, memb, MODETYPE_CHANNEL, modechange);
+ }
+}
+
+void DataKeeper::CreateModeList(ModeType modetype)
+{
+ const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes->GetModes(modetype);
+ for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); ++i)
+ {
+ ModeHandler* mh = i->second;
+ if (mh->creator == mod)
+ handledmodes[modetype].push_back(ProviderInfo(mh));
+ }
+}
+
+void DataKeeper::Save(Module* currmod)
+{
+ this->mod = currmod;
+
+ const ExtensionManager::ExtMap& allexts = ServerInstance->Extensions.GetExts();
+ for (ExtensionManager::ExtMap::const_iterator i = allexts.begin(); i != allexts.end(); ++i)
+ {
+ ExtensionItem* ext = i->second;
+ if (ext->creator == mod)
+ handledexts.push_back(ProviderInfo(ext));
+ }
+
+ CreateModeList(MODETYPE_USER);
+ DoSaveUsers();
+
+ CreateModeList(MODETYPE_CHANNEL);
+ DoSaveChans();
+
+ FOREACH_MOD_CUSTOM(*reloadevprov, ReloadModule::EventListener, OnReloadModuleSave, (mod, this->moddata));
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Saved data about %lu users %lu chans %lu modules", (unsigned long)userdatalist.size(), (unsigned long)chandatalist.size(), (unsigned long)moddata.list.size());
+}
+
+void DataKeeper::VerifyServiceProvider(const ProviderInfo& service, const char* type)
+{
+ const ServiceProvider* sp = service.extitem;
+ if (!sp)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s \"%s\" is no longer available", type, service.itemname.c_str());
+ else if (sp->creator != mod)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s \"%s\" is now handled by %s", type, service.itemname.c_str(), (sp->creator ? sp->creator->ModuleSourceFile.c_str() : "<core>"));
+}
+
+void DataKeeper::LinkModes(ModeType modetype)
+{
+ std::vector<ProviderInfo>& list = handledmodes[modetype];
+ for (std::vector<ProviderInfo>::iterator i = list.begin(); i != list.end(); ++i)
+ {
+ ProviderInfo& item = *i;
+ item.mh = ServerInstance->Modes->FindMode(item.itemname, modetype);
+ VerifyServiceProvider(item, (modetype == MODETYPE_USER ? "User mode" : "Channel mode"));
+ }
+}
+
+void DataKeeper::LinkExtensions()
+{
+ for (std::vector<ProviderInfo>::iterator i = handledexts.begin(); i != handledexts.end(); ++i)
+ {
+ ProviderInfo& item = *i;
+ item.extitem = ServerInstance->Extensions.GetItem(item.itemname);
+ VerifyServiceProvider(item.extitem, "Extension");
+ }
+}
+
+void DataKeeper::Restore(Module* newmod)
+{
+ this->mod = newmod;
+
+ // Find the new extension items
+ LinkExtensions();
+ LinkModes(MODETYPE_USER);
+ LinkModes(MODETYPE_CHANNEL);
+
+ // Restore
+ DoRestoreUsers();
+ DoRestoreChans();
+ DoRestoreModules();
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restore finished");
+}
+
+void DataKeeper::Fail()
+{
+ this->mod = NULL;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restore failed, notifying modules");
+ DoRestoreModules();
+}
+
+void DataKeeper::RestoreObj(const OwnedModesExts& data, Extensible* extensible, ModeType modetype, Modes::ChangeList& modechange)
+{
+ RestoreExtensions(data.extlist, extensible);
+ RestoreModes(data.modelist, modetype, modechange);
+}
+
+void DataKeeper::RestoreExtensions(const std::vector<InstanceData>& list, Extensible* extensible)
+{
+ for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ const InstanceData& id = *i;
+ handledexts[id.index].extitem->unserialize(FORMAT_INTERNAL, extensible, id.serialized);
+ }
+}
+
+void DataKeeper::RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange)
+{
+ for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ const InstanceData& id = *i;
+ modechange.push_add(handledmodes[modetype][id.index].mh, id.serialized);
+ }
+}
+
+void DataKeeper::DoRestoreUsers()
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring user data");
+ Modes::ChangeList modechange;
+
+ for (std::vector<UserData>::const_iterator i = userdatalist.begin(); i != userdatalist.end(); ++i)
+ {
+ const UserData& userdata = *i;
+ User* const user = ServerInstance->FindUUID(userdata.owner);
+ if (!user)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone", userdata.owner.c_str());
+ continue;
+ }
+
+ RestoreObj(userdata, user, MODETYPE_USER, modechange);
+ ServerInstance->Modes.Process(ServerInstance->FakeClient, NULL, user, modechange, ModeParser::MODE_LOCALONLY);
+ modechange.clear();
+ }
+}
+
+void DataKeeper::DoRestoreChans()
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring channel data");
+ Modes::ChangeList modechange;
+
+ for (std::vector<ChanData>::const_iterator i = chandatalist.begin(); i != chandatalist.end(); ++i)
+ {
+ const ChanData& chandata = *i;
+ Channel* const chan = ServerInstance->FindChan(chandata.owner);
+ if (!chan)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Channel %s not found", chandata.owner.c_str());
+ continue;
+ }
+
+ RestoreObj(chandata, chan, MODETYPE_CHANNEL, modechange);
+ // Process the mode change before applying any prefix modes
+ ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
+ modechange.clear();
+
+ // Restore all member data
+ RestoreMemberData(chan, chandata.memberdatalist, modechange);
+ ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
+ modechange.clear();
+ }
+}
+
+void DataKeeper::DoRestoreModules()
+{
+ for (ReloadModule::CustomData::List::iterator i = moddata.list.begin(); i != moddata.list.end(); ++i)
+ {
+ ReloadModule::CustomData::Data& data = *i;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Calling module data handler %p", (void*)data.handler);
+ data.handler->OnReloadModuleRestore(mod, data.data);
+ }
+}
+
+} // namespace ReloadModule
+
+class ReloadAction : public HandlerBase0<void>
+{
+ Module* const mod;
+ const std::string uuid;
+ const std::string passedname;
+
public:
- const std::string name;
- const std::string uid;
- ReloadModuleWorker(const std::string& uuid, const std::string& modn)
- : name(modn), uid(uuid) {}
- void Call(bool result)
- {
- ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded",
- name.c_str(), result ? "" : "un");
- User* user = ServerInstance->FindNick(uid);
+ ReloadAction(Module* m, const std::string& uid, const std::string& passedmodname)
+ : mod(m)
+ , uuid(uid)
+ , passedname(passedmodname)
+ {
+ }
+
+ void Call()
+ {
+ ReloadModule::DataKeeper datakeeper;
+ datakeeper.Save(mod);
+
+ DLLManager* dll = mod->ModuleDLLManager;
+ std::string name = mod->ModuleSourceFile;
+ ServerInstance->Modules->DoSafeUnload(mod);
+ ServerInstance->GlobalCulls.Apply();
+ delete dll;
+ bool result = ServerInstance->Modules->Load(name);
+
+ if (result)
+ {
+ Module* newmod = ServerInstance->Modules->Find(name);
+ datakeeper.Restore(newmod);
+ }
+ else
+ datakeeper.Fail();
+
+ ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded", passedname.c_str(), result ? "" : "un");
+ User* user = ServerInstance->FindUUID(uuid);
if (user)
- user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %ssuccessfully reloaded.",
- name.c_str(), result ? "" : "un");
+ user->WriteNumeric(RPL_LOADEDMODULE, passedname, InspIRCd::Format("Module %ssuccessfully reloaded.", (result ? "" : "un")));
+
ServerInstance->GlobalCulls.AddItem(this);
}
};
@@ -58,19 +613,21 @@ CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameter
Module* m = ServerInstance->Modules->Find(parameters[0]);
if (m == creator)
{
- user->WriteNumeric(RPL_LOADEDMODULE, "%s :You cannot reload core_reloadmodule.so (unload and load it)",
- parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "You cannot reload core_reloadmodule.so (unload and load it)");
return CMD_FAILURE;
}
- if (m)
+ if (creator->dying)
+ return CMD_FAILURE;
+
+ if ((m) && (ServerInstance->Modules.CanUnload(m)))
{
- ServerInstance->Modules->Reload(m, new ReloadModuleWorker(user->uuid, parameters[0]));
+ ServerInstance->AtomicActions.AddAction(new ReloadAction(m, user->uuid, parameters[0]));
return CMD_SUCCESS;
}
else
{
- user->WriteNumeric(RPL_LOADEDMODULE, "%s :Could not find module by that name", parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Could not find module by that name");
return CMD_FAILURE;
}
}
diff --git a/src/coremods/core_stats.cpp b/src/coremods/core_stats.cpp
index e0e5b3a0f..b91653908 100644
--- a/src/coremods/core_stats.cpp
+++ b/src/coremods/core_stats.cpp
@@ -31,11 +31,11 @@
*/
class CommandStats : public Command
{
- void DoStats(char statschar, User* user, string_list &results);
+ void DoStats(Stats::Context& stats);
public:
/** Constructor for stats.
*/
- CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { syntax = "<stats-symbol> [<servername>]"; }
+ CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { allow_empty_last_param = false; syntax = "<stats-symbol> [<servername>]"; }
/** Handle command.
* @param parameters The parameters to the command
* @param user The user issuing the command
@@ -44,14 +44,29 @@ class CommandStats : public Command
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- if (parameters.size() > 1)
+ if ((parameters.size() > 1) && (parameters[1].find('.') != std::string::npos))
return ROUTE_UNICAST(parameters[1]);
return ROUTE_LOCALONLY;
}
};
-void CommandStats::DoStats(char statschar, User* user, string_list &results)
+static void GenerateStatsLl(Stats::Context& stats)
{
+ stats.AddRow(211, InspIRCd::Format("nick[ident@%s] sendq cmds_out bytes_out cmds_in bytes_in time_open", (stats.GetSymbol() == 'l' ? "host" : "ip")));
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* u = *i;
+ stats.AddRow(211, u->nick+"["+u->ident+"@"+(stats.GetSymbol() == 'l' ? u->dhost : u->GetIPString())+"] "+ConvToStr(u->eh.getSendQSize())+" "+ConvToStr(u->cmds_out)+" "+ConvToStr(u->bytes_out)+" "+ConvToStr(u->cmds_in)+" "+ConvToStr(u->bytes_in)+" "+ConvToStr(ServerInstance->Time() - u->signon));
+ }
+}
+
+void CommandStats::DoStats(Stats::Context& stats)
+{
+ User* const user = stats.GetSource();
+ const char statschar = stats.GetSymbol();
+
bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos;
bool isRemoteOper = IS_REMOTE(user) && (user->IsOper());
bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
@@ -62,15 +77,15 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
"%s '%c' denied for %s (%s@%s)",
(IS_LOCAL(user) ? "Stats" : "Remote stats"),
statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
- results.push_back("481 " + user->nick + " :Permission Denied - STATS " + statschar + " requires the servers/auspex priv.");
+ stats.AddRow(481, (std::string("Permission Denied - STATS ") + statschar + " requires the servers/auspex priv."));
return;
}
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnStats, MOD_RESULT, (statschar, user, results));
+ FIRST_MOD_RESULT(OnStats, MOD_RESULT, (stats));
if (MOD_RESULT == MOD_RES_DENY)
{
- results.push_back("219 "+user->nick+" "+statschar+" :End of /STATS report");
+ stats.AddRow(219, statschar, "End of /STATS report");
ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
(IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
return;
@@ -87,11 +102,15 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
std::string ip = ls->bind_addr;
if (ip.empty())
ip.assign("*");
+ else if (ip.find_first_of(':') != std::string::npos)
+ {
+ ip.insert(ip.begin(), '[');
+ ip.insert(ip.end(), ']');
+ }
std::string type = ls->bind_tag->getString("type", "clients");
std::string hook = ls->bind_tag->getString("ssl", "plaintext");
- results.push_back("249 "+user->nick+" :"+ ip + ":"+ConvToStr(ls->bind_port)+
- " (" + type + ", " + hook + ")");
+ stats.AddRow(249, ip + ":"+ConvToStr(ls->bind_port) + " (" + type + ", " + hook + ")");
}
}
break;
@@ -103,28 +122,32 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
case 'i':
{
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
{
ConnectClass* c = *i;
- std::stringstream res;
- res << "215 " << user->nick << " I " << c->name << ' ';
+ Stats::Row row(215);
+ row.push("I").push(c->name);
+
+ std::string param;
if (c->type == CC_ALLOW)
- res << '+';
+ param.push_back('+');
if (c->type == CC_DENY)
- res << '-';
+ param.push_back('-');
if (c->type == CC_NAMED)
- res << '*';
+ param.push_back('*');
else
- res << c->host;
+ param.append(c->host);
- res << ' ' << c->config->getString("port", "*") << ' ';
+ row.push(param).push(c->config->getString("port", "*"));
+ row.push(ConvToStr(c->GetRecvqMax())).push(ConvToStr(c->GetSendqSoftMax())).push(ConvToStr(c->GetSendqHardMax())).push(ConvToStr(c->GetCommandRate()));
- res << c->GetRecvqMax() << ' ' << c->GetSendqSoftMax() << ' ' << c->GetSendqHardMax()
- << ' ' << c->GetCommandRate() << ' ' << c->GetPenaltyThreshold();
+ param = ConvToStr(c->GetPenaltyThreshold());
if (c->fakelag)
- res << '*';
- results.push_back(res.str());
+ param.push_back('*');
+ row.push(param);
+
+ stats.AddRow(row);
}
}
break;
@@ -132,12 +155,11 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
case 'Y':
{
int idx = 0;
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
{
ConnectClass* c = *i;
- results.push_back("215 "+user->nick+" i NOMATCH * "+c->GetHost()+" "+ConvToStr(c->limit ? c->limit : SocketEngine::GetMaxFds())+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *");
- results.push_back("218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqHardMax())+" :"+
- ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
+ stats.AddRow(215, 'i', "NOMATCH", '*', c->GetHost(), (c->limit ? c->limit : SocketEngine::GetMaxFds()), idx, ServerInstance->Config->ServerName, '*');
+ stats.AddRow(218, 'Y', idx, c->GetPingTime(), '0', c->GetSendqHardMax(), ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
idx++;
}
}
@@ -153,71 +175,68 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
if (!oper->server->IsULine())
{
LocalUser* lu = IS_LOCAL(oper);
- results.push_back("249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
+ stats.AddRow(249, oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
(lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
idx++;
}
}
- results.push_back("249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)");
+ stats.AddRow(249, ConvToStr(idx)+" OPER(s)");
}
break;
case 'k':
- ServerInstance->XLines->InvokeStats("K",216,user,results);
+ ServerInstance->XLines->InvokeStats("K",216,stats);
break;
case 'g':
- ServerInstance->XLines->InvokeStats("G",223,user,results);
+ ServerInstance->XLines->InvokeStats("G",223,stats);
break;
case 'q':
- ServerInstance->XLines->InvokeStats("Q",217,user,results);
+ ServerInstance->XLines->InvokeStats("Q",217,stats);
break;
case 'Z':
- ServerInstance->XLines->InvokeStats("Z",223,user,results);
+ ServerInstance->XLines->InvokeStats("Z",223,stats);
break;
case 'e':
- ServerInstance->XLines->InvokeStats("E",223,user,results);
+ ServerInstance->XLines->InvokeStats("E",223,stats);
break;
case 'E':
{
- const SocketEngine::Statistics& stats = SocketEngine::GetStats();
- results.push_back("249 "+user->nick+" :Total events: "+ConvToStr(stats.TotalEvents));
- results.push_back("249 "+user->nick+" :Read events: "+ConvToStr(stats.ReadEvents));
- results.push_back("249 "+user->nick+" :Write events: "+ConvToStr(stats.WriteEvents));
- results.push_back("249 "+user->nick+" :Error events: "+ConvToStr(stats.ErrorEvents));
+ const SocketEngine::Statistics& sestats = SocketEngine::GetStats();
+ stats.AddRow(249, "Total events: "+ConvToStr(sestats.TotalEvents));
+ stats.AddRow(249, "Read events: "+ConvToStr(sestats.ReadEvents));
+ stats.AddRow(249, "Write events: "+ConvToStr(sestats.WriteEvents));
+ stats.AddRow(249, "Error events: "+ConvToStr(sestats.ErrorEvents));
break;
}
/* stats m (list number of times each command has been used, plus bytecount) */
case 'm':
- for (Commandtable::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++)
+ {
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
{
if (i->second->use_count)
{
/* RPL_STATSCOMMANDS */
- results.push_back("212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count));
+ stats.AddRow(212, i->second->name, i->second->use_count);
}
}
+ }
break;
/* stats z (debug and memory info) */
case 'z':
{
- results.push_back("249 "+user->nick+" :Users: "+ConvToStr(ServerInstance->Users->GetUsers().size()));
- results.push_back("249 "+user->nick+" :Channels: "+ConvToStr(ServerInstance->GetChans().size()));
- results.push_back("249 "+user->nick+" :Commands: "+ConvToStr(ServerInstance->Parser->cmdlist.size()));
+ stats.AddRow(249, "Users: "+ConvToStr(ServerInstance->Users->GetUsers().size()));
+ stats.AddRow(249, "Channels: "+ConvToStr(ServerInstance->GetChans().size()));
+ stats.AddRow(249, "Commands: "+ConvToStr(ServerInstance->Parser.GetCommands().size()));
float kbitpersec_in, kbitpersec_out, kbitpersec_total;
- char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
-
SocketEngine::GetStats().GetBandwidth(kbitpersec_in, kbitpersec_out, kbitpersec_total);
- snprintf(kbitpersec_total_s, 30, "%03.5f", kbitpersec_total);
- snprintf(kbitpersec_out_s, 30, "%03.5f", kbitpersec_out);
- snprintf(kbitpersec_in_s, 30, "%03.5f", kbitpersec_in);
-
- results.push_back("249 "+user->nick+" :Bandwidth total: "+ConvToStr(kbitpersec_total_s)+" kilobits/sec");
- results.push_back("249 "+user->nick+" :Bandwidth out: "+ConvToStr(kbitpersec_out_s)+" kilobits/sec");
- results.push_back("249 "+user->nick+" :Bandwidth in: "+ConvToStr(kbitpersec_in_s)+" kilobits/sec");
+ stats.AddRow(249, InspIRCd::Format("Bandwidth total: %03.5f kilobits/sec", kbitpersec_total));
+ stats.AddRow(249, InspIRCd::Format("Bandwidth out: %03.5f kilobits/sec", kbitpersec_out));
+ stats.AddRow(249, InspIRCd::Format("Bandwidth in: %03.5f kilobits/sec", kbitpersec_in));
#ifndef _WIN32
/* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
@@ -228,35 +247,32 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
/* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */
if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */
{
- results.push_back("249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
- results.push_back("249 "+user->nick+" :Signals: "+ConvToStr(R.ru_nsignals));
- results.push_back("249 "+user->nick+" :Page faults: "+ConvToStr(R.ru_majflt));
- results.push_back("249 "+user->nick+" :Swaps: "+ConvToStr(R.ru_nswap));
- results.push_back("249 "+user->nick+" :Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
-
- char percent[30];
-
- float n_elapsed = (ServerInstance->Time() - ServerInstance->stats->LastSampled.tv_sec) * 1000000
- + (ServerInstance->Time_ns() - ServerInstance->stats->LastSampled.tv_nsec) / 1000;
- float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats->LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats->LastCPU.tv_usec);
+ stats.AddRow(249, "Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
+ stats.AddRow(249, "Signals: "+ConvToStr(R.ru_nsignals));
+ stats.AddRow(249, "Page faults: "+ConvToStr(R.ru_majflt));
+ stats.AddRow(249, "Swaps: "+ConvToStr(R.ru_nswap));
+ stats.AddRow(249, "Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
+
+ float n_elapsed = (ServerInstance->Time() - ServerInstance->stats.LastSampled.tv_sec) * 1000000
+ + (ServerInstance->Time_ns() - ServerInstance->stats.LastSampled.tv_nsec) / 1000;
+ float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats.LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats.LastCPU.tv_usec);
float per = (n_eaten / n_elapsed) * 100;
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back("249 "+user->nick+" :CPU Use (now): "+percent);
+ stats.AddRow(249, InspIRCd::Format("CPU Use (now): %03.5f%%", per));
n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
n_eaten = (float)R.ru_utime.tv_sec + R.ru_utime.tv_usec / 100000.0;
per = (n_eaten / n_elapsed) * 100;
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back("249 "+user->nick+" :CPU Use (total): "+percent);
+
+ stats.AddRow(249, InspIRCd::Format("CPU Use (total): %03.5f%%", per));
}
#else
PROCESS_MEMORY_COUNTERS MemCounters;
if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
{
- results.push_back("249 "+user->nick+" :Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
- results.push_back("249 "+user->nick+" :Pagefile usage: "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
- results.push_back("249 "+user->nick+" :Page faults: "+ConvToStr(MemCounters.PageFaultCount));
+ stats.AddRow(249, "Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
+ stats.AddRow(249, "Pagefile usage: "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
+ stats.AddRow(249, "Page faults: "+ConvToStr(MemCounters.PageFaultCount));
}
FILETIME CreationTime;
@@ -269,20 +285,17 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
{
KernelTime.dwHighDateTime += UserTime.dwHighDateTime;
KernelTime.dwLowDateTime += UserTime.dwLowDateTime;
- double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats->LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats->LastCPU.dwLowDateTime) )/100000;
- double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats->LastSampled.QuadPart) / ServerInstance->stats->QPFrequency.QuadPart;
+ double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats.LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats.LastCPU.dwLowDateTime) )/100000;
+ double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats.LastSampled.QuadPart) / ServerInstance->stats.QPFrequency.QuadPart;
double per = (n_eaten/n_elapsed);
- char percent[30];
-
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back("249 "+user->nick+" :CPU Use (now): "+percent);
+ stats.AddRow(249, InspIRCd::Format("CPU Use (now): %03.5f%%", per));
n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
n_eaten = (double)(( (uint64_t)(KernelTime.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime))/100000;
per = (n_eaten / n_elapsed);
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back("249 "+user->nick+" :CPU Use (total): "+percent);
+
+ stats.AddRow(249, InspIRCd::Format("CPU Use (total): %03.5f%%", per));
}
#endif
}
@@ -290,13 +303,13 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
case 'T':
{
- results.push_back("249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats->statsAccept)+" refused "+ConvToStr(ServerInstance->stats->statsRefused));
- results.push_back("249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats->statsUnknown));
- results.push_back("249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats->statsCollisions));
- results.push_back("249 "+user->nick+" :dns requests "+ConvToStr(ServerInstance->stats->statsDnsGood+ServerInstance->stats->statsDnsBad)+" succeeded "+ConvToStr(ServerInstance->stats->statsDnsGood)+" failed "+ConvToStr(ServerInstance->stats->statsDnsBad));
- results.push_back("249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats->statsConnects));
- results.push_back(InspIRCd::Format("249 %s :bytes sent %5.2fK recv %5.2fK", user->nick.c_str(),
- ServerInstance->stats->statsSent / 1024.0, ServerInstance->stats->statsRecv / 1024.0));
+ stats.AddRow(249, "accepts "+ConvToStr(ServerInstance->stats.Accept)+" refused "+ConvToStr(ServerInstance->stats.Refused));
+ stats.AddRow(249, "unknown commands "+ConvToStr(ServerInstance->stats.Unknown));
+ stats.AddRow(249, "nick collisions "+ConvToStr(ServerInstance->stats.Collisions));
+ stats.AddRow(249, "dns requests "+ConvToStr(ServerInstance->stats.DnsGood+ServerInstance->stats.DnsBad)+" succeeded "+ConvToStr(ServerInstance->stats.DnsGood)+" failed "+ConvToStr(ServerInstance->stats.DnsBad));
+ stats.AddRow(249, "connection count "+ConvToStr(ServerInstance->stats.Connects));
+ stats.AddRow(249, InspIRCd::Format("bytes sent %5.2fK recv %5.2fK",
+ ServerInstance->stats.Sent / 1024.0, ServerInstance->stats.Recv / 1024.0));
}
break;
@@ -307,20 +320,19 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
for(ConfigIter i = tags.first; i != tags.second; ++i)
{
ConfigTag* tag = i->second;
- results.push_back("243 "+user->nick+" O "+tag->getString("host")+" * "+
- tag->getString("name") + " " + tag->getString("type")+" 0");
+ stats.AddRow(243, 'O', tag->getString("host"), '*', tag->getString("name"), tag->getString("type"), '0');
}
}
break;
case 'O':
{
- for (OperIndex::const_iterator i = ServerInstance->Config->OperTypes.begin(); i != ServerInstance->Config->OperTypes.end(); ++i)
+ for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->OperTypes.begin(); i != ServerInstance->Config->OperTypes.end(); ++i)
{
OperInfo* tag = i->second;
tag->init();
std::string umodes;
std::string cmodes;
- for(char c='A'; c < 'z'; c++)
+ for(char c='A'; c <= 'z'; c++)
{
ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A'])
@@ -329,53 +341,24 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
cmodes.push_back(c);
}
- results.push_back("243 "+user->nick+" O "+tag->name.c_str() + " " + umodes + " " + cmodes);
+ stats.AddRow(243, 'O', tag->name, umodes, cmodes);
}
}
break;
/* stats l (show user I/O stats) */
case 'l':
- results.push_back("211 "+user->nick+" :nick[ident@host] sendq cmds_out bytes_out cmds_in bytes_in time_open");
- for (LocalUserList::iterator n = ServerInstance->Users->local_users.begin(); n != ServerInstance->Users->local_users.end(); n++)
- {
- LocalUser* i = *n;
- results.push_back("211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->dhost+"] "+ConvToStr(i->eh.getSendQSize())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age));
- }
- break;
-
/* stats L (show user I/O stats with IP addresses) */
case 'L':
- results.push_back("211 "+user->nick+" :nick[ident@ip] sendq cmds_out bytes_out cmds_in bytes_in time_open");
- for (LocalUserList::iterator n = ServerInstance->Users->local_users.begin(); n != ServerInstance->Users->local_users.end(); n++)
- {
- LocalUser* i = *n;
- results.push_back("211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->GetIPString()+"] "+ConvToStr(i->eh.getSendQSize())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age));
- }
+ GenerateStatsLl(stats);
break;
/* stats u (show server uptime) */
case 'u':
{
- time_t current_time = 0;
- current_time = ServerInstance->Time();
- time_t server_uptime = current_time - ServerInstance->startup_time;
- struct tm* stime;
- stime = gmtime(&server_uptime);
- /* i dont know who the hell would have an ircd running for over a year nonstop, but
- * Craig suggested this, and it seemed a good idea so in it went */
- if (stime->tm_year > 70)
- {
- results.push_back(InspIRCd::Format("242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d",
- user->nick.c_str(), stime->tm_year - 70, stime->tm_yday, stime->tm_hour,
- stime->tm_min, stime->tm_sec));
- }
- else
- {
- results.push_back(InspIRCd::Format("242 %s :Server up %d days, %.2d:%.2d:%.2d",
- user->nick.c_str(), stime->tm_yday, stime->tm_hour, stime->tm_min,
- stime->tm_sec));
- }
+ unsigned int up = static_cast<unsigned int>(ServerInstance->Time() - ServerInstance->startup_time);
+ stats.AddRow(242, InspIRCd::Format("Server up %u days, %.2u:%.2u:%.2u",
+ up / 86400, (up / 3600) % 24, (up / 60) % 60, up % 60));
}
break;
@@ -383,7 +366,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
break;
}
- results.push_back("219 "+user->nick+" "+statschar+" :End of /STATS report");
+ stats.AddRow(219, statschar, "End of /STATS report");
ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
(IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
return;
@@ -392,14 +375,21 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
CmdResult CommandStats::Handle (const std::vector<std::string>& parameters, User *user)
{
if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
+ {
+ // Give extra penalty if a non-oper does /STATS <remoteserver>
+ LocalUser* localuser = IS_LOCAL(user);
+ if ((localuser) && (!user->IsOper()))
+ localuser->CommandFloodPenalty += 2000;
return CMD_SUCCESS;
- string_list values;
- char search = parameters[0][0];
- DoStats(search, user, values);
-
- const std::string p = ":" + ServerInstance->Config->ServerName + " ";
- for (size_t i = 0; i < values.size(); i++)
- user->SendText(p + values[i]);
+ }
+ Stats::Context stats(user, parameters[0][0]);
+ DoStats(stats);
+ const std::vector<Stats::Row>& rows = stats.GetRows();
+ for (std::vector<Stats::Row>::const_iterator i = rows.begin(); i != rows.end(); ++i)
+ {
+ const Stats::Row& row = *i;
+ user->WriteRemoteNumeric(row);
+ }
return CMD_SUCCESS;
}
diff --git a/src/coremods/core_stub.cpp b/src/coremods/core_stub.cpp
index 30c7ce752..91fc16241 100644
--- a/src/coremods/core_stub.cpp
+++ b/src/coremods/core_stub.cpp
@@ -33,7 +33,7 @@ class CommandConnect : public Command
: Command(parent, "CONNECT", 1)
{
flags_needed = 'o';
- syntax = "<servername> [<remote-server>]";
+ syntax = "<servername>";
}
/** Handle command.
@@ -46,7 +46,7 @@ class CommandConnect : public Command
/*
* This is handled by the server linking module, if necessary. Do not remove this stub.
*/
- user->WriteServ( "NOTICE %s :Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.", user->nick.c_str());
+ user->WriteNotice("Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.");
return CMD_SUCCESS;
}
};
@@ -70,8 +70,8 @@ class CommandLinks : public Command
*/
CmdResult Handle(const std::vector<std::string>& parameters, User* user)
{
- user->WriteNumeric(RPL_LINKS, "%s %s :0 %s", ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerDesc.c_str());
- user->WriteNumeric(RPL_ENDOFLINKS, "* :End of /LINKS list.");
+ user->WriteNumeric(RPL_LINKS, ServerInstance->Config->ServerName, ServerInstance->Config->ServerName, InspIRCd::Format("0 %s", ServerInstance->Config->ServerDesc.c_str()));
+ user->WriteNumeric(RPL_ENDOFLINKS, '*', "End of /LINKS list.");
return CMD_SUCCESS;
}
};
@@ -98,11 +98,11 @@ class CommandServer : public Command
{
if (user->registered == REG_ALL)
{
- user->WriteNumeric(ERR_ALREADYREGISTERED, ":You are already registered. (Perhaps your IRC client does not have a /SERVER command).");
+ user->WriteNumeric(ERR_ALREADYREGISTERED, "You are already registered. (Perhaps your IRC client does not have a /SERVER command).");
}
else
{
- user->WriteNumeric(ERR_NOTREGISTERED, ":You may not register as a server (servers have separate ports from clients, change your config)");
+ user->WriteNumeric(ERR_NOTREGISTERED, "SERVER", "You may not register as a server (servers have separate ports from clients, change your config)");
}
return CMD_FAILURE;
}
@@ -119,7 +119,7 @@ class CommandSquit : public Command
: Command(parent, "SQUIT", 1, 2)
{
flags_needed = 'o';
- syntax = "<servername> [<reason>]";
+ syntax = "<servername>";
}
/** Handle command.
@@ -129,7 +129,7 @@ class CommandSquit : public Command
*/
CmdResult Handle(const std::vector<std::string>& parameters, User* user)
{
- user->WriteServ("NOTICE %s :Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.", user->nick.c_str());
+ user->WriteNotice("Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.");
return CMD_FAILURE;
}
};
diff --git a/src/coremods/core_user/cmd_away.cpp b/src/coremods/core_user/cmd_away.cpp
index adc6e6c18..fb720a5a7 100644
--- a/src/coremods/core_user/cmd_away.cpp
+++ b/src/coremods/core_user/cmd_away.cpp
@@ -43,7 +43,7 @@ CmdResult CommandAway::Handle (const std::vector<std::string>& parameters, User
user->awaytime = ServerInstance->Time();
user->awaymsg.assign(parameters[0], 0, ServerInstance->Config->Limits.MaxAway);
- user->WriteNumeric(RPL_NOWAWAY, ":You have been marked as being away");
+ user->WriteNumeric(RPL_NOWAWAY, "You have been marked as being away");
}
else
{
@@ -53,7 +53,7 @@ CmdResult CommandAway::Handle (const std::vector<std::string>& parameters, User
return CMD_FAILURE;
user->awaymsg.clear();
- user->WriteNumeric(RPL_UNAWAY, ":You are no longer marked as being away");
+ user->WriteNumeric(RPL_UNAWAY, "You are no longer marked as being away");
}
return CMD_SUCCESS;
diff --git a/src/coremods/core_user/cmd_mode.cpp b/src/coremods/core_user/cmd_mode.cpp
new file mode 100644
index 000000000..2b2652606
--- /dev/null
+++ b/src/coremods/core_user/cmd_mode.cpp
@@ -0,0 +1,174 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandMode::CommandMode(Module* parent)
+ : Command(parent, "MODE", 1)
+ , seq(0)
+{
+ syntax = "<target> <modes> {<mode-parameters>}";
+ memset(&sent, 0, sizeof(sent));
+}
+
+CmdResult CommandMode::Handle(const std::vector<std::string>& parameters, User* user)
+{
+ const std::string& target = parameters[0];
+ Channel* targetchannel = ServerInstance->FindChan(target);
+ User* targetuser = NULL;
+ if (!targetchannel)
+ {
+ if (IS_LOCAL(user))
+ targetuser = ServerInstance->FindNickOnly(target);
+ else
+ targetuser = ServerInstance->FindNick(target);
+ }
+
+ if ((!targetchannel) && (!targetuser))
+ {
+ user->WriteNumeric(Numerics::NoSuchNick(target));
+ return CMD_FAILURE;
+ }
+ if (parameters.size() == 1)
+ {
+ this->DisplayCurrentModes(user, targetuser, targetchannel);
+ return CMD_SUCCESS;
+ }
+
+ // Populate a temporary Modes::ChangeList with the parameters
+ Modes::ChangeList changelist;
+ ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
+ ServerInstance->Modes.ModeParamsToChangeList(user, type, parameters, changelist);
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, changelist));
+
+ ModeParser::ModeProcessFlag flags = ModeParser::MODE_NONE;
+ if (IS_LOCAL(user))
+ {
+ if (MOD_RESULT == MOD_RES_PASSTHRU)
+ {
+ if ((targetuser) && (user != targetuser))
+ {
+ // Local users may only change the modes of other users if a module explicitly allows it
+ user->WriteNumeric(ERR_USERSDONTMATCH, "Can't change mode for other users");
+ return CMD_FAILURE;
+ }
+
+ // This is a mode change by a local user and modules didn't explicitly allow/deny.
+ // Ensure access checks will happen for each mode being changed.
+ flags |= ModeParser::MODE_CHECKACCESS;
+ }
+ else if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE; // Entire mode change denied by a module
+ }
+ else
+ flags |= ModeParser::MODE_LOCALONLY;
+
+ if (IS_LOCAL(user))
+ ServerInstance->Modes->ProcessSingle(user, targetchannel, targetuser, changelist, flags);
+ else
+ ServerInstance->Modes->Process(user, targetchannel, targetuser, changelist, flags);
+
+ if ((ServerInstance->Modes.GetLastParse().empty()) && (targetchannel) && (parameters.size() == 2))
+ {
+ /* Special case for displaying the list for listmodes,
+ * e.g. MODE #chan b, or MODE #chan +b without a parameter
+ */
+ this->DisplayListModes(user, targetchannel, parameters[1]);
+ }
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandMode::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
+
+void CommandMode::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence)
+{
+ seq++;
+
+ for (std::string::const_iterator i = mode_sequence.begin(); i != mode_sequence.end(); ++i)
+ {
+ unsigned char mletter = *i;
+ if (mletter == '+')
+ continue;
+
+ ModeHandler* mh = ServerInstance->Modes->FindMode(mletter, MODETYPE_CHANNEL);
+ if (!mh || !mh->IsListMode())
+ return;
+
+ /* Ensure the user doesnt request the same mode twice,
+ * so they can't flood themselves off out of idiocy.
+ */
+ if (sent[mletter] == seq)
+ continue;
+
+ sent[mletter] = seq;
+ ServerInstance->Modes.ShowListModeList(user, chan, mh);
+ }
+}
+
+static std::string GetSnomasks(const User* user)
+{
+ ModeHandler* const snomask = ServerInstance->Modes.FindMode('s', MODETYPE_USER);
+ std::string snomaskstr = snomask->GetUserParameter(user);
+ // snomaskstr is empty if the snomask mode isn't set, otherwise it begins with a '+'.
+ // In the former case output a "+", not an empty string.
+ if (snomaskstr.empty())
+ snomaskstr.push_back('+');
+ return snomaskstr;
+}
+
+void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel)
+{
+ if (targetchannel)
+ {
+ // Display channel's current mode string
+ user->WriteNumeric(RPL_CHANNELMODEIS, targetchannel->name, (std::string("+") + targetchannel->ChanModes(targetchannel->HasUser(user))));
+ user->WriteNumeric(RPL_CHANNELCREATED, targetchannel->name, (unsigned long)targetchannel->age);
+ }
+ else
+ {
+ if (targetuser == user)
+ {
+ // Display user's current mode string
+ user->WriteNumeric(RPL_UMODEIS, targetuser->GetModeLetters());
+ if (targetuser->IsOper())
+ user->WriteNumeric(RPL_SNOMASKIS, GetSnomasks(targetuser), "Server notice mask");
+ }
+ else if (user->HasPrivPermission("users/auspex"))
+ {
+ // Querying the modes of another user.
+ // We cannot use RPL_UMODEIS because that's only for showing the user's own modes.
+ user->WriteNumeric(RPL_OTHERUMODEIS, targetuser->nick, targetuser->GetModeLetters());
+ if (targetuser->IsOper())
+ user->WriteNumeric(RPL_OTHERSNOMASKIS, targetuser->nick, GetSnomasks(targetuser), "Server notice mask");
+ }
+ else
+ {
+ user->WriteNumeric(ERR_USERSDONTMATCH, "Can't view modes for other users");
+ }
+ }
+}
diff --git a/src/coremods/core_user/cmd_nick.cpp b/src/coremods/core_user/cmd_nick.cpp
index 166941c6d..cedafbbf4 100644
--- a/src/coremods/core_user/cmd_nick.cpp
+++ b/src/coremods/core_user/cmd_nick.cpp
@@ -24,7 +24,7 @@
#include "core_user.h"
CommandNick::CommandNick(Module* parent)
- : Command(parent, "NICK", 1, 1)
+ : SplitCommand(parent, "NICK", 1, 1)
{
works_before_reg = true;
syntax = "<newnick>";
@@ -36,18 +36,18 @@ CommandNick::CommandNick(Module* parent)
* for the client introduction code in here, youre in the wrong place.
* You need to look in the spanningtree module for this!
*/
-CmdResult CommandNick::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandNick::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
{
std::string oldnick = user->nick;
std::string newnick = parameters[0];
// anything except the initial NICK gets a flood penalty
- if (user->registered == REG_ALL && IS_LOCAL(user))
- IS_LOCAL(user)->CommandFloodPenalty += 4000;
+ if (user->registered == REG_ALL)
+ user->CommandFloodPenalty += 4000;
if (newnick.empty())
{
- user->WriteNumeric(ERR_ERRONEUSNICKNAME, "* :Erroneous Nickname");
+ user->WriteNumeric(ERR_NONICKNAMEGIVEN, "No nickname given");
return CMD_FAILURE;
}
@@ -57,28 +57,40 @@ CmdResult CommandNick::Handle (const std::vector<std::string>& parameters, User
}
else if (!ServerInstance->IsNick(newnick))
{
- user->WriteNumeric(ERR_ERRONEUSNICKNAME, "%s :Erroneous Nickname", newnick.c_str());
+ user->WriteNumeric(ERR_ERRONEUSNICKNAME, newnick, "Erroneous Nickname");
return CMD_FAILURE;
}
- if (!user->ChangeNick(newnick, false))
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (user, newnick));
+
+ // If a module denied the change, abort now
+ if (MOD_RESULT == MOD_RES_DENY)
return CMD_FAILURE;
- if (user->registered < REG_NICKUSER)
+ // Disallow the nick change if <security:restrictbannedusers> is on and there is a ban matching this user in
+ // one of the channels they are on
+ if (ServerInstance->Config->RestrictBannedUsers)
{
- user->registered = (user->registered | REG_NICK);
- if (user->registered == REG_NICKUSER)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); ++i)
{
- /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (IS_LOCAL(user)));
- if (MOD_RESULT == MOD_RES_DENY)
+ Channel* chan = (*i)->chan;
+ if (chan->GetPrefixValue(user) < VOICE_VALUE && chan->IsBanned(user))
+ {
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)");
return CMD_FAILURE;
-
- // return early to not penalize new users
- return CMD_SUCCESS;
+ }
}
}
+ if (!user->ChangeNick(newnick))
+ return CMD_FAILURE;
+
+ if (user->registered < REG_NICKUSER)
+ {
+ user->registered = (user->registered | REG_NICK);
+ return CommandUser::CheckRegister(user);
+ }
+
return CMD_SUCCESS;
}
diff --git a/src/coremods/core_user/cmd_part.cpp b/src/coremods/core_user/cmd_part.cpp
index 9f82c15a5..4da2787d9 100644
--- a/src/coremods/core_user/cmd_part.cpp
+++ b/src/coremods/core_user/cmd_part.cpp
@@ -44,13 +44,15 @@ CmdResult CommandPart::Handle (const std::vector<std::string>& parameters, User
Channel* c = ServerInstance->FindChan(parameters[0]);
- if (c)
+ if (!c)
{
- c->PartUser(user, reason);
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+ return CMD_FAILURE;
}
- else
+
+ if (!c->PartUser(user, reason))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(ERR_NOTONCHANNEL, c->name, "You're not on that channel");
return CMD_FAILURE;
}
diff --git a/src/coremods/core_user/cmd_user.cpp b/src/coremods/core_user/cmd_user.cpp
index 6de762e44..37ac116df 100644
--- a/src/coremods/core_user/cmd_user.cpp
+++ b/src/coremods/core_user/cmd_user.cpp
@@ -40,7 +40,7 @@ CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, L
* RFC says we must use this numeric, so we do. Let's make it a little more nub friendly though. :)
* -- Craig, and then w00t.
*/
- user->WriteNumeric(ERR_NEEDMOREPARAMS, "USER :Your username is not valid");
+ user->WriteNumeric(ERR_NEEDMOREPARAMS, name, "Your username is not valid");
return CMD_FAILURE;
}
else
@@ -57,20 +57,25 @@ CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, L
}
else
{
- user->WriteNumeric(ERR_ALREADYREGISTERED, ":You may not reregister");
+ user->WriteNumeric(ERR_ALREADYREGISTERED, "You may not reregister");
+ user->CommandFloodPenalty += 1000;
return CMD_FAILURE;
}
/* parameters 2 and 3 are local and remote hosts, and are ignored */
+ return CheckRegister(user);
+}
+
+CmdResult CommandUser::CheckRegister(LocalUser* user)
+{
+ // If the user is registered, return CMD_SUCCESS/CMD_FAILURE depending on what modules say, otherwise just
+ // return CMD_SUCCESS without doing anything, knowing the other handler will call us again
if (user->registered == REG_NICKUSER)
{
ModResult MOD_RESULT;
-
- /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (user));
if (MOD_RESULT == MOD_RES_DENY)
return CMD_FAILURE;
-
}
return CMD_SUCCESS;
diff --git a/src/coremods/core_user/core_user.cpp b/src/coremods/core_user/core_user.cpp
index 103880a6e..a2ffc009e 100644
--- a/src/coremods/core_user/core_user.cpp
+++ b/src/coremods/core_user/core_user.cpp
@@ -20,34 +20,6 @@
#include "inspircd.h"
#include "core_user.h"
-class CommandMode : public Command
-{
- public:
- /** Constructor for mode.
- */
- CommandMode(Module* parent)
- : Command(parent, "MODE", 1)
- {
- syntax = "<target> <modes> {<mode-parameters>}";
- }
-
- /** Handle command.
- * @param parameters The parameters to the command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User* user)
- {
- ServerInstance->Modes->Process(parameters, user, (IS_LOCAL(user) ? ModeParser::MODE_NONE : ModeParser::MODE_LOCALONLY));
- return CMD_SUCCESS;
- }
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
- }
-};
-
/** Handle /PASS.
*/
class CommandPass : public SplitCommand
@@ -73,7 +45,8 @@ class CommandPass : public SplitCommand
// Check to make sure they haven't registered -- Fix by FCS
if (user->registered == REG_ALL)
{
- user->WriteNumeric(ERR_ALREADYREGISTERED, ":You may not reregister");
+ user->CommandFloodPenalty += 1000;
+ user->WriteNumeric(ERR_ALREADYREGISTERED, "You may not reregister");
return CMD_FAILURE;
}
user->password = parameters[0];
@@ -92,7 +65,6 @@ class CommandPing : public Command
CommandPing(Module* parent)
: Command(parent, "PING", 1, 2)
{
- Penalty = 0;
syntax = "<servername> [:<servername>]";
}
@@ -130,8 +102,15 @@ class CommandPong : public Command
CmdResult Handle(const std::vector<std::string>& parameters, User* user)
{
// set the user as alive so they survive to next ping
- if (IS_LOCAL(user))
- IS_LOCAL(user)->lastping = 1;
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ {
+ // Increase penalty unless we've sent a PING and this is the reply
+ if (localuser->lastping)
+ localuser->CommandFloodPenalty += 1000;
+ else
+ localuser->lastping = 1;
+ }
return CMD_SUCCESS;
}
};
diff --git a/src/coremods/core_user/core_user.h b/src/coremods/core_user/core_user.h
index 9c63e6592..0418588c1 100644
--- a/src/coremods/core_user/core_user.h
+++ b/src/coremods/core_user/core_user.h
@@ -62,9 +62,43 @@ class CommandAway : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
+class CommandMode : public Command
+{
+ unsigned int sent[256];
+ unsigned int seq;
+
+ /** Show the list of one or more list modes to a user.
+ * @param user User to send to.
+ * @param chan Channel whose lists to show.
+ * @param mode_sequence Mode letters to show the lists of.
+ */
+ void DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence);
+
+ /** Show the current modes of a channel or a user to a user.
+ * @param user User to show the modes to.
+ * @param targetuser User whose modes to show. NULL if showing the modes of a channel.
+ * @param targetchannel Channel whose modes to show. NULL if showing the modes of a user.
+ */
+ void DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel);
+
+ public:
+ /** Constructor for mode.
+ */
+ CommandMode(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
/** Handle /NICK.
*/
-class CommandNick : public Command
+class CommandNick : public SplitCommand
{
public:
/** Constructor for nick.
@@ -76,7 +110,7 @@ class CommandNick : public Command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
};
/** Handle /PART.
@@ -135,4 +169,13 @@ class CommandUser : public SplitCommand
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
+
+ /** Run the OnUserRegister hook if the user has sent both NICK and USER. Called after an unregistered user
+ * successfully executes the USER or the NICK command.
+ * @param user User to inspect and possibly pass to the OnUserRegister hook
+ * @return CMD_FAILURE if OnUserRegister was called and it returned MOD_RES_DENY, CMD_SUCCESS in every other case
+ * (i.e. if the hook wasn't fired because the user still needs to send NICK/USER or if it was fired and finished with
+ * a non-MOD_RES_DENY result).
+ */
+ static CmdResult CheckRegister(LocalUser* user);
};
diff --git a/src/coremods/core_userhost.cpp b/src/coremods/core_userhost.cpp
index 541402c10..3100995a8 100644
--- a/src/coremods/core_userhost.cpp
+++ b/src/coremods/core_userhost.cpp
@@ -24,11 +24,16 @@
*/
class CommandUserhost : public Command
{
+ UserModeReference hideopermode;
+
public:
/** Constructor for userhost.
*/
- CommandUserhost ( Module* parent) : Command(parent,"USERHOST", 1, 5) {
- syntax = "<nick> {<nick>}";
+ CommandUserhost(Module* parent)
+ : Command(parent,"USERHOST", 1)
+ , hideopermode(parent, "hideoper")
+ {
+ syntax = "<nick> [<nick> ...]";
}
/** Handle command.
* @param parameters The parameters to the command
@@ -40,42 +45,39 @@ class CommandUserhost : public Command
CmdResult CommandUserhost::Handle (const std::vector<std::string>& parameters, User *user)
{
- std::string retbuf = "302 " + user->nick + " :";
+ const bool has_privs = user->HasPrivPermission("users/auspex");
+
+ std::string retbuf;
- for (unsigned int i = 0; i < parameters.size(); i++)
+ unsigned int max = parameters.size();
+ if (max > 5)
+ max = 5;
+
+ for (unsigned int i = 0; i < max; i++)
{
User *u = ServerInstance->FindNickOnly(parameters[i]);
if ((u) && (u->registered == REG_ALL))
{
- retbuf = retbuf + u->nick;
+ retbuf += u->nick;
if (u->IsOper())
- retbuf = retbuf + "*";
-
- retbuf = retbuf + "=";
-
- if (u->IsAway())
- retbuf += "-";
- else
- retbuf += "+";
-
- retbuf = retbuf + u->ident + "@";
-
- if (user->HasPrivPermission("users/auspex"))
- {
- retbuf = retbuf + u->host;
- }
- else
{
- retbuf = retbuf + u->dhost;
+ // XXX: +H hidden opers must not be shown as opers
+ if ((u == user) || (has_privs) || (!u->IsModeSet(hideopermode)))
+ retbuf += '*';
}
- retbuf = retbuf + " ";
+ retbuf += '=';
+ retbuf += (u->IsAway() ? '-' : '+');
+ retbuf += u->ident;
+ retbuf += '@';
+ retbuf += (((u == user) || (has_privs)) ? u->host : u->dhost);
+ retbuf += ' ';
}
}
- user->WriteServ(retbuf);
+ user->WriteNumeric(RPL_USERHOST, retbuf);
return CMD_SUCCESS;
}
diff --git a/src/coremods/core_wallops.cpp b/src/coremods/core_wallops.cpp
index 5dfb79b67..0210df8ee 100644
--- a/src/coremods/core_wallops.cpp
+++ b/src/coremods/core_wallops.cpp
@@ -55,7 +55,8 @@ CmdResult CommandWallops::Handle (const std::vector<std::string>& parameters, Us
std::string wallop("WALLOPS :");
wallop.append(parameters[0]);
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
User* t = *i;
if (t->IsModeSet(wallopsmode))
diff --git a/src/coremods/core_who.cpp b/src/coremods/core_who.cpp
index 6f4bc088e..677a1eb6d 100644
--- a/src/coremods/core_who.cpp
+++ b/src/coremods/core_who.cpp
@@ -38,14 +38,18 @@ class CommandWho : public Command
bool opt_time;
ChanModeReference secretmode;
ChanModeReference privatemode;
+ UserModeReference hidechansmode;
UserModeReference invisiblemode;
- Membership* get_first_visible_channel(User* u)
+ Membership* get_first_visible_channel(User* source, User* u)
{
- for (UCListIter i = u->chans.begin(); i != u->chans.end(); ++i)
+ for (User::ChanList::iterator i = u->chans.begin(); i != u->chans.end(); ++i)
{
Membership* memb = *i;
- if (!memb->chan->IsModeSet(secretmode))
+
+ /* XXX move the +I check into m_hidechans */
+ bool has_modes = memb->chan->IsModeSet(secretmode) || memb->chan->IsModeSet(privatemode) || u->IsModeSet(hidechansmode);
+ if (source == u || !has_modes || memb->chan->HasUser(source))
return memb;
}
return NULL;
@@ -58,12 +62,13 @@ class CommandWho : public Command
: Command(parent, "WHO", 1)
, secretmode(parent, "secret")
, privatemode(parent, "private")
+ , hidechansmode(parent, "hidechans")
, invisiblemode(parent, "invisible")
{
syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [ohurmMiaplf]";
}
- void SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string& initial, Membership* memb, User* u, std::vector<std::string>& whoresults);
+ void SendWhoLine(User* user, const std::vector<std::string>& parms, Membership* memb, User* u, std::vector<Numeric::Numeric>& whoresults);
/** Handle command.
* @param parameters The parameters to the command
* @param user The user issuing the command
@@ -144,7 +149,7 @@ bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext)
long seconds = InspIRCd::Duration(matchtext);
// Okay, so time matching, we want all users connected `seconds' ago
- if (user->age >= ServerInstance->Time() - seconds)
+ if (user->signon >= ServerInstance->Time() - seconds)
match = true;
}
@@ -171,9 +176,6 @@ bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext)
bool CommandWho::CanView(Channel* chan, User* user)
{
- if (!user || !chan)
- return false;
-
/* Bug #383 - moved higher up the list, because if we are in the channel
* we can see all its users
*/
@@ -189,48 +191,52 @@ bool CommandWho::CanView(Channel* chan, User* user)
return false;
}
-void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string& initial, Membership* memb, User* u, std::vector<std::string>& whoresults)
+void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, Membership* memb, User* u, std::vector<Numeric::Numeric>& whoresults)
{
if (!memb)
- memb = get_first_visible_channel(u);
+ memb = get_first_visible_channel(user, u);
- std::string wholine = initial + (memb ? memb->chan->name : "*") + " " + u->ident + " " +
- (opt_showrealhost ? u->host : u->dhost) + " ";
+ Numeric::Numeric wholine(RPL_WHOREPLY);
+ wholine.push(memb ? memb->chan->name : "*").push(u->ident);
+ wholine.push(opt_showrealhost ? u->host : u->dhost);
if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
- wholine.append(ServerInstance->Config->HideWhoisServer);
+ wholine.push(ServerInstance->Config->HideWhoisServer);
else
- wholine.append(u->server->GetName());
+ wholine.push(u->server->GetName());
- wholine.append(" " + u->nick + " ");
+ wholine.push(u->nick);
+ std::string param;
/* away? */
if (u->IsAway())
{
- wholine.append("G");
+ param.push_back('G');
}
else
{
- wholine.append("H");
+ param.push_back('H');
}
/* oper? */
if (u->IsOper())
{
- wholine.push_back('*');
+ param.push_back('*');
}
if (memb)
{
char prefix = memb->GetPrefixChar();
if (prefix)
- wholine.push_back(prefix);
+ param.push_back(prefix);
}
- wholine.append(" :0 " + u->fullname);
+ wholine.push(param);
+ wholine.push("0 ");
+ wholine.GetParams().back().append(u->fullname);
- FOREACH_MOD(OnSendWhoLine, (user, parms, u, memb, wholine));
-
- if (!wholine.empty())
+ ModResult res;
+ FIRST_MOD_RESULT(OnSendWhoLine, res, (user, parms, u, memb, wholine));
+ if (res != MOD_RES_DENY)
whoresults.push_back(wholine);
}
@@ -256,8 +262,7 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *
opt_far = false;
opt_time = false;
- std::vector<std::string> whoresults;
- std::string initial = "352 " + user->nick + " ";
+ std::vector<Numeric::Numeric> whoresults;
/* Change '0' into '*' so the wildcard matcher can grok it */
std::string matchtext = ((parameters[0] == "0") ? "*" : parameters[0]);
@@ -325,9 +330,8 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *
bool inside = ch->HasUser(user);
/* who on a channel. */
- const UserMembList *cu = ch->GetUsers();
-
- for (UserMembCIter i = cu->begin(); i != cu->end(); i++)
+ const Channel::MemberMap& cu = ch->GetUsers();
+ for (Channel::MemberMap::const_iterator i = cu.begin(); i != cu.end(); ++i)
{
/* None of this applies if we WHO ourselves */
if (user != i->first)
@@ -341,7 +345,7 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *
continue;
}
- SendWhoLine(user, parameters, initial, i->second, i->first, whoresults);
+ SendWhoLine(user, parameters, i->second, i->first, whoresults);
}
}
}
@@ -364,7 +368,7 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *
continue;
}
- SendWhoLine(user, parameters, initial, NULL, oper, whoresults);
+ SendWhoLine(user, parameters, NULL, oper, whoresults);
}
}
}
@@ -381,15 +385,15 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *
continue;
}
- SendWhoLine(user, parameters, initial, NULL, i->second, whoresults);
+ SendWhoLine(user, parameters, NULL, i->second, whoresults);
}
}
}
}
/* Send the results out */
- for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
- user->WriteServ(*n);
- user->WriteNumeric(RPL_ENDOFWHO, "%s :End of /WHO list.", *parameters[0].c_str() ? parameters[0].c_str() : "*");
+ for (std::vector<Numeric::Numeric>::const_iterator n = whoresults.begin(); n != whoresults.end(); ++n)
+ user->WriteNumeric(*n);
+ user->WriteNumeric(RPL_ENDOFWHO, (*parameters[0].c_str() ? parameters[0] : "*"), "End of /WHO list.");
// Penalize the user a bit for large queries
// (add one unit of penalty per 200 results)
diff --git a/src/coremods/core_whois.cpp b/src/coremods/core_whois.cpp
index 934dd2102..81e62c8e5 100644
--- a/src/coremods/core_whois.cpp
+++ b/src/coremods/core_whois.cpp
@@ -21,6 +21,30 @@
#include "inspircd.h"
+class WhoisContextImpl : public Whois::Context
+{
+ Events::ModuleEventProvider& lineevprov;
+
+ public:
+ WhoisContextImpl(LocalUser* src, User* targ, Events::ModuleEventProvider& evprov)
+ : Whois::Context(src, targ)
+ , lineevprov(evprov)
+ {
+ }
+
+ using Whois::Context::SendLine;
+ void SendLine(Numeric::Numeric& numeric) CXX11_OVERRIDE;
+};
+
+void WhoisContextImpl::SendLine(Numeric::Numeric& numeric)
+{
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT_CUSTOM(lineevprov, Whois::LineEventListener, OnWhoisLine, MOD_RESULT, (*this, numeric));
+
+ if (MOD_RESULT != MOD_RES_DENY)
+ source->WriteNumeric(numeric);
+}
+
/** Handle /WHOIS.
*/
class CommandWhois : public SplitCommand
@@ -28,10 +52,11 @@ class CommandWhois : public SplitCommand
ChanModeReference secretmode;
ChanModeReference privatemode;
UserModeReference snomaskmode;
+ Events::ModuleEventProvider evprov;
+ Events::ModuleEventProvider lineevprov;
- void SplitChanList(User* source, User* dest, const std::string& cl);
- void DoWhois(User* user, User* dest, unsigned long signon, unsigned long idle);
- std::string ChannelList(User* source, User* dest, bool spy);
+ void DoWhois(LocalUser* user, User* dest, unsigned long signon, unsigned long idle);
+ void SendChanList(WhoisContextImpl& whois);
public:
/** Constructor for whois.
@@ -41,6 +66,8 @@ class CommandWhois : public SplitCommand
, secretmode(parent, "secret")
, privatemode(parent, "private")
, snomaskmode(parent, "snomask")
+ , evprov(parent, "event/whois")
+ , lineevprov(parent, "event/whoisline")
{
Penalty = 2;
syntax = "<nick>{,<nick>}";
@@ -55,116 +82,144 @@ class CommandWhois : public SplitCommand
CmdResult HandleRemote(const std::vector<std::string>& parameters, RemoteUser* target);
};
-std::string CommandWhois::ChannelList(User* source, User* dest, bool spy)
+class WhoisNumericSink
{
- std::string list;
+ WhoisContextImpl& whois;
+ public:
+ WhoisNumericSink(WhoisContextImpl& whoisref)
+ : whois(whoisref)
+ {
+ }
- for (UCListIter i = dest->chans.begin(); i != dest->chans.end(); i++)
+ void operator()(Numeric::Numeric& numeric) const
{
- Membership* memb = *i;
- Channel* c = memb->chan;
- /* If the target is the sender, neither +p nor +s is set, or
- * the channel contains the user, it is not a spy channel
- */
- if (spy != (source == dest || !(c->IsModeSet(privatemode) || c->IsModeSet(secretmode)) || c->HasUser(source)))
- {
- char prefix = memb->GetPrefixChar();
- if (prefix)
- list.push_back(prefix);
- list.append(c->name).push_back(' ');
- }
+ whois.SendLine(numeric);
}
+};
- return list;
-}
+class WhoisChanListNumericBuilder : public Numeric::GenericBuilder<' ', false, WhoisNumericSink>
+{
+ public:
+ WhoisChanListNumericBuilder(WhoisContextImpl& whois)
+ : Numeric::GenericBuilder<' ', false, WhoisNumericSink>(WhoisNumericSink(whois), 319, false, whois.GetSource()->nick.size() + whois.GetTarget()->nick.size() + 1)
+ {
+ GetNumeric().push(whois.GetTarget()->nick).push(std::string());
+ }
+};
-void CommandWhois::SplitChanList(User* source, User* dest, const std::string& cl)
+class WhoisChanList
{
- std::string line;
- std::ostringstream prefix;
- std::string::size_type start, pos;
+ const ServerConfig::OperSpyWhoisState spywhois;
+ WhoisChanListNumericBuilder num;
+ WhoisChanListNumericBuilder spynum;
+ std::string prefixstr;
- prefix << dest->nick << " :";
- line = prefix.str();
- int namelen = ServerInstance->Config->ServerName.length() + 6;
+ void AddMember(Membership* memb, WhoisChanListNumericBuilder& out)
+ {
+ prefixstr.clear();
+ const char prefix = memb->GetPrefixChar();
+ if (prefix)
+ prefixstr.push_back(prefix);
+ out.Add(prefixstr, memb->chan->name);
+ }
- for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
+ public:
+ WhoisChanList(WhoisContextImpl& whois)
+ : spywhois(whois.GetSource()->HasPrivPermission("users/auspex") ? ServerInstance->Config->OperSpyWhois : ServerConfig::SPYWHOIS_NONE)
+ , num(whois)
+ , spynum(whois)
{
- if (line.length() + namelen + pos - start > 510)
- {
- ServerInstance->SendWhoisLine(source, dest, 319, line);
- line = prefix.str();
- }
+ }
- line.append(cl.substr(start, pos - start + 1));
+ void AddVisible(Membership* memb)
+ {
+ AddMember(memb, num);
}
- if (line.length() != prefix.str().length())
+ void AddHidden(Membership* memb)
{
- ServerInstance->SendWhoisLine(source, dest, 319, line);
+ if (spywhois == ServerConfig::SPYWHOIS_NONE)
+ return;
+ AddMember(memb, (spywhois == ServerConfig::SPYWHOIS_SPLITMSG ? spynum : num));
}
-}
-void CommandWhois::DoWhois(User* user, User* dest, unsigned long signon, unsigned long idle)
-{
- ServerInstance->SendWhoisLine(user, dest, 311, "%s %s %s * :%s", dest->nick.c_str(), dest->ident.c_str(), dest->dhost.c_str(), dest->fullname.c_str());
- if (user == dest || user->HasPrivPermission("users/auspex"))
+ void Flush(WhoisContextImpl& whois)
{
- ServerInstance->SendWhoisLine(user, dest, 378, "%s :is connecting from %s@%s %s", dest->nick.c_str(), dest->ident.c_str(), dest->host.c_str(), dest->GetIPString().c_str());
+ num.Flush();
+ if (!spynum.IsEmpty())
+ whois.SendLine(336, "is on private/secret channels:");
+ spynum.Flush();
}
+};
- std::string cl = ChannelList(user, dest, false);
- const ServerConfig::OperSpyWhoisState state = user->HasPrivPermission("users/auspex") ? ServerInstance->Config->OperSpyWhois : ServerConfig::SPYWHOIS_NONE;
+void CommandWhois::SendChanList(WhoisContextImpl& whois)
+{
+ WhoisChanList chanlist(whois);
+
+ User* const target = whois.GetTarget();
+ for (User::ChanList::iterator i = target->chans.begin(); i != target->chans.end(); ++i)
+ {
+ Membership* memb = *i;
+ Channel* c = memb->chan;
+ /* If the target is the sender, neither +p nor +s is set, or
+ * the channel contains the user, it is not a spy channel
+ */
+ if ((whois.IsSelfWhois()) || ((!c->IsModeSet(privatemode)) && (!c->IsModeSet(secretmode))) || (c->HasUser(whois.GetSource())))
+ chanlist.AddVisible(memb);
+ else
+ chanlist.AddHidden(memb);
+ }
- if (state == ServerConfig::SPYWHOIS_SINGLEMSG)
- cl.append(ChannelList(user, dest, true));
+ chanlist.Flush(whois);
+}
- SplitChanList(user, dest, cl);
+void CommandWhois::DoWhois(LocalUser* user, User* dest, unsigned long signon, unsigned long idle)
+{
+ WhoisContextImpl whois(user, dest, lineevprov);
- if (state == ServerConfig::SPYWHOIS_SPLITMSG)
+ whois.SendLine(311, dest->ident, dest->dhost, '*', dest->fullname);
+ if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
{
- std::string scl = ChannelList(user, dest, true);
- if (scl.length())
- {
- ServerInstance->SendWhoisLine(user, dest, 336, "%s :is on private/secret channels:", dest->nick.c_str());
- SplitChanList(user, dest, scl);
- }
+ whois.SendLine(378, InspIRCd::Format("is connecting from %s@%s %s", dest->ident.c_str(), dest->host.c_str(), dest->GetIPString().c_str()));
}
- if (user != dest && !ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
+
+ SendChanList(whois);
+
+ if (!whois.IsSelfWhois() && !ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
{
- ServerInstance->SendWhoisLine(user, dest, 312, "%s %s :%s", dest->nick.c_str(), ServerInstance->Config->HideWhoisServer.c_str(), ServerInstance->Config->Network.c_str());
+ whois.SendLine(312, ServerInstance->Config->HideWhoisServer, ServerInstance->Config->Network);
}
else
{
- ServerInstance->SendWhoisLine(user, dest, 312, "%s %s :%s", dest->nick.c_str(), dest->server->GetName().c_str(), dest->server->GetDesc().c_str());
+ whois.SendLine(312, dest->server->GetName(), dest->server->GetDesc());
}
if (dest->IsAway())
{
- ServerInstance->SendWhoisLine(user, dest, 301, "%s :%s", dest->nick.c_str(), dest->awaymsg.c_str());
+ whois.SendLine(301, dest->awaymsg);
}
if (dest->IsOper())
{
if (ServerInstance->Config->GenericOper)
- ServerInstance->SendWhoisLine(user, dest, 313, "%s :is an IRC operator", dest->nick.c_str());
+ whois.SendLine(313, "is an IRC operator");
else
- ServerInstance->SendWhoisLine(user, dest, 313, "%s :is %s %s on %s", dest->nick.c_str(), (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"),dest->oper->name.c_str(), ServerInstance->Config->Network.c_str());
+ whois.SendLine(313, InspIRCd::Format("is %s %s on %s", (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"), dest->oper->name.c_str(), ServerInstance->Config->Network.c_str()));
}
- if (user == dest || user->HasPrivPermission("users/auspex"))
+ if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
{
if (dest->IsModeSet(snomaskmode))
{
- ServerInstance->SendWhoisLine(user, dest, 379, "%s :is using modes +%s %s", dest->nick.c_str(), dest->FormatModes(), snomaskmode->GetUserParameter(dest).c_str());
+ whois.SendLine(379, InspIRCd::Format("is using modes %s %s", dest->GetModeLetters().c_str(), snomaskmode->GetUserParameter(dest).c_str()));
}
else
{
- ServerInstance->SendWhoisLine(user, dest, 379, "%s :is using modes +%s", dest->nick.c_str(), dest->FormatModes());
+ whois.SendLine(379, InspIRCd::Format("is using modes %s", dest->GetModeLetters().c_str()));
}
}
- FOREACH_MOD(OnWhois, (user,dest));
+ FOREACH_MOD_CUSTOM(evprov, Whois::EventListener, OnWhois, (whois));
/*
* We only send these if we've been provided them. That is, if hidewhois is turned off, and user is local, or
@@ -172,10 +227,10 @@ void CommandWhois::DoWhois(User* user, User* dest, unsigned long signon, unsigne
*/
if ((idle) || (signon))
{
- ServerInstance->SendWhoisLine(user, dest, 317, "%s %lu %lu :seconds idle, signon time", dest->nick.c_str(), idle, signon);
+ whois.SendLine(317, idle, signon, "seconds idle, signon time");
}
- ServerInstance->SendWhoisLine(user, dest, 318, "%s :End of /WHOIS list.", dest->nick.c_str());
+ whois.SendLine(318, "End of /WHOIS list.");
}
CmdResult CommandWhois::HandleRemote(const std::vector<std::string>& parameters, RemoteUser* target)
@@ -187,8 +242,13 @@ CmdResult CommandWhois::HandleRemote(const std::vector<std::string>& parameters,
if (!user)
return CMD_FAILURE;
+ // User doing the whois must be on this server
+ LocalUser* localuser = IS_LOCAL(user);
+ if (!localuser)
+ return CMD_FAILURE;
+
unsigned long idle = ConvToInt(parameters.back());
- DoWhois(user, target, target->signon, idle);
+ DoWhois(localuser, target, target->signon, idle);
return CMD_SUCCESS;
}
@@ -224,7 +284,7 @@ CmdResult CommandWhois::HandleLocal(const std::vector<std::string>& parameters,
LocalUser* localuser = IS_LOCAL(dest);
if (localuser && (ServerInstance->Config->HideWhoisServer.empty() || parameters.size() > 1))
{
- idle = abs((long)((localuser->idle_lastmsg)-ServerInstance->Time()));
+ idle = labs((long)((localuser->idle_lastmsg)-ServerInstance->Time()));
signon = dest->signon;
}
@@ -233,8 +293,8 @@ CmdResult CommandWhois::HandleLocal(const std::vector<std::string>& parameters,
else
{
/* no such nick/channel */
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
- user->WriteNumeric(RPL_ENDOFWHOIS, "%s :End of /WHOIS list.", !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
+ user->WriteNumeric(Numerics::NoSuchNick(!parameters[userindex].empty() ? parameters[userindex] : "*"));
+ user->WriteNumeric(RPL_ENDOFWHOIS, (!parameters[userindex].empty() ? parameters[userindex] : "*"), "End of /WHOIS list.");
return CMD_FAILURE;
}
diff --git a/src/coremods/core_whowas.cpp b/src/coremods/core_whowas.cpp
index 0227fdb51..f52fb0174 100644
--- a/src/coremods/core_whowas.cpp
+++ b/src/coremods/core_whowas.cpp
@@ -25,7 +25,6 @@
CommandWhowas::CommandWhowas( Module* parent)
: Command(parent, "WHOWAS", 1)
- , GroupSize(0), MaxGroups(0), MaxKeep(0)
{
syntax = "<nick>{,<nick>}";
Penalty = 2;
@@ -34,189 +33,223 @@ CommandWhowas::CommandWhowas( Module* parent)
CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user)
{
/* if whowas disabled in config */
- if (this->GroupSize == 0 || this->MaxGroups == 0)
+ if (!manager.IsEnabled())
{
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", name.c_str());
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, name, "This command has been disabled.");
return CMD_FAILURE;
}
- whowas_users::iterator i = whowas.find(assign(parameters[0]));
-
- if (i == whowas.end())
+ const WhoWas::Nick* const nick = manager.FindNick(parameters[0]);
+ if (!nick)
{
- user->WriteNumeric(ERR_WASNOSUCHNICK, "%s :There was no such nickname", parameters[0].c_str());
+ user->WriteNumeric(ERR_WASNOSUCHNICK, parameters[0], "There was no such nickname");
}
else
{
- whowas_set* grp = i->second;
- if (!grp->empty())
+ const WhoWas::Nick::List& list = nick->entries;
+ for (WhoWas::Nick::List::const_iterator i = list.begin(); i != list.end(); ++i)
{
- for (whowas_set::iterator ux = grp->begin(); ux != grp->end(); ux++)
- {
- WhoWasGroup* u = *ux;
+ WhoWas::Entry* u = *i;
- user->WriteNumeric(RPL_WHOWASUSER, "%s %s %s * :%s", parameters[0].c_str(),
- u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
+ user->WriteNumeric(RPL_WHOWASUSER, parameters[0], u->ident, u->dhost, '*', u->gecos);
- if (user->HasPrivPermission("users/auspex"))
- user->WriteNumeric(RPL_WHOWASIP, "%s :was connecting from *@%s",
- parameters[0].c_str(), u->host.c_str());
+ if (user->HasPrivPermission("users/auspex"))
+ user->WriteNumeric(RPL_WHOWASIP, parameters[0], InspIRCd::Format("was connecting from *@%s", u->host.c_str()));
- std::string signon = InspIRCd::TimeString(u->signon);
- bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
- user->WriteNumeric(RPL_WHOISSERVER, "%s %s :%s", parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), signon.c_str());
- }
- }
- else
- {
- user->WriteNumeric(ERR_WASNOSUCHNICK, "%s :There was no such nickname", parameters[0].c_str());
+ std::string signon = InspIRCd::TimeString(u->signon);
+ bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
+ user->WriteNumeric(RPL_WHOISSERVER, parameters[0], (hide_server ? ServerInstance->Config->HideWhoisServer : u->server), signon);
}
}
- user->WriteNumeric(RPL_ENDOFWHOWAS, "%s :End of WHOWAS", parameters[0].c_str());
+ user->WriteNumeric(RPL_ENDOFWHOWAS, parameters[0], "End of WHOWAS");
return CMD_SUCCESS;
}
-std::string CommandWhowas::GetStats()
+WhoWas::Manager::Manager()
+ : GroupSize(0), MaxGroups(0), MaxKeep(0)
{
- int whowas_size = 0;
- int whowas_bytes = 0;
- for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
+}
+
+const WhoWas::Nick* WhoWas::Manager::FindNick(const std::string& nickname) const
+{
+ whowas_users::const_iterator it = whowas.find(nickname);
+ if (it == whowas.end())
+ return NULL;
+ return it->second;
+}
+
+WhoWas::Manager::Stats WhoWas::Manager::GetStats() const
+{
+ size_t entrycount = 0;
+ for (whowas_users::const_iterator i = whowas.begin(); i != whowas.end(); ++i)
{
- whowas_set* n = i->second;
- whowas_size += n->size();
- whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
+ WhoWas::Nick::List& list = i->second->entries;
+ entrycount += list.size();
}
- return "Whowas entries: " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)";
+
+ Stats stats;
+ stats.entrycount = entrycount;
+ return stats;
}
-void CommandWhowas::AddToWhoWas(User* user)
+void WhoWas::Manager::Add(User* user)
{
- /* if whowas disabled */
- if (this->GroupSize == 0 || this->MaxGroups == 0)
- {
+ if (!IsEnabled())
return;
- }
// Insert nick if it doesn't exist
// 'first' will point to the newly inserted element or to the existing element with an equivalent key
- std::pair<whowas_users::iterator, bool> ret = whowas.insert(std::make_pair(irc::string(user->nick.c_str()), static_cast<whowas_set*>(NULL)));
+ std::pair<whowas_users::iterator, bool> ret = whowas.insert(std::make_pair(user->nick, static_cast<WhoWas::Nick*>(NULL)));
if (ret.second) // If inserted
{
// This nick is new, create a list for it and add the first record to it
- whowas_set* n = new whowas_set;
- n->push_back(new WhoWasGroup(user));
- ret.first->second = n;
+ WhoWas::Nick* nick = new WhoWas::Nick(ret.first->first);
+ nick->entries.push_back(new Entry(user));
+ ret.first->second = nick;
// Add this nick to the fifo too
- whowas_fifo.push_back(std::make_pair(ServerInstance->Time(), ret.first->first));
+ whowas_fifo.push_back(nick);
if (whowas.size() > this->MaxGroups)
{
// Too many nicks, remove the nick which was inserted the longest time ago from both the map and the fifo
- whowas_users::iterator it = whowas.find(whowas_fifo.front().second);
- if (it != whowas.end())
- {
- whowas_set* set = it->second;
- stdalgo::delete_all(*set);
-
- delete set;
- whowas.erase(it);
- }
- whowas_fifo.pop_front();
+ PurgeNick(whowas_fifo.front());
}
}
else
{
// We've met this nick before, add a new record to the list
- whowas_set* set = ret.first->second;
- set->push_back(new WhoWasGroup(user));
+ WhoWas::Nick::List& list = ret.first->second->entries;
+ list.push_back(new Entry(user));
// If there are too many records for this nick, remove the oldest (front)
- if (set->size() > this->GroupSize)
+ if (list.size() > this->GroupSize)
{
- delete set->front();
- set->pop_front();
+ delete list.front();
+ list.pop_front();
}
}
}
/* on rehash, refactor maps according to new conf values */
-void CommandWhowas::Prune()
+void WhoWas::Manager::Prune()
{
time_t min = ServerInstance->Time() - this->MaxKeep;
/* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
while (!whowas_fifo.empty())
{
- if ((whowas_fifo.size() > this->MaxGroups) || (whowas_fifo.front().first < min))
- {
- whowas_users::iterator iter = whowas.find(whowas_fifo.front().second);
-
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
- {
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS", LOG_DEFAULT, "BUG: Whowas maps got corrupted! (1)");
- return;
- }
-
- whowas_set* set = iter->second;
- stdalgo::delete_all(*set);
-
- delete set;
- whowas.erase(iter);
- whowas_fifo.pop_front();
- }
+ WhoWas::Nick* nick = whowas_fifo.front();
+ if ((whowas_fifo.size() > this->MaxGroups) || (nick->addtime < min))
+ PurgeNick(nick);
else
break;
}
/* Then cut the whowas sets to new size (groupsize) */
- for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); )
{
- whowas_set* n = i->second;
- while (n->size() > this->GroupSize)
+ WhoWas::Nick::List& list = i->second->entries;
+ while (list.size() > this->GroupSize)
{
- delete n->front();
- n->pop_front();
+ delete list.front();
+ list.pop_front();
}
+
+ if (list.empty())
+ PurgeNick(i++);
+ else
+ ++i;
}
}
/* call maintain once an hour to remove expired nicks */
-void CommandWhowas::Maintain()
+void WhoWas::Manager::Maintain()
{
time_t min = ServerInstance->Time() - this->MaxKeep;
- for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); )
{
- whowas_set* set = i->second;
- while (!set->empty() && set->front()->signon < min)
+ WhoWas::Nick::List& list = i->second->entries;
+ while (!list.empty() && list.front()->signon < min)
{
- delete set->front();
- set->pop_front();
+ delete list.front();
+ list.pop_front();
}
+
+ if (list.empty())
+ PurgeNick(i++);
+ else
+ ++i;
}
}
-CommandWhowas::~CommandWhowas()
+WhoWas::Manager::~Manager()
{
for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
{
- whowas_set* set = i->second;
- for (whowas_set::iterator j = set->begin(); j != set->end(); ++j)
- delete *j;
+ WhoWas::Nick* nick = i->second;
+ delete nick;
+ }
+}
+
+bool WhoWas::Manager::IsEnabled() const
+{
+ return ((GroupSize != 0) && (MaxGroups != 0));
+}
+
+void WhoWas::Manager::UpdateConfig(unsigned int NewGroupSize, unsigned int NewMaxGroups, unsigned int NewMaxKeep)
+{
+ if ((NewGroupSize == GroupSize) && (NewMaxGroups == MaxGroups) && (NewMaxKeep == MaxKeep))
+ return;
+
+ GroupSize = NewGroupSize;
+ MaxGroups = NewMaxGroups;
+ MaxKeep = NewMaxKeep;
+ Prune();
+}
+
+void WhoWas::Manager::PurgeNick(whowas_users::iterator it)
+{
+ WhoWas::Nick* nick = it->second;
+ whowas_fifo.erase(nick);
+ whowas.erase(it);
+ delete nick;
+}
- delete set;
+void WhoWas::Manager::PurgeNick(WhoWas::Nick* nick)
+{
+ whowas_users::iterator it = whowas.find(nick->nick);
+ if (it == whowas.end())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in whowas database, please report");
+ return;
}
+ PurgeNick(it);
}
-WhoWasGroup::WhoWasGroup(User* user) : host(user->host), dhost(user->dhost), ident(user->ident),
- server(user->server->GetName()), gecos(user->fullname), signon(user->signon)
+WhoWas::Entry::Entry(User* user)
+ : host(user->host)
+ , dhost(user->dhost)
+ , ident(user->ident)
+ , server(user->server->GetName())
+ , gecos(user->fullname)
+ , signon(user->signon)
{
}
+WhoWas::Nick::Nick(const std::string& nickname)
+ : addtime(ServerInstance->Time())
+ , nick(nickname)
+{
+}
+
+WhoWas::Nick::~Nick()
+{
+ stdalgo::delete_all(entries);
+}
+
class ModuleWhoWas : public Module
{
CommandWhowas cmd;
@@ -229,18 +262,18 @@ class ModuleWhoWas : public Module
void OnGarbageCollect()
{
// Remove all entries older than MaxKeep
- cmd.Maintain();
+ cmd.manager.Maintain();
}
void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
{
- cmd.AddToWhoWas(user);
+ cmd.manager.Add(user);
}
- ModResult OnStats(char symbol, User* user, string_list &results)
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol == 'z')
- results.push_back("249 "+user->nick+" :"+cmd.GetStats());
+ if (stats.GetSymbol() == 'z')
+ stats.AddRow(249, "Whowas entries: "+ConvToStr(cmd.manager.GetStats().entrycount));
return MOD_RES_PASSTHRU;
}
@@ -252,13 +285,7 @@ class ModuleWhoWas : public Module
unsigned int NewMaxGroups = tag->getInt("maxgroups", 10240, 0, 1000000);
unsigned int NewMaxKeep = tag->getDuration("maxkeep", 3600, 3600);
- if ((NewGroupSize == cmd.GroupSize) && (NewMaxGroups == cmd.MaxGroups) && (NewMaxKeep == cmd.MaxKeep))
- return;
-
- cmd.GroupSize = NewGroupSize;
- cmd.MaxGroups = NewMaxGroups;
- cmd.MaxKeep = NewMaxKeep;
- cmd.Prune();
+ cmd.manager.UpdateConfig(NewGroupSize, NewMaxGroups, NewMaxKeep);
}
Version GetVersion()
diff --git a/src/coremods/core_xline/cmd_gline.cpp b/src/coremods/core_xline/cmd_gline.cpp
index 3f042c366..49932ba9d 100644
--- a/src/coremods/core_xline/cmd_gline.cpp
+++ b/src/coremods/core_xline/cmd_gline.cpp
@@ -26,7 +26,6 @@ CommandGline::CommandGline(Module* parent)
: Command(parent, "GLINE", 1, 3)
{
flags_needed = 'o';
- Penalty = 0;
syntax = "<ident@host> [<duration> :<reason>]";
}
diff --git a/src/coremods/core_xline/cmd_kline.cpp b/src/coremods/core_xline/cmd_kline.cpp
index 50ab88398..db8862d37 100644
--- a/src/coremods/core_xline/cmd_kline.cpp
+++ b/src/coremods/core_xline/cmd_kline.cpp
@@ -26,7 +26,6 @@ CommandKline::CommandKline(Module* parent)
: Command(parent, "KLINE", 1, 3)
{
flags_needed = 'o';
- Penalty = 0;
syntax = "<ident@host> [<duration> :<reason>]";
}
@@ -34,7 +33,7 @@ CommandKline::CommandKline(Module* parent)
*/
CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User *user)
{
- std::string target = parameters[0];
+ std::string target = parameters[0];
if (parameters.size() >= 3)
{
@@ -49,11 +48,11 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User
else
ih = ServerInstance->XLines->IdentSplit(target);
- if (ih.first.empty())
- {
- user->WriteNotice("*** Target not found");
- return CMD_FAILURE;
- }
+ if (ih.first.empty())
+ {
+ user->WriteNotice("*** Target not found");
+ return CMD_FAILURE;
+ }
InsaneBan::IPHostMatcher matcher;
if (InsaneBan::MatchesEveryone(ih.first+"@"+ih.second, matcher, user, "K", "hostmasks"))
diff --git a/src/coremods/core_xline/cmd_qline.cpp b/src/coremods/core_xline/cmd_qline.cpp
index 955efeaf0..6dc0da9ba 100644
--- a/src/coremods/core_xline/cmd_qline.cpp
+++ b/src/coremods/core_xline/cmd_qline.cpp
@@ -27,7 +27,6 @@ CommandQline::CommandQline(Module* parent)
: Command(parent, "QLINE", 1, 3)
{
flags_needed = 'o';
- Penalty = 0;
syntax = "<nick> [<duration> :<reason>]";
}
diff --git a/src/coremods/core_xline/cmd_zline.cpp b/src/coremods/core_xline/cmd_zline.cpp
index 859be1004..1bc7e8afd 100644
--- a/src/coremods/core_xline/cmd_zline.cpp
+++ b/src/coremods/core_xline/cmd_zline.cpp
@@ -27,7 +27,6 @@ CommandZline::CommandZline(Module* parent)
: Command(parent, "ZLINE", 1, 3)
{
flags_needed = 'o';
- Penalty = 0;
syntax = "<ipmask> [<duration> :<reason>]";
}
diff --git a/src/coremods/core_xline/core_xline.cpp b/src/coremods/core_xline/core_xline.cpp
index 7daa70b49..93ac1db31 100644
--- a/src/coremods/core_xline/core_xline.cpp
+++ b/src/coremods/core_xline/core_xline.cpp
@@ -18,6 +18,7 @@
#include "inspircd.h"
+#include "xline.h"
#include "core_xline.h"
bool InsaneBan::MatchesEveryone(const std::string& mask, MatcherBase& test, User* user, const char* bantype, const char* confkey)
@@ -63,6 +64,26 @@ class CoreModXLine : public Module
{
}
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
+ {
+ // Check Q-Lines (for local nick changes only, remote servers have our Q-Lines to enforce themselves)
+
+ XLine* xline = ServerInstance->XLines->MatchesLine("Q", newnick);
+ if (!xline)
+ return MOD_RES_PASSTHRU; // No match
+
+ // A Q-Line matched the new nick, tell opers if the user is registered
+ if (user->registered == REG_ALL)
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "Q-Lined nickname %s from %s: %s",
+ newnick.c_str(), user->GetFullRealHost().c_str(), xline->reason.c_str());
+ }
+
+ // Send a numeric because if we deny then the core doesn't reply anything
+ user->WriteNumeric(ERR_ERRONEUSNICKNAME, newnick, InspIRCd::Format("Invalid nickname: %s", xline->reason.c_str()));
+ return MOD_RES_DENY;
+ }
+
Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the ELINE, GLINE, KLINE, QLINE, and ZLINE commands", VF_VENDOR|VF_CORE);
diff --git a/src/coremods/core_xline/core_xline.h b/src/coremods/core_xline/core_xline.h
index d4ad498a0..5b34e7a4d 100644
--- a/src/coremods/core_xline/core_xline.h
+++ b/src/coremods/core_xline/core_xline.h
@@ -133,7 +133,6 @@ class CommandQline : public Command
/** Handle command.
* @param parameters The parameters to the command
- * @param pcnt The number of parameters passed to the command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
diff --git a/src/cull_list.cpp b/src/cull_list.cpp
index 5cbe3aef3..73f2def51 100644
--- a/src/cull_list.cpp
+++ b/src/cull_list.cpp
@@ -21,7 +21,9 @@
#include "inspircd.h"
+#ifdef INSPIRCD_ENABLE_RTTI
#include <typeinfo>
+#endif
void CullList::Apply()
{
@@ -46,8 +48,12 @@ void CullList::Apply()
classbase* c = list[i];
if (gone.insert(c).second)
{
+#ifdef INSPIRCD_ENABLE_RTTI
ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Deleting %s @%p", typeid(*c).name(),
(void*)c);
+#else
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Deleting @%p", (void*)c);
+#endif
c->cull();
queue.push_back(c);
}
diff --git a/src/dynamic.cpp b/src/dynamic.cpp
index 1470dff0c..9984f4dbe 100644
--- a/src/dynamic.cpp
+++ b/src/dynamic.cpp
@@ -22,7 +22,7 @@
#include "inspircd.h"
-#include "dynamic.h"
+
#ifndef _WIN32
#include <dlfcn.h>
#else
@@ -97,9 +97,15 @@ std::string DLLManager::GetVersion()
#ifdef _WIN32
void DLLManager::RetrieveLastError()
{
- CHAR errmsg[100];
- FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errmsg, 100, 0);
+ char errmsg[500];
+ DWORD dwErrorCode = GetLastError();
+ if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)errmsg, _countof(errmsg), NULL) == 0)
+ sprintf_s(errmsg, _countof(errmsg), "Error code: %u", dwErrorCode);
SetLastError(ERROR_SUCCESS);
err = errmsg;
+
+ std::string::size_type p;
+ while ((p = err.find_last_of("\r\n")) != std::string::npos)
+ err.erase(p, 1);
}
#endif
diff --git a/src/filelogger.cpp b/src/filelogger.cpp
index fff0b37fa..5786758da 100644
--- a/src/filelogger.cpp
+++ b/src/filelogger.cpp
@@ -21,8 +21,6 @@
#include "inspircd.h"
#include <fstream>
-#include "socketengine.h"
-#include "filelogger.h"
FileLogStream::FileLogStream(LogLevel loglevel, FileWriter *fw) : LogStream(loglevel), f(fw)
{
@@ -47,10 +45,7 @@ void FileLogStream::OnLog(LogLevel loglevel, const std::string &type, const std:
if (ServerInstance->Time() != LAST)
{
- time_t local = ServerInstance->Time();
- struct tm *timeinfo = localtime(&local);
-
- TIMESTR.assign(asctime(timeinfo), 24);
+ TIMESTR = InspIRCd::TimeString(ServerInstance->Time());
LAST = ServerInstance->Time();
}
diff --git a/src/hashcomp.cpp b/src/hashcomp.cpp
index 32f74475f..2c7dca5b1 100644
--- a/src/hashcomp.cpp
+++ b/src/hashcomp.cpp
@@ -21,7 +21,6 @@
#include "inspircd.h"
-#include "hashcomp.h"
/******************************************************
*
@@ -152,15 +151,7 @@ unsigned const char rfc_case_sensitive_map[256] = {
250, 251, 252, 253, 254, 255, // 250-255
};
-size_t CoreExport irc::hash::operator()(const irc::string &s) const
-{
- register size_t t = 0;
- for (irc::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
- t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
- return t;
-}
-
-bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2) const
+bool irc::equals(const std::string& s1, const std::string& s2)
{
const unsigned char* n1 = (const unsigned char*)s1.c_str();
const unsigned char* n2 = (const unsigned char*)s2.c_str();
@@ -197,7 +188,7 @@ size_t irc::insensitive::operator()(const std::string &s) const
* only with *x replaced with national_case_insensitive_map[*x].
* This avoids a copy to use hash<const char*>
*/
- register size_t t = 0;
+ size_t t = 0;
for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
return t;
@@ -269,7 +260,7 @@ bool irc::tokenstream::GetToken(std::string &token)
/* This is the last parameter */
if (token[0] == ':' && !first)
{
- token = token.substr(1);
+ token.erase(token.begin());
if (!StreamEnd())
{
token += ' ';
@@ -281,14 +272,6 @@ bool irc::tokenstream::GetToken(std::string &token)
return true;
}
-bool irc::tokenstream::GetToken(irc::string &token)
-{
- std::string stdstring;
- bool returnval = GetToken(stdstring);
- token = assign(stdstring);
- return returnval;
-}
-
bool irc::tokenstream::GetToken(int &token)
{
std::string tok;
@@ -333,7 +316,7 @@ bool irc::sepstream::GetToken(std::string &token)
if (p == std::string::npos)
p = this->tokens.length();
- token = this->tokens.substr(this->pos, p - this->pos);
+ token.assign(tokens, this->pos, p - this->pos);
this->pos = p + 1;
return true;
@@ -349,71 +332,6 @@ bool irc::sepstream::StreamEnd()
return this->pos > this->tokens.length();
}
-irc::modestacker::modestacker(bool add) : adding(add)
-{
- sequence.clear();
- sequence.push_back("");
-}
-
-void irc::modestacker::Push(char modeletter, const std::string &parameter)
-{
- *(sequence.begin()) += modeletter;
- sequence.push_back(parameter);
-}
-
-void irc::modestacker::Push(char modeletter)
-{
- this->Push(modeletter,"");
-}
-
-void irc::modestacker::PushPlus()
-{
- this->Push('+',"");
-}
-
-void irc::modestacker::PushMinus()
-{
- this->Push('-',"");
-}
-
-int irc::modestacker::GetStackedLine(std::vector<std::string> &result, int max_line_size)
-{
- if (sequence.empty())
- {
- return 0;
- }
-
- unsigned int n = 0;
- int size = 1; /* Account for initial +/- char */
- int nextsize = 0;
- int start = result.size();
- std::string modeline = adding ? "+" : "-";
- result.push_back(modeline);
-
- if (sequence.size() > 1)
- nextsize = sequence[1].length() + 2;
-
- while (!sequence[0].empty() && (sequence.size() > 1) && (n < ServerInstance->Config->Limits.MaxModes) && ((size + nextsize) < max_line_size))
- {
- modeline += *(sequence[0].begin());
- if (!sequence[1].empty())
- {
- result.push_back(sequence[1]);
- size += nextsize; /* Account for mode character and whitespace */
- }
- sequence[0].erase(sequence[0].begin());
- sequence.erase(sequence.begin() + 1);
-
- if (sequence.size() > 1)
- nextsize = sequence[1].length() + 2;
-
- n++;
- }
- result[start] = modeline;
-
- return n;
-}
-
std::string irc::stringjoiner(const std::vector<std::string>& sequence, char separator)
{
std::string joined;
@@ -478,10 +396,9 @@ long irc::portparser::GetToken()
std::string::size_type dash = x.rfind('-');
if (dash != std::string::npos)
{
- std::string sbegin = x.substr(0, dash);
- std::string send = x.substr(dash+1, x.length());
+ std::string sbegin(x, 0, dash);
range_begin = atoi(sbegin.c_str());
- range_end = atoi(send.c_str());
+ range_end = atoi(x.c_str()+dash+1);
if ((range_begin > 0) && (range_end > 0) && (range_begin < 65536) && (range_end < 65536) && (range_begin < range_end))
{
diff --git a/src/helperfuncs.cpp b/src/helperfuncs.cpp
index 6316d1e34..c9135679c 100644
--- a/src/helperfuncs.cpp
+++ b/src/helperfuncs.cpp
@@ -37,14 +37,7 @@ User* InspIRCd::FindNick(const std::string &nick)
{
if (!nick.empty() && isdigit(*nick.begin()))
return FindUUID(nick);
-
- user_hash::iterator iter = this->Users->clientlist.find(nick);
-
- if (iter == this->Users->clientlist.end())
- /* Couldn't find it */
- return NULL;
-
- return iter->second;
+ return FindNickOnly(nick);
}
User* InspIRCd::FindNickOnly(const std::string &nick)
@@ -79,24 +72,6 @@ Channel* InspIRCd::FindChan(const std::string &chan)
return iter->second;
}
-/* Send an error notice to all users, registered or not */
-void InspIRCd::SendError(const std::string &s)
-{
- for (LocalUserList::const_iterator i = this->Users->local_users.begin(); i != this->Users->local_users.end(); i++)
- {
- User* u = *i;
- if (u->registered == REG_ALL)
- {
- u->WriteNotice(s);
- }
- else
- {
- /* Unregistered connections receive ERROR, not a NOTICE */
- u->Write("ERROR :" + s);
- }
- }
-}
-
bool InspIRCd::IsValidMask(const std::string &mask)
{
const char* dest = mask.c_str();
@@ -152,7 +127,8 @@ void InspIRCd::StripColor(std::string &sentence)
else
seq = 0;
- if (seq || ((*i == 2) || (*i == 15) || (*i == 22) || (*i == 21) || (*i == 31)))
+ // Strip all control codes too except \001 for CTCP
+ if (seq || ((*i >= 0) && (*i < 32) && (*i != 1)))
i = sentence.erase(i);
else
++i;
@@ -312,24 +288,6 @@ void InspIRCd::CheckRoot()
#endif
}
-void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const std::string &text)
-{
- std::string copy_text = text;
-
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnWhoisLine, MOD_RESULT, (user, dest, numeric, copy_text));
-
- if (MOD_RESULT != MOD_RES_DENY)
- user->WriteNumeric(numeric, copy_text);
-}
-
-void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...)
-{
- std::string textbuffer;
- VAFORMAT(textbuffer, format, format)
- this->SendWhoisLine(user, dest, numeric, textbuffer);
-}
-
/** Refactored by Brain, Jun 2009. Much faster with some clever O(1) array
* lookups and pointer maths.
*/
@@ -403,14 +361,14 @@ const char* InspIRCd::Format(const char* formatString, ...)
return ret;
}
-std::string InspIRCd::TimeString(time_t curtime)
+std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
{
#ifdef _WIN32
if (curtime < 0)
curtime = 0;
#endif
- struct tm* timeinfo = localtime(&curtime);
+ struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
if (!timeinfo)
{
curtime = 0;
@@ -424,7 +382,15 @@ std::string InspIRCd::TimeString(time_t curtime)
else if (timeinfo->tm_year + 1900 < 1000)
timeinfo->tm_year = 0;
- return std::string(asctime(timeinfo),24);
+ // This is the default format used by asctime without the terminating new line.
+ if (!format)
+ format = "%a %b %d %H:%M:%S %Y";
+
+ char buffer[512];
+ if (!strftime(buffer, sizeof(buffer), format, timeinfo))
+ buffer[0] = '\0';
+
+ return buffer;
}
std::string InspIRCd::GenRandomStr(int length, bool printable)
diff --git a/src/inspircd.cpp b/src/inspircd.cpp
index efbb013ca..0c9b67910 100644
--- a/src/inspircd.cpp
+++ b/src/inspircd.cpp
@@ -52,12 +52,7 @@
#include <fstream>
#include <iostream>
#include "xline.h"
-#include "bancache.h"
-#include "socketengine.h"
-#include "socket.h"
-#include "command_parse.h"
#include "exitcodes.h"
-#include "caller.h"
#include "testsuite.h"
InspIRCd* ServerInstance = NULL;
@@ -113,11 +108,6 @@ void InspIRCd::Cleanup()
}
ports.clear();
- /* Close all client sockets, or the new process inherits them */
- LocalUserList& list = Users->local_users;
- for (LocalUserList::iterator i = list.begin(); i != list.end(); ++i)
- Users->QuitUser(*i, "Server shutdown");
-
GlobalCulls.Apply();
Modules->UnloadAll();
@@ -129,20 +119,10 @@ void InspIRCd::Cleanup()
FakeClient->cull();
}
DeleteZero(this->FakeClient);
- DeleteZero(this->Users);
- DeleteZero(this->Modes);
DeleteZero(this->XLines);
- DeleteZero(this->Parser);
- DeleteZero(this->stats);
- DeleteZero(this->Modules);
- DeleteZero(this->BanCache);
- DeleteZero(this->SNO);
DeleteZero(this->Config);
- DeleteZero(this->PI);
- DeleteZero(this->Threads);
SocketEngine::Deinit();
Logs->CloseLogs();
- DeleteZero(this->Logs);
}
void InspIRCd::SetSignals()
@@ -214,7 +194,7 @@ bool InspIRCd::DaemonSeed()
#endif
}
-void InspIRCd::WritePID(const std::string &filename)
+void InspIRCd::WritePID(const std::string& filename, bool exitonfail)
{
#ifndef _WIN32
std::string fname(filename);
@@ -228,22 +208,25 @@ void InspIRCd::WritePID(const std::string &filename)
}
else
{
- std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
- this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s', exiting.",fname.c_str());
- Exit(EXIT_STATUS_PID);
+ if (exitonfail)
+ std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s'%s", fname.c_str(), (exitonfail ? ", exiting." : ""));
+ if (exitonfail)
+ Exit(EXIT_STATUS_PID);
}
#endif
}
InspIRCd::InspIRCd(int argc, char** argv) :
ConfigFileName(INSPIRCD_CONFIG_PATH "/inspircd.conf"),
+ PI(&DefaultProtocolInterface),
/* Functor pointer initialisation.
*
* THIS MUST MATCH THE ORDER OF DECLARATION OF THE FUNCTORS, e.g. the methods
* themselves within the class.
*/
- OperQuit("operquit", NULL),
+ OperQuit("operquit", ExtensionItem::EXT_USER, NULL),
GenRandom(&HandleGenRandom),
IsChannel(&HandleIsChannel),
IsNick(&HandleIsNick),
@@ -260,44 +243,18 @@ InspIRCd::InspIRCd(int argc, char** argv) :
do_nolog = 0, do_root = 0;
// Initialize so that if we exit before proper initialization they're not deleted
- this->Logs = 0;
- this->Threads = 0;
- this->PI = 0;
- this->Users = 0;
this->Config = 0;
- this->SNO = 0;
- this->BanCache = 0;
- this->Modules = 0;
- this->stats = 0;
- this->Parser = 0;
this->XLines = 0;
- this->Modes = 0;
this->ConfigThread = NULL;
this->FakeClient = NULL;
UpdateTime();
this->startup_time = TIME.tv_sec;
- // This must be created first, so other parts of Insp can use it while starting up
- this->Logs = new LogManager;
-
SocketEngine::Init();
- this->Threads = new ThreadEngine;
-
- /* Default implementation does nothing */
- this->PI = new ProtocolInterface;
-
- // Create base manager classes early, so nothing breaks
- this->Users = new UserManager;
-
this->Config = new ServerConfig;
- this->SNO = new SnomaskManager;
- this->BanCache = new BanCacheManager;
- this->Modules = new ModuleManager();
dynamic_reference_base::reset_all();
- this->stats = new serverstats();
- this->Parser = new CommandParser;
this->XLines = new XLineManager;
this->Config->cmdline.argv = argv;
@@ -369,7 +326,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
if (do_version)
{
- std::cout << std::endl << INSPIRCD_VERSION << " " << INSPIRCD_REVISION << std::endl;
+ std::cout << std::endl << INSPIRCD_VERSION << std::endl;
Exit(EXIT_STATUS_NOERROR);
}
@@ -386,7 +343,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
if (do_debug)
{
- FileWriter* fw = new FileWriter(stdout);
+ FileWriter* fw = new FileWriter(stdout, 1);
FileLogStream* fls = new FileLogStream(LOG_RAWIO, fw);
Logs->AddLogTypes("*", fls, true);
}
@@ -411,15 +368,8 @@ InspIRCd::InspIRCd(int argc, char** argv) :
}
}
- std::cout << con_green << "Inspire Internet Relay Chat Server" << con_reset << ", compiled on " __DATE__ " at " __TIME__ << std::endl;
- std::cout << con_green << "(C) InspIRCd Development Team." << con_reset << std::endl << std::endl;
- std::cout << "Developers:" << std::endl;
- std::cout << con_green << "\tBrain, FrostyCoolSlug, w00t, Om, Special, peavey" << std::endl;
- std::cout << "\taquanight, psychon, dz, danieldg, jackmcbarn" << std::endl;
- std::cout << "\tAttila" << con_reset << std::endl << std::endl;
- std::cout << "Others:\t\t\t" << con_green << "See /INFO Output" << con_reset << std::endl;
-
- this->Modes = new ModeParser;
+ std::cout << con_green << "InspIRCd - Internet Relay Chat Daemon" << con_reset << ", compiled on " __DATE__ " at " __TIME__ << std::endl;
+ std::cout << "For contributors & authors: " << con_green << "See /INFO Output" << con_reset << std::endl;
#ifndef _WIN32
if (!do_root)
@@ -549,7 +499,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
FreeConsole();
}
- QueryPerformanceFrequency(&stats->QPFrequency);
+ QueryPerformanceFrequency(&stats.QPFrequency);
#endif
Logs->Log("STARTUP", LOG_DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SocketEngine::GetMaxFds());
@@ -683,38 +633,35 @@ void InspIRCd::Run()
{
#ifndef _WIN32
getrusage(RUSAGE_SELF, &ru);
- stats->LastSampled = TIME;
- stats->LastCPU = ru.ru_utime;
+ stats.LastSampled = TIME;
+ stats.LastCPU = ru.ru_utime;
#else
- if(QueryPerformanceCounter(&stats->LastSampled))
+ if(QueryPerformanceCounter(&stats.LastSampled))
{
FILETIME CreationTime;
FILETIME ExitTime;
FILETIME KernelTime;
FILETIME UserTime;
GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime);
- stats->LastCPU.dwHighDateTime = KernelTime.dwHighDateTime + UserTime.dwHighDateTime;
- stats->LastCPU.dwLowDateTime = KernelTime.dwLowDateTime + UserTime.dwLowDateTime;
+ stats.LastCPU.dwHighDateTime = KernelTime.dwHighDateTime + UserTime.dwHighDateTime;
+ stats.LastCPU.dwLowDateTime = KernelTime.dwLowDateTime + UserTime.dwLowDateTime;
}
#endif
/* Allow a buffer of two seconds drift on this so that ntpdate etc dont harass admins */
if (TIME.tv_sec < OLDTIME - 2)
{
- SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %lu secs.", (unsigned long)OLDTIME-TIME.tv_sec);
+ SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %lu secs.", (unsigned long)(OLDTIME-TIME.tv_sec));
}
else if (TIME.tv_sec > OLDTIME + 2)
{
- SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)TIME.tv_sec - OLDTIME);
+ SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)(TIME.tv_sec - OLDTIME));
}
OLDTIME = TIME.tv_sec;
if ((TIME.tv_sec % 3600) == 0)
- {
- Users->GarbageCollect();
FOREACH_MOD(OnGarbageCollect, ());
- }
Timers.TickTimers(TIME.tv_sec);
Users->DoBackgroundUserStuff();
diff --git a/src/inspsocket.cpp b/src/inspsocket.cpp
index f22645168..9bfc6a73e 100644
--- a/src/inspsocket.cpp
+++ b/src/inspsocket.cpp
@@ -23,18 +23,15 @@
#include "inspircd.h"
-#include "socket.h"
-#include "inspstring.h"
-#include "socketengine.h"
#include "iohook.h"
-#ifndef DISABLE_WRITEV
-#include <sys/uio.h>
-#endif
-
-#ifndef IOV_MAX
-#define IOV_MAX 1024
-#endif
+static IOHook* GetNextHook(IOHook* hook)
+{
+ IOHookMiddle* const iohm = IOHookMiddle::ToMiddleHook(hook);
+ if (iohm)
+ return iohm->GetNextHook();
+ return NULL;
+}
BufferedSocket::BufferedSocket()
{
@@ -110,7 +107,7 @@ BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs&
if (!SocketEngine::AddFd(this, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE | FD_WRITE_WILL_BLOCK))
return I_ERR_NOMOREFDS;
- this->Timeout = new SocketTimeout(this->GetFd(), this, timeout, ServerInstance->Time());
+ this->Timeout = new SocketTimeout(this->GetFd(), this, timeout);
ServerInstance->Timers.AddTimer(this->Timeout);
ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BufferedSocket::DoConnect success");
@@ -123,19 +120,15 @@ void StreamSocket::Close()
{
// final chance, dump as much of the sendq as we can
DoWrite();
- if (GetIOHook())
+
+ IOHook* hook = GetIOHook();
+ DelIOHook();
+ while (hook)
{
- try
- {
- GetIOHook()->OnStreamSocketClose(this);
- }
- catch (CoreException& modexcept)
- {
- ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "%s threw an exception: %s",
- modexcept.GetSource().c_str(), modexcept.GetReason().c_str());
- }
- delete iohook;
- DelIOHook();
+ hook->OnStreamSocketClose(this);
+ IOHook* const nexthook = GetNextHook(hook);
+ delete hook;
+ hook = nexthook;
}
SocketEngine::Shutdown(this, 2);
SocketEngine::Close(this);
@@ -153,67 +146,79 @@ bool StreamSocket::GetNextLine(std::string& line, char delim)
std::string::size_type i = recvq.find(delim);
if (i == std::string::npos)
return false;
- line = recvq.substr(0, i);
- // TODO is this the most efficient way to split?
- recvq = recvq.substr(i + 1);
+ line.assign(recvq, 0, i);
+ recvq.erase(0, i + 1);
return true;
}
-void StreamSocket::DoRead()
+int StreamSocket::HookChainRead(IOHook* hook, std::string& rq)
{
- if (GetIOHook())
+ if (!hook)
+ return ReadToRecvQ(rq);
+
+ IOHookMiddle* const iohm = IOHookMiddle::ToMiddleHook(hook);
+ if (iohm)
{
- int rv = -1;
- try
- {
- rv = GetIOHook()->OnStreamSocketRead(this, recvq);
- }
- catch (CoreException& modexcept)
- {
- ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "%s threw an exception: %s",
- modexcept.GetSource().c_str(), modexcept.GetReason().c_str());
- return;
- }
- if (rv > 0)
- OnDataReady();
- if (rv < 0)
- SetError("Read Error"); // will not overwrite a better error message
+ // Call the next hook to put data into the recvq of the current hook
+ const int ret = HookChainRead(iohm->GetNextHook(), iohm->GetRecvQ());
+ if (ret <= 0)
+ return ret;
}
- else
+ return hook->OnStreamSocketRead(this, rq);
+}
+
+void StreamSocket::DoRead()
+{
+ const std::string::size_type prevrecvqsize = recvq.size();
+
+ const int result = HookChainRead(GetIOHook(), recvq);
+ if (result < 0)
{
+ SetError("Read Error"); // will not overwrite a better error message
+ return;
+ }
+
+ if (recvq.size() > prevrecvqsize)
+ OnDataReady();
+}
+
+int StreamSocket::ReadToRecvQ(std::string& rq)
+{
char* ReadBuffer = ServerInstance->GetReadBuffer();
int n = SocketEngine::Recv(this, ReadBuffer, ServerInstance->Config->NetBufferSize, 0);
if (n == ServerInstance->Config->NetBufferSize)
{
SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
- recvq.append(ReadBuffer, n);
- OnDataReady();
+ rq.append(ReadBuffer, n);
}
else if (n > 0)
{
SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ);
- recvq.append(ReadBuffer, n);
- OnDataReady();
+ rq.append(ReadBuffer, n);
}
else if (n == 0)
{
error = "Connection closed";
SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+ return -1;
}
else if (SocketEngine::IgnoreError())
{
SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_READ_WILL_BLOCK);
+ return 0;
}
else if (errno == EINTR)
{
SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
+ return 0;
}
else
{
error = SocketEngine::LastError();
SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+ return -1;
}
- }
+ return n;
}
/* Don't try to prepare huge blobs of data to send to a blocked socket */
@@ -221,120 +226,56 @@ static const int MYIOV_MAX = IOV_MAX < 128 ? IOV_MAX : 128;
void StreamSocket::DoWrite()
{
- if (sendq.empty())
+ if (getSendQSize() == 0)
return;
- if (!error.empty() || fd < 0 || fd == INT_MAX)
+ if (!error.empty() || fd < 0)
{
ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DoWrite on errored or closed socket");
return;
}
-#ifndef DISABLE_WRITEV
- if (GetIOHook())
-#endif
+ SendQueue* psendq = &sendq;
+ IOHook* hook = GetIOHook();
+ while (hook)
{
- int rv = -1;
- try
- {
- while (error.empty() && !sendq.empty())
- {
- if (sendq.size() > 1 && sendq[0].length() < 1024)
- {
- // Avoid multiple repeated SSL encryption invocations
- // This adds a single copy of the queue, but avoids
- // much more overhead in terms of system calls invoked
- // by the IOHook.
- //
- // The length limit of 1024 is to prevent merging strings
- // more than once when writes begin to block.
- std::string tmp;
- tmp.reserve(1280);
- while (!sendq.empty() && tmp.length() < 1024)
- {
- tmp.append(sendq.front());
- sendq.pop_front();
- }
- sendq.push_front(tmp);
- }
- std::string& front = sendq.front();
- int itemlen = front.length();
- if (GetIOHook())
- {
- rv = GetIOHook()->OnStreamSocketWrite(this, front);
- if (rv > 0)
- {
- // consumed the entire string, and is ready for more
- sendq_len -= itemlen;
- sendq.pop_front();
- }
- else if (rv == 0)
- {
- // socket has blocked. Stop trying to send data.
- // IOHook has requested unblock notification from the socketengine
+ int rv = hook->OnStreamSocketWrite(this, *psendq);
+ psendq = NULL;
- // Since it is possible that a partial write took place, adjust sendq_len
- sendq_len = sendq_len - itemlen + front.length();
- return;
- }
- else
- {
- SetError("Write Error"); // will not overwrite a better error message
- return;
- }
- }
-#ifdef DISABLE_WRITEV
- else
- {
- rv = SocketEngine::Send(this, front.data(), itemlen, 0);
- if (rv == 0)
- {
- SetError("Connection closed");
- return;
- }
- else if (rv < 0)
- {
- if (errno == EINTR || SocketEngine::IgnoreError())
- SocketEngine::ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
- else
- SetError(SocketEngine::LastError());
- return;
- }
- else if (rv < itemlen)
- {
- SocketEngine::ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
- front = front.substr(rv);
- sendq_len -= rv;
- return;
- }
- else
- {
- sendq_len -= itemlen;
- sendq.pop_front();
- if (sendq.empty())
- SocketEngine::ChangeEventMask(this, FD_WANT_EDGE_WRITE);
- }
- }
-#endif
- }
+ // rv == 0 means the socket has blocked. Stop trying to send data.
+ // IOHook has requested unblock notification from the socketengine.
+ if (rv == 0)
+ break;
+
+ if (rv < 0)
+ {
+ SetError("Write Error"); // will not overwrite a better error message
+ break;
}
- catch (CoreException& modexcept)
+
+ IOHookMiddle* const iohm = IOHookMiddle::ToMiddleHook(hook);
+ hook = NULL;
+ if (iohm)
{
- ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "%s threw an exception: %s",
- modexcept.GetSource().c_str(), modexcept.GetReason().c_str());
+ psendq = &iohm->GetSendQ();
+ hook = iohm->GetNextHook();
}
}
-#ifndef DISABLE_WRITEV
- else
- {
+
+ if (psendq)
+ FlushSendQ(*psendq);
+}
+
+void StreamSocket::FlushSendQ(SendQueue& sq)
+{
// don't even try if we are known to be blocking
if (GetEventMask() & FD_WRITE_WILL_BLOCK)
return;
// start out optimistic - we won't need to write any more
int eventChange = FD_WANT_EDGE_WRITE;
- while (error.empty() && sendq_len && eventChange == FD_WANT_EDGE_WRITE)
+ while (error.empty() && !sq.empty() && eventChange == FD_WANT_EDGE_WRITE)
{
// Prepare a writev() call to write all buffers efficiently
- int bufcount = sendq.size();
+ int bufcount = sq.size();
// cap the number of buffers at MYIOV_MAX
if (bufcount > MYIOV_MAX)
@@ -343,22 +284,25 @@ void StreamSocket::DoWrite()
}
int rv_max = 0;
- iovec* iovecs = new iovec[bufcount];
- for(int i=0; i < bufcount; i++)
+ int rv;
{
- iovecs[i].iov_base = const_cast<char*>(sendq[i].data());
- iovecs[i].iov_len = sendq[i].length();
- rv_max += sendq[i].length();
+ SocketEngine::IOVector iovecs[MYIOV_MAX];
+ size_t j = 0;
+ for (SendQueue::const_iterator i = sq.begin(), end = i+bufcount; i != end; ++i, j++)
+ {
+ const SendQueue::Element& elem = *i;
+ iovecs[j].iov_base = const_cast<char*>(elem.data());
+ iovecs[j].iov_len = elem.length();
+ rv_max += elem.length();
+ }
+ rv = SocketEngine::WriteV(this, iovecs, bufcount);
}
- int rv = writev(fd, iovecs, bufcount);
- delete[] iovecs;
- if (rv == (int)sendq_len)
+ if (rv == (int)sq.bytes())
{
// it's our lucky day, everything got written out. Fast cleanup.
// This won't ever happen if the number of buffers got capped.
- sendq_len = 0;
- sendq.clear();
+ sq.clear();
}
else if (rv > 0)
{
@@ -368,20 +312,19 @@ void StreamSocket::DoWrite()
// it's going to block now
eventChange = FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
}
- sendq_len -= rv;
- while (rv > 0 && !sendq.empty())
+ while (rv > 0 && !sq.empty())
{
- std::string& front = sendq.front();
+ const SendQueue::Element& front = sq.front();
if (front.length() <= (size_t)rv)
{
// this string got fully written out
rv -= front.length();
- sendq.pop_front();
+ sq.pop_front();
}
else
{
// stopped in the middle of this string
- front = front.substr(rv);
+ sq.erase_front(rv);
rv = 0;
}
}
@@ -413,8 +356,6 @@ void StreamSocket::DoWrite()
{
SocketEngine::ChangeEventMask(this, eventChange);
}
- }
-#endif
}
void StreamSocket::WriteData(const std::string &data)
@@ -428,7 +369,6 @@ void StreamSocket::WriteData(const std::string &data)
/* Append the data to the back of the queue ready for writing */
sendq.push_back(data);
- sendq_len += data.length();
SocketEngine::ChangeEventMask(this, FD_ADD_TRIAL_WRITE);
}
@@ -464,7 +404,7 @@ bool SocketTimeout::Tick(time_t)
void BufferedSocket::OnConnected() { }
void BufferedSocket::OnTimeout() { return; }
-void BufferedSocket::DoWrite()
+void BufferedSocket::OnEventHandlerWrite()
{
if (state == I_CONNECTING)
{
@@ -473,73 +413,127 @@ void BufferedSocket::DoWrite()
if (!GetIOHook())
SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
}
- this->StreamSocket::DoWrite();
+ this->StreamSocket::OnEventHandlerWrite();
}
BufferedSocket::~BufferedSocket()
{
this->Close();
- if (Timeout)
+ // The timer is removed from the TimerManager in Timer::~Timer()
+ delete Timeout;
+}
+
+void StreamSocket::OnEventHandlerError(int errornum)
+{
+ if (!error.empty())
+ return;
+
+ if (errornum == 0)
+ SetError("Connection closed");
+ else
+ SetError(SocketEngine::GetError(errornum));
+
+ BufferedSocketError errcode = I_ERR_OTHER;
+ switch (errornum)
{
- // The timer is removed from the TimerManager in Timer::~Timer()
- delete Timeout;
+ case ETIMEDOUT:
+ errcode = I_ERR_TIMEOUT;
+ break;
+ case ECONNREFUSED:
+ case 0:
+ errcode = I_ERR_CONNECT;
+ break;
+ case EADDRINUSE:
+ errcode = I_ERR_BIND;
+ break;
+ case EPIPE:
+ case EIO:
+ errcode = I_ERR_WRITE;
+ break;
}
+
+ // Log and call OnError()
+ CheckError(errcode);
}
-void StreamSocket::HandleEvent(EventType et, int errornum)
+void StreamSocket::OnEventHandlerRead()
{
if (!error.empty())
return;
- BufferedSocketError errcode = I_ERR_OTHER;
- try {
- switch (et)
- {
- case EVENT_ERROR:
- {
- if (errornum == 0)
- SetError("Connection closed");
- else
- SetError(SocketEngine::GetError(errornum));
- switch (errornum)
- {
- case ETIMEDOUT:
- errcode = I_ERR_TIMEOUT;
- break;
- case ECONNREFUSED:
- case 0:
- errcode = I_ERR_CONNECT;
- break;
- case EADDRINUSE:
- errcode = I_ERR_BIND;
- break;
- case EPIPE:
- case EIO:
- errcode = I_ERR_WRITE;
- break;
- }
- break;
- }
- case EVENT_READ:
- {
- DoRead();
- break;
- }
- case EVENT_WRITE:
- {
- DoWrite();
- break;
- }
- }
+
+ try
+ {
+ DoRead();
}
catch (CoreException& ex)
{
- ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Caught exception in socket processing on FD %d - '%s'",
- fd, ex.GetReason().c_str());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Caught exception in socket processing on FD %d - '%s'", fd, ex.GetReason().c_str());
SetError(ex.GetReason());
}
+ CheckError(I_ERR_OTHER);
+}
+
+void StreamSocket::OnEventHandlerWrite()
+{
+ if (!error.empty())
+ return;
+
+ DoWrite();
+ CheckError(I_ERR_OTHER);
+}
+
+void StreamSocket::CheckError(BufferedSocketError errcode)
+{
if (!error.empty())
{
ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error on FD %d - '%s'", fd, error.c_str());
OnError(errcode);
}
}
+
+IOHook* StreamSocket::GetModHook(Module* mod) const
+{
+ for (IOHook* curr = GetIOHook(); curr; curr = GetNextHook(curr))
+ {
+ if (curr->prov->creator == mod)
+ return curr;
+ }
+ return NULL;
+}
+
+void StreamSocket::AddIOHook(IOHook* newhook)
+{
+ IOHook* curr = GetIOHook();
+ if (!curr)
+ {
+ iohook = newhook;
+ return;
+ }
+
+ IOHookMiddle* lasthook;
+ while (curr)
+ {
+ lasthook = IOHookMiddle::ToMiddleHook(curr);
+ if (!lasthook)
+ return;
+ curr = lasthook->GetNextHook();
+ }
+
+ lasthook->SetNextHook(newhook);
+}
+
+size_t StreamSocket::getSendQSize() const
+{
+ size_t ret = sendq.bytes();
+ IOHook* curr = GetIOHook();
+ while (curr)
+ {
+ const IOHookMiddle* const iohm = IOHookMiddle::ToMiddleHook(curr);
+ if (!iohm)
+ break;
+
+ ret += iohm->GetSendQ().bytes();
+ curr = iohm->GetNextHook();
+ }
+ return ret;
+}
diff --git a/src/inspstring.cpp b/src/inspstring.cpp
index 7fa4762c5..b59492738 100644
--- a/src/inspstring.cpp
+++ b/src/inspstring.cpp
@@ -108,3 +108,19 @@ std::string Base64ToBin(const std::string& data_str, const char* table)
}
return rv;
}
+
+bool InspIRCd::TimingSafeCompare(const std::string& one, const std::string& two)
+{
+ if (one.length() != two.length())
+ return false;
+
+ unsigned int diff = 0;
+ for (std::string::const_iterator i = one.begin(), j = two.begin(); i != one.end(); ++i, ++j)
+ {
+ unsigned char a = static_cast<unsigned char>(*i);
+ unsigned char b = static_cast<unsigned char>(*j);
+ diff |= a ^ b;
+ }
+
+ return (diff == 0);
+}
diff --git a/src/listensocket.cpp b/src/listensocket.cpp
index cb4bfd2db..d09f5e624 100644
--- a/src/listensocket.cpp
+++ b/src/listensocket.cpp
@@ -19,8 +19,7 @@
#include "inspircd.h"
-#include "socket.h"
-#include "socketengine.h"
+#include "iohook.h"
#ifndef _WIN32
#include <netinet/tcp.h>
@@ -28,7 +27,6 @@
ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_to)
: bind_tag(tag)
- , iohookprov(NULL, std::string())
{
irc::sockets::satoap(bind_to, bind_addr, bind_port);
bind_desc = bind_to.str();
@@ -56,12 +54,27 @@ ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_t
}
#endif
+ if (tag->getBool("free"))
+ {
+ socklen_t enable = 1;
+#if defined IP_FREEBIND // Linux 2.4+
+ setsockopt(fd, SOL_IP, IP_FREEBIND, &enable, sizeof(enable));
+#elif defined IP_BINDANY // FreeBSD
+ setsockopt(fd, IPPROTO_IP, IP_BINDANY, &enable, sizeof(enable));
+#elif defined SO_BINDANY // NetBSD/OpenBSD
+ setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable, sizeof(enable));
+#else
+ (void)enable;
+#endif
+ }
+
SocketEngine::SetReuse(fd);
int rv = SocketEngine::Bind(this->fd, bind_to);
if (rv >= 0)
rv = SocketEngine::Listen(this->fd, ServerInstance->Config->MaxConn);
- int timeout = tag->getInt("defer", 0);
+ // Default defer to on for TLS listeners because in TLS the client always speaks first
+ int timeout = tag->getInt("defer", (tag->getString("ssl").empty() ? 0 : 3));
if (timeout && !rv)
{
#if defined TCP_DEFER_ACCEPT
@@ -102,8 +115,7 @@ ListenSocket::~ListenSocket()
}
}
-/* Just seperated into another func for tidiness really.. */
-void ListenSocket::AcceptInternal()
+void ListenSocket::OnEventHandlerRead()
{
irc::sockets::sockaddrs client;
irc::sockets::sockaddrs server;
@@ -111,10 +123,10 @@ void ListenSocket::AcceptInternal()
socklen_t length = sizeof(client);
int incomingSockfd = SocketEngine::Accept(this, &client.sa, &length);
- ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "HandleEvent for Listensocket %s nfd=%d", bind_desc.c_str(), incomingSockfd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Accepting connection on socket %s fd %d", bind_desc.c_str(), incomingSockfd);
if (incomingSockfd < 0)
{
- ServerInstance->stats->statsRefused++;
+ ServerInstance->stats.Refused++;
return;
}
@@ -170,42 +182,34 @@ void ListenSocket::AcceptInternal()
}
if (res == MOD_RES_ALLOW)
{
- ServerInstance->stats->statsAccept++;
+ ServerInstance->stats.Accept++;
}
else
{
- ServerInstance->stats->statsRefused++;
+ ServerInstance->stats.Refused++;
ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Refusing connection on %s - %s",
bind_desc.c_str(), res == MOD_RES_DENY ? "Connection refused by module" : "Module for this port not found");
SocketEngine::Close(incomingSockfd);
}
}
-void ListenSocket::HandleEvent(EventType e, int err)
+void ListenSocket::ResetIOHookProvider()
{
- switch (e)
+ iohookprovs[0].SetProvider(bind_tag->getString("hook"));
+
+ // Check that all non-last hooks support being in the middle
+ for (IOHookProvList::iterator i = iohookprovs.begin(); i != iohookprovs.end()-1; ++i)
{
- case EVENT_ERROR:
- ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ListenSocket::HandleEvent() received a socket engine error event! well shit! '%s'", strerror(err));
- break;
- case EVENT_WRITE:
- ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "*** BUG *** ListenSocket::HandleEvent() got a WRITE event!!!");
- break;
- case EVENT_READ:
- this->AcceptInternal();
- break;
+ IOHookProvRef& curr = *i;
+ // Ignore if cannot be in the middle
+ if ((curr) && (!curr->IsMiddle()))
+ curr.SetProvider(std::string());
}
-}
-bool ListenSocket::ResetIOHookProvider()
-{
std::string provname = bind_tag->getString("ssl");
if (!provname.empty())
provname.insert(0, "ssl/");
- // Set the new provider name, dynref handles the rest
- iohookprov.SetProvider(provname);
-
- // Return true if no provider was set, or one was set and it was also found
- return (provname.empty() || iohookprov);
+ // SSL should be the last
+ iohookprovs.back().SetProvider(provname);
}
diff --git a/src/listmode.cpp b/src/listmode.cpp
index 0f139bb01..cd034688c 100644
--- a/src/listmode.cpp
+++ b/src/listmode.cpp
@@ -22,7 +22,8 @@
ListModeBase::ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag)
: ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_LIST),
listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy),
- configtag(ctag), extItem("listbase_mode_" + name + "_list", Creator)
+ configtag(ctag)
+ , extItem("listbase_mode_" + name + "_list", ExtensionItem::EXT_CHANNEL, Creator)
{
list = true;
}
@@ -32,27 +33,27 @@ void ListModeBase::DisplayList(User* user, Channel* channel)
ChanData* cd = extItem.get(channel);
if (cd)
{
- for (ModeList::reverse_iterator it = cd->list.rbegin(); it != cd->list.rend(); ++it)
+ for (ModeList::const_iterator it = cd->list.begin(); it != cd->list.end(); ++it)
{
- user->WriteNumeric(listnumeric, "%s %s %s %lu", channel->name.c_str(), it->mask.c_str(), (!it->setter.empty() ? it->setter.c_str() : ServerInstance->Config->ServerName.c_str()), (unsigned long) it->time);
+ user->WriteNumeric(listnumeric, channel->name, it->mask, it->setter, (unsigned long) it->time);
}
}
- user->WriteNumeric(endoflistnumeric, "%s :%s", channel->name.c_str(), endofliststring.c_str());
+ user->WriteNumeric(endoflistnumeric, channel->name, endofliststring);
}
void ListModeBase::DisplayEmptyList(User* user, Channel* channel)
{
- user->WriteNumeric(endoflistnumeric, "%s :%s", channel->name.c_str(), endofliststring.c_str());
+ user->WriteNumeric(endoflistnumeric, channel->name, endofliststring);
}
-void ListModeBase::RemoveMode(Channel* channel, irc::modestacker& stack)
+void ListModeBase::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
{
ChanData* cd = extItem.get(channel);
if (cd)
{
for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++)
{
- stack.Push(this->GetModeChar(), it->mask);
+ changelist.push_remove(this, it->mask);
}
}
}
@@ -74,8 +75,9 @@ void ListModeBase::DoRehash()
chanlimits.push_back(limit);
}
- if (chanlimits.empty())
- chanlimits.push_back(ListLimit("*", 64));
+ // Add the default entry. This is inserted last so if the user specifies a
+ // wildcard record in the config it will take precedence over this entry.
+ chanlimits.push_back(ListLimit("*", 64));
// Most of the time our settings are unchanged, so we can avoid iterating the chanlist
if (oldlimits == chanlimits)
@@ -191,7 +193,7 @@ ModeAction ListModeBase::OnModeChange(User* source, User*, Channel* channel, std
{
if (parameter == it->mask)
{
- cd->list.erase(it);
+ stdalgo::vector::swaperase(cd->list, it);
return MODEACTION_ALLOW;
}
}
@@ -210,7 +212,7 @@ bool ListModeBase::ValidateParam(User*, Channel*, std::string&)
void ListModeBase::TellListTooLong(User* source, Channel* channel, std::string& parameter)
{
- source->WriteNumeric(ERR_BANLISTFULL, "%s %s :Channel ban list is full", channel->name.c_str(), parameter.c_str());
+ source->WriteNumeric(ERR_BANLISTFULL, channel->name, parameter, "Channel ban list is full");
}
void ListModeBase::TellAlreadyOnList(User*, Channel*, std::string&)
diff --git a/src/logger.cpp b/src/logger.cpp
index 78809c555..e3e956325 100644
--- a/src/logger.cpp
+++ b/src/logger.cpp
@@ -21,7 +21,6 @@
#include "inspircd.h"
-#include "filelogger.h"
/*
* Suggested implementation...
@@ -51,7 +50,7 @@
*/
const char LogStream::LogHeader[] =
- "Log started for " INSPIRCD_VERSION " (" INSPIRCD_REVISION ", " MODULE_INIT_STR ")"
+ "Log started for " INSPIRCD_VERSION " (" MODULE_INIT_STR ")"
" - compiled on " INSPIRCD_SYSTEM;
LogManager::LogManager()
@@ -121,7 +120,7 @@ void LogManager::OpenFileLogs()
struct tm *mytime = gmtime(&time);
strftime(realtarget, sizeof(realtarget), target.c_str(), mytime);
FILE* f = fopen(realtarget, "a");
- fw = new FileWriter(f);
+ fw = new FileWriter(f, static_cast<unsigned int>(tag->getInt("flush", 20, 1, INT_MAX)));
logmap.insert(std::make_pair(target, fw));
}
else
@@ -208,10 +207,9 @@ void LogManager::DelLogStream(LogStream* l)
{
for (std::map<std::string, std::vector<LogStream*> >::iterator i = LogStreams.begin(); i != LogStreams.end(); ++i)
{
- std::vector<LogStream*>::iterator it;
- while ((it = std::find(i->second.begin(), i->second.end(), l)) != i->second.end())
+ while (stdalgo::erase(i->second, l))
{
- i->second.erase(it);
+ // Keep erasing while it exists
}
}
@@ -237,11 +235,8 @@ bool LogManager::DelLogType(const std::string &type, LogStream *l)
if (i != LogStreams.end())
{
- std::vector<LogStream *>::iterator it = std::find(i->second.begin(), i->second.end(), l);
-
- if (it != i->second.end())
+ if (stdalgo::erase(i->second, l))
{
- i->second.erase(it);
if (i->second.size() == 0)
{
LogStreams.erase(i);
@@ -293,7 +288,7 @@ void LogManager::Log(const std::string &type, LogLevel loglevel, const std::stri
for (std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.begin(); gi != GlobalLogStreams.end(); ++gi)
{
- if (std::find(gi->second.begin(), gi->second.end(), type) != gi->second.end())
+ if (stdalgo::isin(gi->second, type))
{
continue;
}
@@ -314,8 +309,10 @@ void LogManager::Log(const std::string &type, LogLevel loglevel, const std::stri
}
-FileWriter::FileWriter(FILE* logfile)
-: log(logfile), writeops(0)
+FileWriter::FileWriter(FILE* logfile, unsigned int flushcount)
+ : log(logfile)
+ , flush(flushcount)
+ , writeops(0)
{
}
@@ -327,7 +324,7 @@ void FileWriter::WriteLogLine(const std::string &line)
// throw CoreException("FileWriter::WriteLogLine called with a closed logfile");
fputs(line.c_str(), log);
- if (++writeops % 20 == 0)
+ if (++writeops % flush == 0)
{
fflush(log);
}
diff --git a/src/mode.cpp b/src/mode.cpp
index 9d24160f6..22173c189 100644
--- a/src/mode.cpp
+++ b/src/mode.cpp
@@ -27,7 +27,7 @@
#include "builtinmodes.h"
ModeHandler::ModeHandler(Module* Creator, const std::string& Name, char modeletter, ParamSpec Params, ModeType type, Class mclass)
- : ServiceProvider(Creator, Name, SERVICE_MODE), modeid(ModeParser::MODEID_MAX), m_paramtype(TR_TEXT),
+ : ServiceProvider(Creator, Name, SERVICE_MODE), modeid(ModeParser::MODEID_MAX),
parameters_taken(Params), mode(modeletter), oper(false),
list(false), m_type(type), type_id(mclass), levelrequired(HALFOP_VALUE)
{
@@ -35,7 +35,7 @@ ModeHandler::ModeHandler(Module* Creator, const std::string& Name, char modelett
CullResult ModeHandler::cull()
{
- if (ServerInstance->Modes)
+ if (ServerInstance)
ServerInstance->Modes->DelMode(this);
return classbase::cull();
}
@@ -44,21 +44,21 @@ ModeHandler::~ModeHandler()
{
}
-int ModeHandler::GetNumParams(bool adding)
+bool ModeHandler::NeedsParam(bool adding) const
{
switch (parameters_taken)
{
case PARAM_ALWAYS:
- return 1;
+ return true;
case PARAM_SETONLY:
- return adding ? 1 : 0;
+ return adding;
case PARAM_NONE:
break;
}
- return 0;
+ return false;
}
-std::string ModeHandler::GetUserParameter(User* user)
+std::string ModeHandler::GetUserParameter(const User* user) const
{
return "";
}
@@ -90,6 +90,12 @@ bool ModeHandler::ResolveModeConflict(std::string& theirs, const std::string& ou
return (theirs < ours);
}
+void ModeHandler::RegisterService()
+{
+ ServerInstance->Modes.AddMode(this);
+ ServerInstance->Modules.AddReferent((GetModeType() == MODETYPE_CHANNEL ? "mode/" : "umode/") + name, this);
+}
+
ModeAction SimpleUserModeHandler::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
{
/* We're either trying to add a mode we already have or
@@ -138,11 +144,6 @@ ModeWatcher::~ModeWatcher()
ServerInstance->Modes->DelModeWatcher(this);
}
-ModeType ModeWatcher::GetModeType()
-{
- return m_type;
-}
-
bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool)
{
return true;
@@ -152,42 +153,11 @@ void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool)
{
}
-void ModeParser::DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text)
-{
- if (targetchannel)
- {
- /* Display channel's current mode string */
- user->WriteNumeric(RPL_CHANNELMODEIS, "%s +%s", targetchannel->name.c_str(), targetchannel->ChanModes(targetchannel->HasUser(user)));
- user->WriteNumeric(RPL_CHANNELCREATED, "%s %lu", targetchannel->name.c_str(), (unsigned long)targetchannel->age);
- return;
- }
- else
- {
- if (targetuser == user || user->HasPrivPermission("users/auspex"))
- {
- /* Display user's current mode string */
- user->WriteNumeric(RPL_UMODEIS, ":+%s", targetuser->FormatModes());
- if ((targetuser->IsOper()))
- {
- ModeHandler* snomask = FindMode('s', MODETYPE_USER);
- user->WriteNumeric(RPL_SNOMASKIS, "%s :Server notice mask", snomask->GetUserParameter(user).c_str());
- }
- return;
- }
- else
- {
- user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't view modes for other users");
- return;
- }
- }
-}
-
-PrefixMode::PrefixMode(Module* Creator, const std::string& Name, char ModeLetter)
+PrefixMode::PrefixMode(Module* Creator, const std::string& Name, char ModeLetter, unsigned int Rank, char PrefixChar)
: ModeHandler(Creator, Name, ModeLetter, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_PREFIX)
- , prefix(0), prefixrank(0)
+ , prefix(PrefixChar), prefixrank(Rank)
{
list = true;
- m_paramtype = TR_NICK;
}
ModeAction PrefixMode::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
@@ -200,7 +170,7 @@ ModeAction PrefixMode::OnModeChange(User* source, User*, Channel* chan, std::str
if (!target)
{
- source->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameter.c_str());
+ source->WriteNumeric(Numerics::NoSuchNick(parameter));
return MODEACTION_DENY;
}
@@ -238,17 +208,18 @@ ModeAction ParamModeBase::OnModeChange(User* source, User*, Channel* chan, std::
return MODEACTION_ALLOW;
}
-ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool adding, const unsigned char modechar,
- std::string &parameter, bool SkipACL)
+ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, Modes::Change& mcitem, bool SkipACL)
{
ModeType type = chan ? MODETYPE_CHANNEL : MODETYPE_USER;
- ModeHandler *mh = FindMode(modechar, type);
- int pcnt = mh->GetNumParams(adding);
+ ModeHandler* mh = mcitem.mh;
+ bool adding = mcitem.adding;
+ const bool needs_param = mh->NeedsParam(adding);
+ std::string& parameter = mcitem.param;
// crop mode parameter size to 250 characters
if (parameter.length() > 250 && adding)
- parameter = parameter.substr(0, 250);
+ parameter.erase(250);
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mh, parameter, adding));
@@ -256,6 +227,8 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
if (IS_LOCAL(user) && (MOD_RESULT == MOD_RES_DENY))
return MODEACTION_DENY;
+ const char modechar = mh->GetModeChar();
+
if (chan && !SkipACL && (MOD_RESULT != MOD_RES_ALLOW))
{
MOD_RESULT = mh->AccessCheck(user, chan, parameter, adding);
@@ -273,11 +246,12 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
unsigned int ourrank = chan->GetPrefixValue(user);
if (ourrank < neededrank)
{
- PrefixMode* neededmh = NULL;
- for(char c='A'; c <= 'z'; c++)
+ const PrefixMode* neededmh = NULL;
+ const PrefixModeList& prefixmodes = GetPrefixModes();
+ for (PrefixModeList::const_iterator i = prefixmodes.begin(); i != prefixmodes.end(); ++i)
{
- PrefixMode* privmh = FindPrefixMode(c);
- if (privmh && privmh->GetPrefixRank() >= neededrank)
+ const PrefixMode* const privmh = *i;
+ if (privmh->GetPrefixRank() >= neededrank)
{
// this mode is sufficient to allow this action
if (!neededmh || privmh->GetPrefixRank() < neededmh->GetPrefixRank())
@@ -285,19 +259,18 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
}
}
if (neededmh)
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must have channel %s access or above to %sset channel mode %c",
- chan->name.c_str(), neededmh->name.c_str(), adding ? "" : "un", modechar);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, InspIRCd::Format("You must have channel %s access or above to %sset channel mode %c",
+ neededmh->name.c_str(), adding ? "" : "un", modechar));
else
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You cannot %sset channel mode %c",
- chan->name.c_str(), adding ? "" : "un", modechar);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, InspIRCd::Format("You cannot %sset channel mode %c", (adding ? "" : "un"), modechar));
return MODEACTION_DENY;
}
}
}
// Ask mode watchers whether this mode change is OK
- std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mh->name);
- for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
+ std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mh->name);
+ for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
{
ModeWatcher* mw = i->second;
if (mw->GetModeType() == type)
@@ -306,7 +279,7 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
return MODEACTION_DENY;
// A module whacked the parameter completely, and there was one. Abort.
- if (pcnt && parameter.empty())
+ if ((needs_param) && (parameter.empty()))
return MODEACTION_DENY;
}
}
@@ -316,24 +289,24 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
char* disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes;
if (disabled[modechar - 'A'])
{
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - %s mode %c has been locked by the administrator",
- type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+ user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - %s mode %c has been locked by the administrator",
+ type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
return MODEACTION_DENY;
}
}
- if (adding && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(modechar, type))
+ if ((adding) && (IS_LOCAL(user)) && (mh->NeedsOper()) && (!user->HasModePermission(mh)))
{
/* It's an oper only mode, and they don't have access to it. */
if (user->IsOper())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper type %s does not have access to set %s mode %c",
- user->oper->name.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+ user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper type %s does not have access to set %s mode %c",
+ user->oper->name.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
}
else
{
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Only operators may set %s mode %c",
- type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+ user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Only operators may set %s mode %c",
+ type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
}
return MODEACTION_DENY;
}
@@ -341,14 +314,14 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
/* Call the handler for the mode */
ModeAction ma = mh->OnModeChange(user, targetuser, chan, parameter, adding);
- if (pcnt && parameter.empty())
+ if ((needs_param) && (parameter.empty()))
return MODEACTION_DENY;
if (ma != MODEACTION_ALLOW)
return ma;
itpair = modewatchermap.equal_range(mh->name);
- for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
+ for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
{
ModeWatcher* mw = i->second;
if (mw->GetModeType() == type)
@@ -358,61 +331,15 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
return MODEACTION_ALLOW;
}
-void ModeParser::Process(const std::vector<std::string>& parameters, User* user, ModeProcessFlag flags)
+void ModeParser::ModeParamsToChangeList(User* user, ModeType type, const std::vector<std::string>& parameters, Modes::ChangeList& changelist, unsigned int beginindex, unsigned int endindex)
{
- const std::string& target = parameters[0];
- Channel* targetchannel = ServerInstance->FindChan(target);
- User* targetuser = NULL;
- if (!targetchannel)
- {
- if (IS_LOCAL(user))
- targetuser = ServerInstance->FindNickOnly(target);
- else
- targetuser = ServerInstance->FindNick(target);
- }
- ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
+ if (endindex > parameters.size())
+ endindex = parameters.size();
- LastParse.clear();
- LastParseParams.clear();
- LastParseTranslate.clear();
-
- if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser))))
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", target.c_str());
- return;
- }
- if (parameters.size() == 1)
- {
- this->DisplayCurrentModes(user, targetuser, targetchannel, target.c_str());
- return;
- }
-
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, parameters));
-
- bool SkipAccessChecks = false;
-
- if (!IS_LOCAL(user) || MOD_RESULT == MOD_RES_ALLOW)
- SkipAccessChecks = true;
- else if (MOD_RESULT == MOD_RES_DENY)
- return;
-
- if (targetuser && !SkipAccessChecks && user != targetuser)
- {
- user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't change mode for other users");
- return;
- }
-
- std::string mode_sequence = parameters[1];
-
- std::string output_mode;
- std::ostringstream output_parameters;
- LastParseParams.push_back(output_mode);
- LastParseTranslate.push_back(TR_TEXT);
+ const std::string& mode_sequence = parameters[beginindex];
bool adding = true;
- char output_pm = '\0'; // current output state, '+' or '-'
- unsigned int param_at = 2;
+ unsigned int param_at = beginindex+1;
for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
{
@@ -427,129 +354,151 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User* user,
if (!mh)
{
/* No mode handler? Unknown mode character then. */
- user->WriteNumeric(type == MODETYPE_CHANNEL ? ERR_UNKNOWNMODE : ERR_UNKNOWNSNOMASK, "%c :is unknown mode char to me", modechar);
+ user->WriteNumeric(type == MODETYPE_CHANNEL ? ERR_UNKNOWNMODE : ERR_UNKNOWNSNOMASK, modechar, "is unknown mode char to me");
continue;
}
std::string parameter;
- int pcnt = mh->GetNumParams(adding);
- if (pcnt && param_at == parameters.size())
- {
- /* No parameter, continue to the next mode */
- mh->OnParameterMissing(user, targetuser, targetchannel);
- continue;
- }
- else if (pcnt)
- {
+ if ((mh->NeedsParam(adding)) && (param_at < endindex))
parameter = parameters[param_at++];
- /* Make sure the user isn't trying to slip in an invalid parameter */
- if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos))
+
+ changelist.push(mh, adding, parameter);
+ }
+}
+
+static bool IsModeParamValid(User* user, Channel* targetchannel, User* targetuser, const Modes::Change& item)
+{
+ // An empty parameter is never acceptable
+ if (item.param.empty())
+ {
+ item.mh->OnParameterMissing(user, targetuser, targetchannel);
+ return false;
+ }
+
+ // The parameter cannot begin with a ':' character or contain a space
+ if ((item.param[0] == ':') || (item.param.find(' ') != std::string::npos))
+ return false;
+
+ return true;
+}
+
+// Returns true if we should apply a merged mode, false if we should skip it
+static bool ShouldApplyMergedMode(Channel* chan, Modes::Change& item)
+{
+ ModeHandler* mh = item.mh;
+ if ((!chan) || (!chan->IsModeSet(mh)) || (mh->IsListMode()))
+ // Mode not set here or merge is not applicable, apply the incoming mode
+ return true;
+
+ // Mode handler decides
+ std::string ours = chan->GetModeParameter(mh);
+ return mh->ResolveModeConflict(item.param, ours, chan);
+}
+
+void ModeParser::Process(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags)
+{
+ // Call ProcessSingle until the entire list is processed, but at least once to ensure
+ // LastParse and LastChangeList are cleared
+ unsigned int processed = 0;
+ do
+ {
+ unsigned int n = ProcessSingle(user, targetchannel, targetuser, changelist, flags, processed);
+ processed += n;
+ }
+ while (processed < changelist.size());
+}
+
+unsigned int ModeParser::ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags, unsigned int beginindex)
+{
+ LastParse.clear();
+ LastChangeList.clear();
+
+ unsigned int modes_processed = 0;
+ std::string output_mode;
+ std::string output_parameters;
+
+ char output_pm = '\0'; // current output state, '+' or '-'
+ Modes::ChangeList::List& list = changelist.getlist();
+ for (Modes::ChangeList::List::iterator i = list.begin()+beginindex; i != list.end(); ++i)
+ {
+ modes_processed++;
+
+ Modes::Change& item = *i;
+ ModeHandler* mh = item.mh;
+
+ // If the mode is supposed to have a parameter then we first take a look at item.param
+ // and, if we were asked to, also handle mode merges now
+ if (mh->NeedsParam(item.adding))
+ {
+ // Skip the mode if the parameter does not pass basic validation
+ if (!IsModeParamValid(user, targetchannel, targetuser, item))
+ continue;
+
+ // If this is a merge and we won we don't apply this mode
+ if ((flags & MODE_MERGE) && (!ShouldApplyMergedMode(targetchannel, item)))
continue;
- if ((flags & MODE_MERGE) && targetchannel && targetchannel->IsModeSet(mh) && !mh->IsListMode())
- {
- std::string ours = targetchannel->GetModeParameter(mh);
- if (!mh->ResolveModeConflict(parameter, ours, targetchannel))
- /* we won the mode merge, don't apply this mode */
- continue;
- }
}
- ModeAction ma = TryMode(user, targetuser, targetchannel, adding, modechar, parameter, SkipAccessChecks);
+ ModeAction ma = TryMode(user, targetuser, targetchannel, item, (!(flags & MODE_CHECKACCESS)));
if (ma != MODEACTION_ALLOW)
continue;
- char needed_pm = adding ? '+' : '-';
+ char needed_pm = item.adding ? '+' : '-';
if (needed_pm != output_pm)
{
output_pm = needed_pm;
output_mode.append(1, output_pm);
}
- output_mode.append(1, modechar);
+ output_mode.push_back(mh->GetModeChar());
- if (pcnt)
+ if (!item.param.empty())
{
- output_parameters << " " << parameter;
- LastParseParams.push_back(parameter);
- LastParseTranslate.push_back(mh->GetTranslateType());
+ output_parameters.push_back(' ');
+ output_parameters.append(item.param);
}
+ LastChangeList.push(mh, item.adding, item.param);
- if ( (output_mode.length() + output_parameters.str().length() > 450)
+ if ((output_mode.length() + output_parameters.length() > 450)
|| (output_mode.length() > 100)
- || (LastParseParams.size() > ServerInstance->Config->Limits.MaxModes))
+ || (LastChangeList.size() >= ServerInstance->Config->Limits.MaxModes))
{
/* mode sequence is getting too long */
break;
}
}
- LastParseParams[0] = output_mode;
-
if (!output_mode.empty())
{
LastParse = targetchannel ? targetchannel->name : targetuser->nick;
LastParse.append(" ");
LastParse.append(output_mode);
- LastParse.append(output_parameters.str());
-
- if (!(flags & MODE_LOCALONLY))
- ServerInstance->PI->SendMode(user, targetuser, targetchannel, LastParseParams, LastParseTranslate);
+ LastParse.append(output_parameters);
if (targetchannel)
targetchannel->WriteChannel(user, "MODE " + LastParse);
else
targetuser->WriteFrom(user, "MODE " + LastParse);
- FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastParseParams, LastParseTranslate));
- }
- else if (targetchannel && parameters.size() == 2)
- {
- /* Special case for displaying the list for listmodes,
- * e.g. MODE #chan b, or MODE #chan +b without a parameter
- */
- this->DisplayListModes(user, targetchannel, mode_sequence);
+ FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastChangeList, flags, output_mode));
}
+
+ return modes_processed;
}
-void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_sequence)
+void ModeParser::ShowListModeList(User* user, Channel* chan, ModeHandler* mh)
{
- seq++;
-
- for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
{
- unsigned char mletter = *letter;
- if (mletter == '+')
- continue;
-
- /* Ensure the user doesnt request the same mode twice,
- * so they cant flood themselves off out of idiocy.
- */
- if (sent[mletter] == seq)
- continue;
-
- sent[mletter] = seq;
-
- ModeHandler *mh = this->FindMode(mletter, MODETYPE_CHANNEL);
-
- if (!mh || !mh->IsListMode())
- return;
-
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mh, "", true));
if (MOD_RESULT == MOD_RES_DENY)
- continue;
+ return;
bool display = true;
- if (!user->HasPrivPermission("channels/auspex") && ServerInstance->Config->HideModeLists[mletter] && (chan->GetPrefixValue(user) < HALFOP_VALUE))
- {
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You do not have access to view the +%c list",
- chan->name.c_str(), mletter);
- display = false;
- }
// Ask mode watchers whether it's OK to show the list
- std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mh->name);
- for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
+ std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mh->name);
+ for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
{
ModeWatcher* mw = i->second;
if (mw->GetModeType() == MODETYPE_CHANNEL)
@@ -712,16 +661,9 @@ bool ModeParser::DelMode(ModeHandler* mh)
Channel* chan = i->second;
++i;
- irc::modestacker stack(false);
- mh->RemoveMode(chan, stack);
-
- std::vector<std::string> stackresult;
- stackresult.push_back(chan->name);
- while (stack.GetStackedLine(stackresult))
- {
- this->Process(stackresult, ServerInstance->FakeClient, MODE_LOCALONLY);
- stackresult.erase(stackresult.begin() + 1, stackresult.end());
- }
+ Modes::ChangeList changelist;
+ mh->RemoveMode(chan, changelist);
+ this->Process(ServerInstance->FakeClient, chan, NULL, changelist, MODE_LOCALONLY);
}
}
break;
@@ -773,7 +715,7 @@ std::string ModeParser::CreateModeList(ModeType mt, bool needparam)
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
ModeHandler* mh = modehandlers[mt][mode-65];
- if ((mh) && ((!needparam) || (mh->GetNumParams(true))))
+ if ((mh) && ((!needparam) || (mh->NeedsParam(true))))
modestr.push_back(mode);
}
@@ -810,7 +752,7 @@ std::string ModeParser::GiveModeList(ModeType mt)
/* One parameter when adding */
if (mh)
{
- if (mh->GetNumParams(true))
+ if (mh->NeedsParam(true))
{
PrefixMode* pm = mh->IsPrefixMode();
if ((mh->IsListMode()) && ((!pm) || (pm->GetPrefix() == 0)))
@@ -820,7 +762,7 @@ std::string ModeParser::GiveModeList(ModeType mt)
else
{
/* ... and one parameter when removing */
- if (mh->GetNumParams(false))
+ if (mh->NeedsParam(false))
{
/* But not a list mode */
if (!pm)
@@ -845,24 +787,33 @@ std::string ModeParser::GiveModeList(ModeType mt)
return type1 + "," + type2 + "," + type3 + "," + type4;
}
+struct PrefixModeSorter
+{
+ bool operator()(PrefixMode* lhs, PrefixMode* rhs)
+ {
+ return lhs->GetPrefixRank() < rhs->GetPrefixRank();
+ }
+};
+
std::string ModeParser::BuildPrefixes(bool lettersAndModes)
{
std::string mletters;
std::string mprefixes;
- std::map<int,std::pair<char,char> > prefixes;
+ std::vector<PrefixMode*> prefixes;
const PrefixModeList& list = GetPrefixModes();
for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
{
PrefixMode* pm = *i;
if (pm->GetPrefix())
- prefixes[pm->GetPrefixRank()] = std::make_pair(pm->GetPrefix(), pm->GetModeChar());
+ prefixes.push_back(pm);
}
- for(std::map<int,std::pair<char,char> >::reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); n++)
+ std::sort(prefixes.begin(), prefixes.end(), PrefixModeSorter());
+ for (std::vector<PrefixMode*>::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
{
- mletters = mletters + n->second.first;
- mprefixes = mprefixes + n->second.second;
+ mletters += (*n)->GetPrefix();
+ mprefixes += (*n)->GetModeChar();
}
return lettersAndModes ? "(" + mprefixes + ")" + mletters : mletters;
@@ -875,8 +826,8 @@ void ModeParser::AddModeWatcher(ModeWatcher* mw)
bool ModeParser::DelModeWatcher(ModeWatcher* mw)
{
- std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mw->GetModeName());
- for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
+ std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mw->GetModeName());
+ for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
{
if (i->second == mw)
{
@@ -893,45 +844,43 @@ void ModeHandler::RemoveMode(User* user)
// Remove the mode if it's set on the user
if (user->IsModeSet(this->GetModeChar()))
{
- std::vector<std::string> parameters;
- parameters.push_back(user->nick);
- parameters.push_back("-");
- parameters[1].push_back(this->GetModeChar());
- ServerInstance->Modes->Process(parameters, ServerInstance->FakeClient, ModeParser::MODE_LOCALONLY);
+ Modes::ChangeList changelist;
+ changelist.push_remove(this);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, NULL, user, changelist, ModeParser::MODE_LOCALONLY);
}
}
-void ModeHandler::RemoveMode(Channel* channel, irc::modestacker& stack)
+void ModeHandler::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
{
if (channel->IsModeSet(this))
{
- if (this->GetNumParams(false))
+ if (this->NeedsParam(false))
// Removing this mode requires a parameter
- stack.Push(this->GetModeChar(), channel->GetModeParameter(this));
+ changelist.push_remove(this, channel->GetModeParameter(this));
else
- stack.Push(this->GetModeChar());
+ changelist.push_remove(this);
}
}
-void PrefixMode::RemoveMode(Channel* chan, irc::modestacker& stack)
+void PrefixMode::RemoveMode(Channel* chan, Modes::ChangeList& changelist)
{
- const UserMembList* userlist = chan->GetUsers();
- for (UserMembCIter i = userlist->begin(); i != userlist->end(); ++i)
+ const Channel::MemberMap& userlist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
{
- if (i->second->hasMode(this->GetModeChar()))
- stack.Push(this->GetModeChar(), i->first->nick);
+ if (i->second->HasMode(this))
+ changelist.push_remove(this, i->first->nick);
}
}
struct builtin_modes
{
- ModeChannelSecret s;
- ModeChannelPrivate p;
- ModeChannelModerated m;
- ModeChannelTopicOps t;
+ SimpleChannelModeHandler s;
+ SimpleChannelModeHandler p;
+ SimpleChannelModeHandler m;
+ SimpleChannelModeHandler t;
- ModeChannelNoExternal n;
- ModeChannelInviteOnly i;
+ SimpleChannelModeHandler n;
+ SimpleChannelModeHandler i;
ModeChannelKey k;
ModeChannelLimit l;
@@ -939,10 +888,21 @@ struct builtin_modes
ModeChannelOp o;
ModeChannelVoice v;
- ModeUserInvisible ui;
+ SimpleUserModeHandler ui;
ModeUserOperator uo;
ModeUserServerNoticeMask us;
+ builtin_modes()
+ : s(NULL, "secret", 's')
+ , p(NULL, "private", 'p')
+ , m(NULL, "moderated", 'm')
+ , t(NULL, "topiclock", 't')
+ , n(NULL, "noextmsg", 'n')
+ , i(NULL, "inviteonly", 'i')
+ , ui(NULL, "invisible", 'i')
+ {
+ }
+
void init()
{
ServiceProvider* modes[] = { &s, &p, &m, &t, &n, &i, &k, &l, &b, &o, &v,
@@ -964,9 +924,6 @@ ModeParser::ModeParser()
/* Clear mode handler list */
memset(modehandlers, 0, sizeof(modehandlers));
memset(modehandlersbyid, 0, sizeof(modehandlersbyid));
-
- seq = 0;
- memset(&sent, 0, sizeof(sent));
}
ModeParser::~ModeParser()
diff --git a/src/modes/cmode_k.cpp b/src/modes/cmode_k.cpp
index e14f93a77..980b3215a 100644
--- a/src/modes/cmode_k.cpp
+++ b/src/modes/cmode_k.cpp
@@ -21,9 +21,6 @@
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
#include "builtinmodes.h"
ModeChannelKey::ModeChannelKey()
@@ -55,7 +52,8 @@ ModeAction ModeChannelKey::OnModeChange(User* source, User*, Channel* channel, s
channel->SetMode(this, adding);
if (adding)
{
- parameter = parameter.substr(0, 32);
+ if (parameter.length() > maxkeylen)
+ parameter.erase(maxkeylen);
ext.set(channel, parameter);
}
else
diff --git a/src/modes/cmode_l.cpp b/src/modes/cmode_l.cpp
index 128854b50..d61b2597b 100644
--- a/src/modes/cmode_l.cpp
+++ b/src/modes/cmode_l.cpp
@@ -20,9 +20,6 @@
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
#include "builtinmodes.h"
ModeChannelLimit::ModeChannelLimit()
@@ -38,7 +35,11 @@ bool ModeChannelLimit::ResolveModeConflict(std::string &their_param, const std::
ModeAction ModeChannelLimit::OnSet(User* user, Channel* chan, std::string& parameter)
{
- ext.set(chan, ConvToInt(parameter));
+ int limit = ConvToInt(parameter);
+ if (limit < 0)
+ return MODEACTION_DENY;
+
+ ext.set(chan, limit);
return MODEACTION_ALLOW;
}
diff --git a/src/modes/umode_o.cpp b/src/modes/umode_o.cpp
index affd6b50c..6e9517a4f 100644
--- a/src/modes/umode_o.cpp
+++ b/src/modes/umode_o.cpp
@@ -19,9 +19,6 @@
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
#include "builtinmodes.h"
ModeUserOperator::ModeUserOperator() : ModeHandler(NULL, "oper", 'o', PARAM_NONE, MODETYPE_USER)
diff --git a/src/modes/umode_s.cpp b/src/modes/umode_s.cpp
index b355cb824..ffad21662 100644
--- a/src/modes/umode_s.cpp
+++ b/src/modes/umode_s.cpp
@@ -20,9 +20,6 @@
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
#include "builtinmodes.h"
ModeUserServerNoticeMask::ModeUserServerNoticeMask() : ModeHandler(NULL, "snomask", 's', PARAM_SETONLY, MODETYPE_USER)
@@ -53,7 +50,7 @@ ModeAction ModeUserServerNoticeMask::OnModeChange(User* source, User* dest, Chan
return MODEACTION_DENY;
}
-std::string ModeUserServerNoticeMask::GetUserParameter(User* user)
+std::string ModeUserServerNoticeMask::GetUserParameter(const User* user) const
{
std::string ret;
if (!user->IsModeSet(this))
@@ -102,7 +99,7 @@ std::string ModeUserServerNoticeMask::ProcessNoticeMasks(User* user, const std::
{
if (!ServerInstance->SNO->IsSnomaskUsable(*i))
{
- user->WriteNumeric(ERR_UNKNOWNSNOMASK, "%c :is unknown snomask char to me", *i);
+ user->WriteNumeric(ERR_UNKNOWNSNOMASK, *i, "is unknown snomask char to me");
continue;
}
}
diff --git a/src/modmanager_dynamic.cpp b/src/modmanager_dynamic.cpp
index afb690207..9e940cc32 100644
--- a/src/modmanager_dynamic.cpp
+++ b/src/modmanager_dynamic.cpp
@@ -18,10 +18,6 @@
#include "inspircd.h"
-#include "xline.h"
-#include "socket.h"
-#include "socketengine.h"
-#include "command_parse.h"
#include "exitcodes.h"
#include <iostream>
@@ -29,14 +25,18 @@
#include <dirent.h>
#endif
-#ifndef PURE_STATIC
+#ifndef INSPIRCD_STATIC
-bool ModuleManager::Load(const std::string& filename, bool defer)
+bool ModuleManager::Load(const std::string& modname, bool defer)
{
/* Don't allow people to specify paths for modules, it doesn't work as expected */
- if (filename.find('/') != std::string::npos)
+ if (modname.find('/') != std::string::npos)
+ {
+ LastModuleError = "You can't load modules with a path: " + modname;
return false;
+ }
+ const std::string filename = ExpandModName(modname);
const std::string moduleFile = ServerInstance->Config->Paths.PrependModule(filename);
if (!FileSystem::FileExists(moduleFile))
diff --git a/src/modmanager_static.cpp b/src/modmanager_static.cpp
index ac127b703..5c04a7680 100644
--- a/src/modmanager_static.cpp
+++ b/src/modmanager_static.cpp
@@ -23,7 +23,7 @@
#include "exitcodes.h"
#include <iostream>
-#ifdef PURE_STATIC
+#ifdef INSPIRCD_STATIC
typedef std::map<std::string, AllModuleList*> modmap;
static std::vector<AllCommandList::fn>* cmdlist = NULL;
@@ -80,8 +80,9 @@ class AllModule : public Module
MODULE_INIT(AllModule)
-bool ModuleManager::Load(const std::string& name, bool defer)
+bool ModuleManager::Load(const std::string& inputname, bool defer)
{
+ const std::string name = ExpandModName(inputname);
modmap::iterator it = modlist->find(name);
if (it == modlist->end())
return false;
diff --git a/src/modules.cpp b/src/modules.cpp
index 78b00e95e..5c5e5c5c0 100644
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@ -26,25 +26,19 @@
#include <iostream>
#include "inspircd.h"
-#include "xline.h"
-#include "socket.h"
-#include "socketengine.h"
-#include "command_parse.h"
#include "exitcodes.h"
#ifndef _WIN32
#include <dirent.h>
#endif
-static intrusive_list<dynamic_reference_base>* dynrefs = NULL;
-static bool dynref_init_complete = false;
+static insp::intrusive_list<dynamic_reference_base>* dynrefs = NULL;
void dynamic_reference_base::reset_all()
{
- dynref_init_complete = true;
if (!dynrefs)
return;
- for (intrusive_list<dynamic_reference_base>::iterator i = dynrefs->begin(); i != dynrefs->end(); ++i)
+ for (insp::intrusive_list<dynamic_reference_base>::iterator i = dynrefs->begin(); i != dynrefs->end(); ++i)
(*i)->resolve();
}
@@ -58,13 +52,6 @@ Version::Version(const std::string &desc, int flags, const std::string& linkdata
{
}
-Event::Event(Module* src, const std::string &eventid) : source(src), id(eventid) { }
-
-void Event::Send()
-{
- FOREACH_MOD(OnEvent, (*this));
-}
-
// These declarations define the behavours of the base class Module (which does nothing at all)
Module::Module() { }
@@ -92,16 +79,15 @@ void Module::OnUserPart(Membership*, std::string&, CUList&) { DetachEvent(I_OnU
void Module::OnPreRehash(User*, const std::string&) { DetachEvent(I_OnPreRehash); }
void Module::OnModuleRehash(User*, const std::string&) { DetachEvent(I_OnModuleRehash); }
ModResult Module::OnUserPreJoin(LocalUser*, Channel*, const std::string&, std::string&, const std::string&) { DetachEvent(I_OnUserPreJoin); return MOD_RES_PASSTHRU; }
-void Module::OnMode(User*, User*, Channel*, const std::vector<std::string>&, const std::vector<TranslateType>&) { DetachEvent(I_OnMode); }
+void Module::OnMode(User*, User*, Channel*, const Modes::ChangeList&, ModeParser::ModeProcessFlag, const std::string&) { DetachEvent(I_OnMode); }
void Module::OnOper(User*, const std::string&) { DetachEvent(I_OnOper); }
void Module::OnPostOper(User*, const std::string&, const std::string &) { DetachEvent(I_OnPostOper); }
void Module::OnInfo(User*) { DetachEvent(I_OnInfo); }
-void Module::OnWhois(User*, User*) { DetachEvent(I_OnWhois); }
ModResult Module::OnUserPreInvite(User*, User*, Channel*, time_t) { DetachEvent(I_OnUserPreInvite); return MOD_RES_PASSTHRU; }
ModResult Module::OnUserPreMessage(User*, void*, int, std::string&, char, CUList&, MessageType) { DetachEvent(I_OnUserPreMessage); return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserPreNick(User*, const std::string&) { DetachEvent(I_OnUserPreNick); return MOD_RES_PASSTHRU; }
+ModResult Module::OnUserPreNick(LocalUser*, const std::string&) { DetachEvent(I_OnUserPreNick); return MOD_RES_PASSTHRU; }
void Module::OnUserPostNick(User*, const std::string&) { DetachEvent(I_OnUserPostNick); }
-ModResult Module::OnPreMode(User*, User*, Channel*, const std::vector<std::string>&) { DetachEvent(I_OnPreMode); return MOD_RES_PASSTHRU; }
+ModResult Module::OnPreMode(User*, User*, Channel*, Modes::ChangeList&) { DetachEvent(I_OnPreMode); return MOD_RES_PASSTHRU; }
void Module::On005Numeric(std::map<std::string, std::string>&) { DetachEvent(I_On005Numeric); }
ModResult Module::OnKill(User*, User*, const std::string&) { DetachEvent(I_OnKill); return MOD_RES_PASSTHRU; }
void Module::OnLoadModule(Module*) { DetachEvent(I_OnLoadModule); }
@@ -121,16 +107,14 @@ ModResult Module::OnCheckLimit(User*, Channel*) { DetachEvent(I_OnCheckLimit); r
ModResult Module::OnCheckChannelBan(User*, Channel*) { DetachEvent(I_OnCheckChannelBan); return MOD_RES_PASSTHRU; }
ModResult Module::OnCheckBan(User*, Channel*, const std::string&) { DetachEvent(I_OnCheckBan); return MOD_RES_PASSTHRU; }
ModResult Module::OnExtBanCheck(User*, Channel*, char) { DetachEvent(I_OnExtBanCheck); return MOD_RES_PASSTHRU; }
-ModResult Module::OnStats(char, User*, string_list&) { DetachEvent(I_OnStats); return MOD_RES_PASSTHRU; }
+ModResult Module::OnStats(Stats::Context&) { DetachEvent(I_OnStats); return MOD_RES_PASSTHRU; }
ModResult Module::OnChangeLocalUserHost(LocalUser*, const std::string&) { DetachEvent(I_OnChangeLocalUserHost); return MOD_RES_PASSTHRU; }
ModResult Module::OnChangeLocalUserGECOS(LocalUser*, const std::string&) { DetachEvent(I_OnChangeLocalUserGECOS); return MOD_RES_PASSTHRU; }
ModResult Module::OnPreTopicChange(User*, Channel*, const std::string&) { DetachEvent(I_OnPreTopicChange); return MOD_RES_PASSTHRU; }
-void Module::OnEvent(Event&) { DetachEvent(I_OnEvent); }
ModResult Module::OnPassCompare(Extensible* ex, const std::string &password, const std::string &input, const std::string& hashtype) { DetachEvent(I_OnPassCompare); return MOD_RES_PASSTHRU; }
-void Module::OnGlobalOper(User*) { DetachEvent(I_OnGlobalOper); }
void Module::OnPostConnect(User*) { DetachEvent(I_OnPostConnect); }
void Module::OnUserMessage(User*, void*, int, const std::string&, char, const CUList&, MessageType) { DetachEvent(I_OnUserMessage); }
-void Module::OnUserInvite(User*, User*, Channel*, time_t) { DetachEvent(I_OnUserInvite); }
+void Module::OnUserInvite(User*, User*, Channel*, time_t, unsigned int, CUList&) { DetachEvent(I_OnUserInvite); }
void Module::OnPostTopicChange(User*, Channel*, const std::string&) { DetachEvent(I_OnPostTopicChange); }
void Module::OnSyncUser(User*, ProtocolInterface::Server&) { DetachEvent(I_OnSyncUser); }
void Module::OnSyncChannel(Channel*, ProtocolInterface::Server&) { DetachEvent(I_OnSyncChannel); }
@@ -146,15 +130,14 @@ void Module::OnCleanup(int, void*) { }
ModResult Module::OnChannelPreDelete(Channel*) { DetachEvent(I_OnChannelPreDelete); return MOD_RES_PASSTHRU; }
void Module::OnChannelDelete(Channel*) { DetachEvent(I_OnChannelDelete); }
ModResult Module::OnSetAway(User*, const std::string &) { DetachEvent(I_OnSetAway); return MOD_RES_PASSTHRU; }
-ModResult Module::OnWhoisLine(User*, User*, int&, std::string&) { DetachEvent(I_OnWhoisLine); return MOD_RES_PASSTHRU; }
void Module::OnBuildNeighborList(User*, IncludeChanList&, std::map<User*,bool>&) { DetachEvent(I_OnBuildNeighborList); }
void Module::OnGarbageCollect() { DetachEvent(I_OnGarbageCollect); }
ModResult Module::OnSetConnectClass(LocalUser* user, ConnectClass* myclass) { DetachEvent(I_OnSetConnectClass); return MOD_RES_PASSTHRU; }
void Module::OnText(User*, void*, int, const std::string&, char, CUList&) { DetachEvent(I_OnText); }
ModResult Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { DetachEvent(I_OnNamesListItem); return MOD_RES_PASSTHRU; }
-ModResult Module::OnNumeric(User*, unsigned int, const std::string&) { DetachEvent(I_OnNumeric); return MOD_RES_PASSTHRU; }
+ModResult Module::OnNumeric(User*, const Numeric::Numeric&) { DetachEvent(I_OnNumeric); return MOD_RES_PASSTHRU; }
ModResult Module::OnAcceptConnection(int, ListenSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { DetachEvent(I_OnAcceptConnection); return MOD_RES_PASSTHRU; }
-void Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, Membership*, std::string&) { DetachEvent(I_OnSendWhoLine); }
+ModResult Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, Membership*, Numeric::Numeric&) { DetachEvent(I_OnSendWhoLine); return MOD_RES_PASSTHRU; }
void Module::OnSetUserIP(LocalUser*) { DetachEvent(I_OnSetUserIP); }
#ifdef INSPIRCD_ENABLE_TESTSUITE
@@ -171,12 +154,7 @@ ServiceProvider::ServiceProvider(Module* Creator, const std::string& Name, Servi
void ServiceProvider::DisableAutoRegister()
{
if ((ServerInstance) && (ServerInstance->Modules->NewServices))
- {
- ModuleManager::ServiceList& list = *ServerInstance->Modules->NewServices;
- ModuleManager::ServiceList::iterator it = std::find(list.begin(), list.end(), this);
- if (it != list.end())
- list.erase(it);
- }
+ stdalgo::erase(*ServerInstance->Modules->NewServices, this);
}
ModuleManager::ModuleManager()
@@ -189,7 +167,7 @@ ModuleManager::~ModuleManager()
bool ModuleManager::Attach(Implementation i, Module* mod)
{
- if (std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod) != EventHandlers[i].end())
+ if (stdalgo::isin(EventHandlers[i], mod))
return false;
EventHandlers[i].push_back(mod);
@@ -198,13 +176,7 @@ bool ModuleManager::Attach(Implementation i, Module* mod)
bool ModuleManager::Detach(Implementation i, Module* mod)
{
- EventHandlerIter x = std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod);
-
- if (x == EventHandlers[i].end())
- return false;
-
- EventHandlers[i].erase(x);
- return true;
+ return stdalgo::erase(EventHandlers[i], mod);
}
void ModuleManager::Attach(Implementation* i, Module* mod, size_t sz)
@@ -215,22 +187,20 @@ void ModuleManager::Attach(Implementation* i, Module* mod, size_t sz)
void ModuleManager::AttachAll(Module* mod)
{
- for (size_t i = I_BEGIN + 1; i != I_END; ++i)
+ for (size_t i = 0; i != I_END; ++i)
Attach((Implementation)i, mod);
}
void ModuleManager::DetachAll(Module* mod)
{
- for (size_t n = I_BEGIN + 1; n != I_END; ++n)
+ for (size_t n = 0; n != I_END; ++n)
Detach((Implementation)n, mod);
}
-bool ModuleManager::SetPriority(Module* mod, Priority s)
+void ModuleManager::SetPriority(Module* mod, Priority s)
{
- for (size_t n = I_BEGIN + 1; n != I_END; ++n)
+ for (size_t n = 0; n != I_END; ++n)
SetPriority(mod, (Implementation)n, s);
-
- return true;
}
bool ModuleManager::SetPriority(Module* mod, Implementation i, Priority s, Module* which)
@@ -367,17 +337,23 @@ bool ModuleManager::CanUnload(Module* mod)
ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
return false;
}
- if (mod->GetVersion().Flags & VF_STATIC)
- {
- LastModuleError = "Module " + mod->ModuleSourceFile + " not unloadable (marked static)";
- ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
- return false;
- }
mod->dying = true;
return true;
}
+void ModuleManager::UnregisterModes(Module* mod, ModeType modetype)
+{
+ const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes.GetModes(modetype);
+ for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); )
+ {
+ ModeHandler* const mh = i->second;
+ ++i;
+ if (mh->creator == mod)
+ this->DelService(*mh);
+ }
+}
+
void ModuleManager::DoSafeUnload(Module* mod)
{
// First, notify all modules that a module is about to be unloaded, so in case
@@ -387,6 +363,10 @@ void ModuleManager::DoSafeUnload(Module* mod)
std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
+ // Unregister modes before extensions because modes may require their extension to show the mode being unset
+ UnregisterModes(mod, MODETYPE_USER);
+ UnregisterModes(mod, MODETYPE_CHANNEL);
+
std::vector<reference<ExtensionItem> > items;
ServerInstance->Extensions.BeginUnregister(modfind->second, items);
/* Give the module a chance to tidy out all its metadata */
@@ -397,8 +377,8 @@ void ModuleManager::DoSafeUnload(Module* mod)
++c;
mod->OnCleanup(TYPE_CHANNEL, chan);
chan->doUnhookExtensions(items);
- const UserMembList* users = chan->GetUsers();
- for(UserMembCIter mi = users->begin(); mi != users->end(); mi++)
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator mi = users.begin(); mi != users.end(); ++mi)
mi->second->doUnhookExtensions(items);
}
@@ -412,24 +392,6 @@ void ModuleManager::DoSafeUnload(Module* mod)
user->doUnhookExtensions(items);
}
- const ModeParser::ModeHandlerMap& usermodes = ServerInstance->Modes->GetModes(MODETYPE_USER);
- for (ModeParser::ModeHandlerMap::const_iterator i = usermodes.begin(); i != usermodes.end(); )
- {
- ModeHandler* mh = i->second;
- ++i;
- if (mh->creator == mod)
- this->DelService(*mh);
- }
-
- const ModeParser::ModeHandlerMap& chanmodes = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
- for (ModeParser::ModeHandlerMap::const_iterator i = chanmodes.begin(); i != chanmodes.end(); )
- {
- ModeHandler* mh = i->second;
- ++i;
- if (mh->creator == mod)
- this->DelService(*mh);
- }
-
for(std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
{
std::multimap<std::string, ServiceProvider*>::iterator curr = i++;
@@ -487,26 +449,6 @@ namespace
ServerInstance->GlobalCulls.AddItem(this);
}
};
-
- struct ReloadAction : public HandlerBase0<void>
- {
- Module* const mod;
- HandlerBase1<void, bool>* const callback;
- ReloadAction(Module* m, HandlerBase1<void, bool>* c)
- : mod(m), callback(c) {}
- void Call()
- {
- DLLManager* dll = mod->ModuleDLLManager;
- std::string name = mod->ModuleSourceFile;
- ServerInstance->Modules->DoSafeUnload(mod);
- ServerInstance->GlobalCulls.Apply();
- delete dll;
- bool rv = ServerInstance->Modules->Load(name);
- if (callback)
- callback->Call(rv);
- ServerInstance->GlobalCulls.AddItem(this);
- }
- };
}
bool ModuleManager::Unload(Module* mod)
@@ -517,14 +459,6 @@ bool ModuleManager::Unload(Module* mod)
return true;
}
-void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
-{
- if (CanUnload(mod))
- ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
- else
- callback->Call(false);
-}
-
void ModuleManager::LoadAll()
{
std::map<std::string, ServiceList> servicemap;
@@ -535,7 +469,7 @@ void ModuleManager::LoadAll()
{
ConfigTag* tag = i->second;
std::string name = tag->getString("name");
- this->NewServices = &servicemap[name];
+ this->NewServices = &servicemap[ExpandModName(name)];
std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
if (!this->Load(name, true))
@@ -592,22 +526,6 @@ void ModuleManager::AddService(ServiceProvider& item)
{
switch (item.service)
{
- case SERVICE_COMMAND:
- if (!ServerInstance->Parser->AddCommand(static_cast<Command*>(&item)))
- throw ModuleException("Command "+std::string(item.name)+" already exists.");
- return;
- case SERVICE_MODE:
- {
- ModeHandler* mh = static_cast<ModeHandler*>(&item);
- ServerInstance->Modes->AddMode(mh);
- DataProviders.insert(std::make_pair((mh->GetModeType() == MODETYPE_CHANNEL ? "mode/" : "umode/") + item.name, &item));
- dynamic_reference_base::reset_all();
- return;
- }
- case SERVICE_METADATA:
- if (!ServerInstance->Extensions.Register(static_cast<ExtensionItem*>(&item)))
- throw ModuleException("Extension " + std::string(item.name) + " already exists.");
- return;
case SERVICE_DATA:
case SERVICE_IOHOOK:
{
@@ -625,7 +543,7 @@ void ModuleManager::AddService(ServiceProvider& item)
return;
}
default:
- throw ModuleException("Cannot add unknown service type");
+ item.RegisterService();
}
}
@@ -640,13 +558,7 @@ void ModuleManager::DelService(ServiceProvider& item)
case SERVICE_DATA:
case SERVICE_IOHOOK:
{
- for(std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
- {
- std::multimap<std::string, ServiceProvider*>::iterator curr = i++;
- if (curr->second == &item)
- DataProviders.erase(curr);
- }
- dynamic_reference_base::reset_all();
+ DelReferent(&item);
return;
}
default:
@@ -672,13 +584,25 @@ ServiceProvider* ModuleManager::FindService(ServiceType type, const std::string&
}
}
+std::string ModuleManager::ExpandModName(const std::string& modname)
+{
+ // Transform "callerid" -> "m_callerid.so" unless it already has a ".so" extension,
+ // so coremods in the "core_*.so" form aren't changed
+ std::string ret = modname;
+ if ((modname.length() < 3) || (modname.compare(modname.size() - 3, 3, ".so")))
+ ret.insert(0, "m_").append(".so");
+ return ret;
+}
+
dynamic_reference_base::dynamic_reference_base(Module* Creator, const std::string& Name)
- : name(Name), value(NULL), creator(Creator)
+ : name(Name), hook(NULL), value(NULL), creator(Creator)
{
if (!dynrefs)
- dynrefs = new intrusive_list<dynamic_reference_base>;
+ dynrefs = new insp::intrusive_list<dynamic_reference_base>;
dynrefs->push_front(this);
- if (dynref_init_complete)
+
+ // Resolve unless there is no ModuleManager (part of class InspIRCd)
+ if (ServerInstance)
resolve();
}
@@ -700,19 +624,48 @@ void dynamic_reference_base::SetProvider(const std::string& newname)
void dynamic_reference_base::resolve()
{
- std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.find(name);
- if (i != ServerInstance->Modules->DataProviders.end())
- value = static_cast<DataProvider*>(i->second);
+ // Because find() may return any element with a matching key in case count(key) > 1 use lower_bound()
+ // to ensure a dynref with the same name as another one resolves to the same object
+ std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules.DataProviders.lower_bound(name);
+ if ((i != ServerInstance->Modules.DataProviders.end()) && (i->first == this->name))
+ {
+ ServiceProvider* newvalue = i->second;
+ if (value != newvalue)
+ {
+ value = newvalue;
+ if (hook)
+ hook->OnCapture();
+ }
+ }
else
value = NULL;
}
Module* ModuleManager::Find(const std::string &name)
{
- std::map<std::string, Module*>::iterator modfind = Modules.find(name);
+ std::map<std::string, Module*>::const_iterator modfind = Modules.find(ExpandModName(name));
if (modfind == Modules.end())
return NULL;
else
return modfind->second;
}
+
+void ModuleManager::AddReferent(const std::string& name, ServiceProvider* service)
+{
+ DataProviders.insert(std::make_pair(name, service));
+ dynamic_reference_base::reset_all();
+}
+
+void ModuleManager::DelReferent(ServiceProvider* service)
+{
+ for (std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
+ {
+ ServiceProvider* curr = i->second;
+ if (curr == service)
+ DataProviders.erase(i++);
+ else
+ ++i;
+ }
+ dynamic_reference_base::reset_all();
+}
diff --git a/src/modules/extra/m_geoip.cpp b/src/modules/extra/m_geoip.cpp
index 394f7f9b4..c7b0fd210 100644
--- a/src/modules/extra/m_geoip.cpp
+++ b/src/modules/extra/m_geoip.cpp
@@ -17,18 +17,29 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: find_compiler_flags("geoip" "")
+/// $LinkerFlags: find_linker_flags("geoip" "-lGeoIP")
+
+/// $PackageInfo: require_system("centos" "7.0") GeoIP-devel pkgconfig
+/// $PackageInfo: require_system("darwin") geoip pkg-config
+/// $PackageInfo: require_system("ubuntu") libgeoip-dev pkg-config
#include "inspircd.h"
#include "xline.h"
+// Fix warnings about the use of commas at end of enumerator lists on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-extensions"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-pedantic"
+#endif
+
#include <GeoIP.h>
#ifdef _WIN32
# pragma comment(lib, "GeoIP.lib")
#endif
-/* $LinkerFlags: -lGeoIP */
-
class ModuleGeoIP : public Module
{
LocalStringExt ext;
@@ -46,7 +57,9 @@ class ModuleGeoIP : public Module
}
public:
- ModuleGeoIP() : ext("geoip_cc", this), gi(NULL)
+ ModuleGeoIP()
+ : ext("geoip_cc", ExtensionItem::EXT_USER, this)
+ , gi(NULL)
{
}
@@ -56,7 +69,8 @@ class ModuleGeoIP : public Module
if (gi == NULL)
throw ModuleException("Unable to initialize geoip, are you missing GeoIP.dat?");
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
LocalUser* user = *i;
if ((user->registered == REG_ALL) && (!ext.get(user)))
@@ -94,14 +108,16 @@ class ModuleGeoIP : public Module
return MOD_RES_DENY;
}
- ModResult OnStats(char symbol, User* user, string_list &out) CXX11_OVERRIDE
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'G')
+ if (stats.GetSymbol() != 'G')
return MOD_RES_PASSTHRU;
unsigned int unknown = 0;
std::map<std::string, unsigned int> results;
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
std::string* cc = ext.get(*i);
if (cc)
@@ -110,14 +126,13 @@ class ModuleGeoIP : public Module
unknown++;
}
- std::string p = "801 " + user->nick + " :GeoIPSTATS ";
for (std::map<std::string, unsigned int>::const_iterator i = results.begin(); i != results.end(); ++i)
{
- out.push_back(p + i->first + " " + ConvToStr(i->second));
+ stats.AddRow(801, "GeoIPSTATS " + i->first + " " + ConvToStr(i->second));
}
if (unknown)
- out.push_back(p + "Unknown " + ConvToStr(unknown));
+ stats.AddRow(801, "GeoIPSTATS Unknown " + ConvToStr(unknown));
return MOD_RES_DENY;
}
diff --git a/src/modules/extra/m_ldap.cpp b/src/modules/extra/m_ldap.cpp
index d480a88f6..fc1bee939 100644
--- a/src/modules/extra/m_ldap.cpp
+++ b/src/modules/extra/m_ldap.cpp
@@ -1,8 +1,8 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2013-2014 Adam <Adam@anope.org>
- * Copyright (C) 2003-2014 Anope Team <team@anope.org>
+ * Copyright (C) 2013-2015 Adam <Adam@anope.org>
+ * Copyright (C) 2003-2015 Anope Team <team@anope.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -17,17 +17,159 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $LinkerFlags: -llber -lldap_r
+
+/// $PackageInfo: require_system("centos") openldap-devel
+/// $PackageInfo: require_system("ubuntu") libldap2-dev
+
#include "inspircd.h"
#include "modules/ldap.h"
+// Ignore OpenLDAP deprecation warnings on OS X Yosemite and newer.
+#if defined __APPLE__
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
#include <ldap.h>
#ifdef _WIN32
-# pragma comment(lib, "ldap.lib")
-# pragma comment(lib, "lber.lib")
+# pragma comment(lib, "libldap_r.lib")
+# pragma comment(lib, "liblber.lib")
#endif
-/* $LinkerFlags: -lldap */
+class LDAPService;
+
+class LDAPRequest
+{
+ public:
+ LDAPService* service;
+ LDAPInterface* inter;
+ LDAPMessage* message; /* message returned by ldap_ */
+ LDAPResult* result; /* final result */
+ struct timeval tv;
+ QueryType type;
+
+ LDAPRequest(LDAPService* s, LDAPInterface* i)
+ : service(s)
+ , inter(i)
+ , message(NULL)
+ , result(NULL)
+ {
+ type = QUERY_UNKNOWN;
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ }
+
+ virtual ~LDAPRequest()
+ {
+ delete result;
+ if (message != NULL)
+ ldap_msgfree(message);
+ }
+
+ virtual int run() = 0;
+};
+
+class LDAPBind : public LDAPRequest
+{
+ std::string who, pass;
+
+ public:
+ LDAPBind(LDAPService* s, LDAPInterface* i, const std::string& w, const std::string& p)
+ : LDAPRequest(s, i)
+ , who(w)
+ , pass(p)
+ {
+ type = QUERY_BIND;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPSearch : public LDAPRequest
+{
+ std::string base;
+ int searchscope;
+ std::string filter;
+
+ public:
+ LDAPSearch(LDAPService* s, LDAPInterface* i, const std::string& b, int se, const std::string& f)
+ : LDAPRequest(s, i)
+ , base(b)
+ , searchscope(se)
+ , filter(f)
+ {
+ type = QUERY_SEARCH;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPAdd : public LDAPRequest
+{
+ std::string dn;
+ LDAPMods attributes;
+
+ public:
+ LDAPAdd(LDAPService* s, LDAPInterface* i, const std::string& d, const LDAPMods& attr)
+ : LDAPRequest(s, i)
+ , dn(d)
+ , attributes(attr)
+ {
+ type = QUERY_ADD;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPDel : public LDAPRequest
+{
+ std::string dn;
+
+ public:
+ LDAPDel(LDAPService* s, LDAPInterface* i, const std::string& d)
+ : LDAPRequest(s, i)
+ , dn(d)
+ {
+ type = QUERY_DELETE;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPModify : public LDAPRequest
+{
+ std::string base;
+ LDAPMods attributes;
+
+ public:
+ LDAPModify(LDAPService* s, LDAPInterface* i, const std::string& b, const LDAPMods& attr)
+ : LDAPRequest(s, i)
+ , base(b)
+ , attributes(attr)
+ {
+ type = QUERY_MODIFY;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
+
+class LDAPCompare : public LDAPRequest
+{
+ std::string dn, attr, val;
+
+ public:
+ LDAPCompare(LDAPService* s, LDAPInterface* i, const std::string& d, const std::string& a, const std::string& v)
+ : LDAPRequest(s, i)
+ , dn(d)
+ , attr(a)
+ , val(v)
+ {
+ type = QUERY_COMPARE;
+ }
+
+ int run() CXX11_OVERRIDE;
+};
class LDAPService : public LDAPProvider, public SocketThread
{
@@ -36,9 +178,9 @@ class LDAPService : public LDAPProvider, public SocketThread
time_t last_connect;
int searchscope;
time_t timeout;
- time_t last_timeout_check;
- LDAPMod** BuildMods(const LDAPMods& attributes)
+ public:
+ static LDAPMod** BuildMods(const LDAPMods& attributes)
{
LDAPMod** mods = new LDAPMod*[attributes.size() + 1];
memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1));
@@ -69,7 +211,7 @@ class LDAPService : public LDAPProvider, public SocketThread
return mods;
}
- void FreeMods(LDAPMod** mods)
+ static void FreeMods(LDAPMod** mods)
{
for (unsigned int i = 0; mods[i] != NULL; ++i)
{
@@ -86,6 +228,7 @@ class LDAPService : public LDAPProvider, public SocketThread
delete[] mods;
}
+ private:
void Reconnect()
{
// Only try one connect a minute. It is an expensive blocking operation
@@ -97,52 +240,21 @@ class LDAPService : public LDAPProvider, public SocketThread
Connect();
}
- void SaveInterface(LDAPInterface* i, LDAPQuery msgid)
- {
- if (i != NULL)
- {
- this->LockQueue();
- this->queries[msgid] = std::make_pair(ServerInstance->Time(), i);
- this->UnlockQueueWakeup();
- }
- }
-
- void Timeout()
+ void QueueRequest(LDAPRequest* r)
{
- if (last_timeout_check == ServerInstance->Time())
- return;
- last_timeout_check = ServerInstance->Time();
-
- for (query_queue::iterator it = this->queries.begin(); it != this->queries.end(); )
- {
- LDAPQuery msgid = it->first;
- time_t created = it->second.first;
- LDAPInterface* i = it->second.second;
- ++it;
-
- if (ServerInstance->Time() > created + timeout)
- {
- LDAPResult* ldap_result = new LDAPResult();
- ldap_result->id = msgid;
- ldap_result->error = "Query timed out";
-
- this->queries.erase(msgid);
- this->results.push_back(std::make_pair(i, ldap_result));
-
- this->NotifyParent();
- }
- }
+ this->LockQueue();
+ this->queries.push_back(r);
+ this->UnlockQueueWakeup();
}
public:
- typedef std::map<LDAPQuery, std::pair<time_t, LDAPInterface*> > query_queue;
- typedef std::vector<std::pair<LDAPInterface*, LDAPResult*> > result_queue;
- query_queue queries;
- result_queue results;
+ typedef std::vector<LDAPRequest*> query_queue;
+ query_queue queries, results;
+ Mutex process_mutex; /* held when processing requests not in either queue */
LDAPService(Module* c, ConfigTag* tag)
: LDAPProvider(c, "LDAP/" + tag->getString("id"))
- , con(NULL), config(tag), last_connect(0), last_timeout_check(0)
+ , con(NULL), config(tag), last_connect(0)
{
std::string scope = config->getString("searchscope");
if (scope == "base")
@@ -160,30 +272,29 @@ class LDAPService : public LDAPProvider, public SocketThread
{
this->LockQueue();
- for (query_queue::iterator i = this->queries.begin(); i != this->queries.end(); ++i)
+ for (unsigned int i = 0; i < this->queries.size(); ++i)
{
- LDAPQuery msgid = i->first;
- LDAPInterface* inter = i->second.second;
+ LDAPRequest* req = this->queries[i];
- ldap_abandon_ext(this->con, msgid, NULL, NULL);
+ /* queries have no results yet */
+ req->result = new LDAPResult();
+ req->result->type = req->type;
+ req->result->error = "LDAP Interface is going away";
+ req->inter->OnError(*req->result);
- if (inter)
- {
- LDAPResult r;
- r.error = "LDAP Interface is going away";
- inter->OnError(r);
- }
+ delete req;
}
this->queries.clear();
- for (result_queue::iterator i = this->results.begin(); i != this->results.end(); ++i)
+ for (unsigned int i = 0; i < this->results.size(); ++i)
{
- LDAPInterface* inter = i->first;
- LDAPResult* r = i->second;
+ LDAPRequest* req = this->results[i];
- r->error = "LDAP Interface is going away";
- if (inter)
- inter->OnError(*r);
+ /* even though this may have already finished successfully we return that it didn't */
+ req->result->error = "LDAP Interface is going away";
+ req->inter->OnError(*req->result);
+
+ delete req;
}
this->results.clear();
@@ -218,321 +329,197 @@ class LDAPService : public LDAPProvider, public SocketThread
}
}
- LDAPQuery BindAsManager(LDAPInterface* i) CXX11_OVERRIDE
+ void BindAsManager(LDAPInterface* i) CXX11_OVERRIDE
{
std::string binddn = config->getString("binddn");
std::string bindauth = config->getString("bindauth");
- return this->Bind(i, binddn, bindauth);
+ this->Bind(i, binddn, bindauth);
}
- LDAPQuery Bind(LDAPInterface* i, const std::string& who, const std::string& pass) CXX11_OVERRIDE
+ void Bind(LDAPInterface* i, const std::string& who, const std::string& pass) CXX11_OVERRIDE
{
- berval cred;
- cred.bv_val = strdup(pass.c_str());
- cred.bv_len = pass.length();
-
- LDAPQuery msgid;
- int ret = ldap_sasl_bind(con, who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid);
- free(cred.bv_val);
- if (ret != LDAP_SUCCESS)
- {
- if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
- {
- this->Reconnect();
- return this->Bind(i, who, pass);
- }
- else
- throw LDAPException(ldap_err2string(ret));
- }
-
- SaveInterface(i, msgid);
- return msgid;
+ LDAPBind* b = new LDAPBind(this, i, who, pass);
+ QueueRequest(b);
}
- LDAPQuery Search(LDAPInterface* i, const std::string& base, const std::string& filter) CXX11_OVERRIDE
+ void Search(LDAPInterface* i, const std::string& base, const std::string& filter) CXX11_OVERRIDE
{
if (i == NULL)
throw LDAPException("No interface");
- LDAPQuery msgid;
- int ret = ldap_search_ext(this->con, base.c_str(), searchscope, filter.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msgid);
- if (ret != LDAP_SUCCESS)
- {
- if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
- {
- this->Reconnect();
- return this->Search(i, base, filter);
- }
- else
- throw LDAPException(ldap_err2string(ret));
- }
-
- SaveInterface(i, msgid);
- return msgid;
+ LDAPSearch* s = new LDAPSearch(this, i, base, searchscope, filter);
+ QueueRequest(s);
}
- LDAPQuery Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) CXX11_OVERRIDE
+ void Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) CXX11_OVERRIDE
{
- LDAPMod** mods = this->BuildMods(attributes);
- LDAPQuery msgid;
- int ret = ldap_add_ext(this->con, dn.c_str(), mods, NULL, NULL, &msgid);
- this->FreeMods(mods);
-
- if (ret != LDAP_SUCCESS)
- {
- if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
- {
- this->Reconnect();
- return this->Add(i, dn, attributes);
- }
- else
- throw LDAPException(ldap_err2string(ret));
- }
-
- SaveInterface(i, msgid);
- return msgid;
+ LDAPAdd* add = new LDAPAdd(this, i, dn, attributes);
+ QueueRequest(add);
}
- LDAPQuery Del(LDAPInterface* i, const std::string& dn) CXX11_OVERRIDE
+ void Del(LDAPInterface* i, const std::string& dn) CXX11_OVERRIDE
{
- LDAPQuery msgid;
- int ret = ldap_delete_ext(this->con, dn.c_str(), NULL, NULL, &msgid);
-
- if (ret != LDAP_SUCCESS)
- {
- if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
- {
- this->Reconnect();
- return this->Del(i, dn);
- }
- else
- throw LDAPException(ldap_err2string(ret));
- }
-
- SaveInterface(i, msgid);
- return msgid;
+ LDAPDel* del = new LDAPDel(this, i, dn);
+ QueueRequest(del);
}
- LDAPQuery Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) CXX11_OVERRIDE
+ void Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) CXX11_OVERRIDE
{
- LDAPMod** mods = this->BuildMods(attributes);
- LDAPQuery msgid;
- int ret = ldap_modify_ext(this->con, base.c_str(), mods, NULL, NULL, &msgid);
- this->FreeMods(mods);
-
- if (ret != LDAP_SUCCESS)
- {
- if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
- {
- this->Reconnect();
- return this->Modify(i, base, attributes);
- }
- else
- throw LDAPException(ldap_err2string(ret));
- }
+ LDAPModify* mod = new LDAPModify(this, i, base, attributes);
+ QueueRequest(mod);
+ }
- SaveInterface(i, msgid);
- return msgid;
+ void Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) CXX11_OVERRIDE
+ {
+ LDAPCompare* comp = new LDAPCompare(this, i, dn, attr, val);
+ QueueRequest(comp);
}
- LDAPQuery Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) CXX11_OVERRIDE
+ private:
+ void BuildReply(int res, LDAPRequest* req)
{
- berval cred;
- cred.bv_val = strdup(val.c_str());
- cred.bv_len = val.length();
+ LDAPResult* ldap_result = req->result = new LDAPResult();
+ req->result->type = req->type;
- LDAPQuery msgid;
- int ret = ldap_compare_ext(con, dn.c_str(), attr.c_str(), &cred, NULL, NULL, &msgid);
- free(cred.bv_val);
+ if (res != LDAP_SUCCESS)
+ {
+ ldap_result->error = ldap_err2string(res);
+ return;
+ }
- if (ret != LDAP_SUCCESS)
+ if (req->message == NULL)
{
- if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
- {
- this->Reconnect();
- return this->Compare(i, dn, attr, val);
- }
- else
- throw LDAPException(ldap_err2string(ret));
+ return;
}
- SaveInterface(i, msgid);
- return msgid;
- }
+ /* a search result */
- void Run() CXX11_OVERRIDE
- {
- while (!this->GetExitFlag())
+ for (LDAPMessage* cur = ldap_first_message(this->con, req->message); cur; cur = ldap_next_message(this->con, cur))
{
- this->LockQueue();
- if (this->queries.empty())
+ LDAPAttributes attributes;
+
+ char* dn = ldap_get_dn(this->con, cur);
+ if (dn != NULL)
{
- this->WaitForQueue();
- this->UnlockQueue();
- continue;
+ attributes["dn"].push_back(dn);
+ ldap_memfree(dn);
+ dn = NULL;
}
- this->Timeout();
- this->UnlockQueue();
- struct timeval tv = { 1, 0 };
- LDAPMessage* result;
- int rtype = ldap_result(this->con, LDAP_RES_ANY, 1, &tv, &result);
- if (rtype <= 0 || this->GetExitFlag())
- continue;
+ BerElement* ber = NULL;
- int cur_id = ldap_msgid(result);
+ for (char* attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
+ {
+ berval** vals = ldap_get_values_len(this->con, cur, attr);
+ int count = ldap_count_values_len(vals);
- this->LockQueue();
+ std::vector<std::string> attrs;
+ for (int j = 0; j < count; ++j)
+ attrs.push_back(vals[j]->bv_val);
+ attributes[attr] = attrs;
- query_queue::iterator it = this->queries.find(cur_id);
- if (it == this->queries.end())
- {
- this->UnlockQueue();
- ldap_msgfree(result);
- continue;
+ ldap_value_free_len(vals);
+ ldap_memfree(attr);
}
- LDAPInterface* i = it->second.second;
- this->queries.erase(it);
+ if (ber != NULL)
+ ber_free(ber, 0);
- this->UnlockQueue();
+ ldap_result->messages.push_back(attributes);
+ }
+ }
- LDAPResult* ldap_result = new LDAPResult();
- ldap_result->id = cur_id;
+ void SendRequests()
+ {
+ process_mutex.Lock();
- for (LDAPMessage* cur = ldap_first_message(this->con, result); cur; cur = ldap_next_message(this->con, cur))
- {
- int cur_type = ldap_msgtype(cur);
+ query_queue q;
+ this->LockQueue();
+ queries.swap(q);
+ this->UnlockQueue();
- LDAPAttributes attributes;
+ if (q.empty())
+ {
+ process_mutex.Unlock();
+ return;
+ }
- {
- char* dn = ldap_get_dn(this->con, cur);
- if (dn != NULL)
- {
- attributes["dn"].push_back(dn);
- ldap_memfree(dn);
- }
- }
+ for (unsigned int i = 0; i < q.size(); ++i)
+ {
+ LDAPRequest* req = q[i];
+ int ret = req->run();
- switch (cur_type)
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ /* try again */
+ try
{
- case LDAP_RES_BIND:
- ldap_result->type = LDAPResult::QUERY_BIND;
- break;
- case LDAP_RES_SEARCH_ENTRY:
- ldap_result->type = LDAPResult::QUERY_SEARCH;
- break;
- case LDAP_RES_ADD:
- ldap_result->type = LDAPResult::QUERY_ADD;
- break;
- case LDAP_RES_DELETE:
- ldap_result->type = LDAPResult::QUERY_DELETE;
- break;
- case LDAP_RES_MODIFY:
- ldap_result->type = LDAPResult::QUERY_MODIFY;
- break;
- case LDAP_RES_SEARCH_RESULT:
- // If we get here and ldap_result->type is LDAPResult::QUERY_UNKNOWN
- // then the result set is empty
- ldap_result->type = LDAPResult::QUERY_SEARCH;
- break;
- case LDAP_RES_COMPARE:
- ldap_result->type = LDAPResult::QUERY_COMPARE;
- break;
- default:
- continue;
+ Reconnect();
}
-
- switch (cur_type)
+ catch (const LDAPException &)
{
- case LDAP_RES_SEARCH_ENTRY:
- {
- BerElement* ber = NULL;
- for (char* attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
- {
- berval** vals = ldap_get_values_len(this->con, cur, attr);
- int count = ldap_count_values_len(vals);
-
- std::vector<std::string> attrs;
- for (int j = 0; j < count; ++j)
- attrs.push_back(vals[j]->bv_val);
- attributes[attr] = attrs;
-
- ldap_value_free_len(vals);
- ldap_memfree(attr);
- }
- if (ber != NULL)
- ber_free(ber, 0);
-
- break;
- }
- case LDAP_RES_BIND:
- case LDAP_RES_ADD:
- case LDAP_RES_DELETE:
- case LDAP_RES_MODIFY:
- case LDAP_RES_COMPARE:
- {
- int errcode = -1;
- int parse_result = ldap_parse_result(this->con, cur, &errcode, NULL, NULL, NULL, NULL, 0);
- if (parse_result != LDAP_SUCCESS)
- {
- ldap_result->error = ldap_err2string(parse_result);
- }
- else
- {
- if (cur_type == LDAP_RES_COMPARE)
- {
- if (errcode != LDAP_COMPARE_TRUE)
- ldap_result->error = ldap_err2string(errcode);
- }
- else if (errcode != LDAP_SUCCESS)
- ldap_result->error = ldap_err2string(errcode);
- }
- break;
- }
- default:
- continue;
}
- ldap_result->messages.push_back(attributes);
+ ret = req->run();
}
- ldap_msgfree(result);
+ BuildReply(ret, req);
+
+ this->LockQueue();
+ this->results.push_back(req);
+ this->UnlockQueue();
+ }
+
+ this->NotifyParent();
+
+ process_mutex.Unlock();
+ }
+ public:
+ void Run() CXX11_OVERRIDE
+ {
+ while (!this->GetExitFlag())
+ {
this->LockQueue();
- this->results.push_back(std::make_pair(i, ldap_result));
- this->UnlockQueueWakeup();
+ if (this->queries.empty())
+ this->WaitForQueue();
+ this->UnlockQueue();
- this->NotifyParent();
+ SendRequests();
}
}
void OnNotify() CXX11_OVERRIDE
{
- LDAPService::result_queue r;
+ query_queue r;
this->LockQueue();
this->results.swap(r);
this->UnlockQueue();
- for (LDAPService::result_queue::iterator i = r.begin(); i != r.end(); ++i)
+ for (unsigned int i = 0; i < r.size(); ++i)
{
- LDAPInterface* li = i->first;
- LDAPResult* res = i->second;
+ LDAPRequest* req = r[i];
+ LDAPInterface* li = req->inter;
+ LDAPResult* res = req->result;
if (!res->error.empty())
li->OnError(*res);
else
li->OnResult(*res);
- delete res;
+ delete req;
}
}
+
+ LDAP* GetConnection()
+ {
+ return con;
+ }
};
class ModuleLDAP : public Module
{
- typedef std::map<std::string, LDAPService*> ServiceMap;
+ typedef insp::flat_map<std::string, LDAPService*> ServiceMap;
ServiceMap LDAPServices;
public:
@@ -557,7 +544,7 @@ class ModuleLDAP : public Module
conns[id] = conn;
ServerInstance->Modules->AddService(*conn);
- ServerInstance->Threads->Start(conn);
+ ServerInstance->Threads.Start(conn);
}
else
{
@@ -583,34 +570,42 @@ class ModuleLDAP : public Module
for (ServiceMap::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
{
LDAPService* s = it->second;
+
+ s->process_mutex.Lock();
s->LockQueue();
- for (LDAPService::query_queue::iterator it2 = s->queries.begin(); it2 != s->queries.end();)
+
+ for (unsigned int i = s->queries.size(); i > 0; --i)
{
- int msgid = it2->first;
- LDAPInterface* i = it2->second.second;
- ++it2;
+ LDAPRequest* req = s->queries[i - 1];
+ LDAPInterface* li = req->inter;
- if (i->creator == m)
- s->queries.erase(msgid);
+ if (li->creator == m)
+ {
+ s->queries.erase(s->queries.begin() + i - 1);
+ delete req;
+ }
}
+
for (unsigned int i = s->results.size(); i > 0; --i)
{
- LDAPInterface* li = s->results[i - 1].first;
- LDAPResult* r = s->results[i - 1].second;
+ LDAPRequest* req = s->results[i - 1];
+ LDAPInterface* li = req->inter;
if (li->creator == m)
{
s->results.erase(s->results.begin() + i - 1);
- delete r;
+ delete req;
}
}
+
s->UnlockQueue();
+ s->process_mutex.Unlock();
}
}
~ModuleLDAP()
{
- for (std::map<std::string, LDAPService*>::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
+ for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
{
LDAPService* conn = i->second;
conn->join();
@@ -625,4 +620,57 @@ class ModuleLDAP : public Module
}
};
+int LDAPBind::run()
+{
+ berval cred;
+ cred.bv_val = strdup(pass.c_str());
+ cred.bv_len = pass.length();
+
+ int i = ldap_sasl_bind_s(service->GetConnection(), who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
+
+ free(cred.bv_val);
+
+ return i;
+}
+
+int LDAPSearch::run()
+{
+ return ldap_search_ext_s(service->GetConnection(), base.c_str(), searchscope, filter.c_str(), NULL, 0, NULL, NULL, &tv, 0, &message);
+}
+
+int LDAPAdd::run()
+{
+ LDAPMod** mods = LDAPService::BuildMods(attributes);
+ int i = ldap_add_ext_s(service->GetConnection(), dn.c_str(), mods, NULL, NULL);
+ LDAPService::FreeMods(mods);
+ return i;
+}
+
+int LDAPDel::run()
+{
+ return ldap_delete_ext_s(service->GetConnection(), dn.c_str(), NULL, NULL);
+}
+
+int LDAPModify::run()
+{
+ LDAPMod** mods = LDAPService::BuildMods(attributes);
+ int i = ldap_modify_ext_s(service->GetConnection(), base.c_str(), mods, NULL, NULL);
+ LDAPService::FreeMods(mods);
+ return i;
+}
+
+int LDAPCompare::run()
+{
+ berval cred;
+ cred.bv_val = strdup(val.c_str());
+ cred.bv_len = val.length();
+
+ int ret = ldap_compare_ext_s(service->GetConnection(), dn.c_str(), attr.c_str(), &cred, NULL, NULL);
+
+ free(cred.bv_val);
+
+ return ret;
+
+}
+
MODULE_INIT(ModuleLDAP)
diff --git a/src/modules/extra/m_mssql.cpp b/src/modules/extra/m_mssql.cpp
deleted file mode 100644
index 8f8fe080f..000000000
--- a/src/modules/extra/m_mssql.cpp
+++ /dev/null
@@ -1,860 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2008-2009 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008-2009 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include <tds.h>
-#include <tdsconvert.h>
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include "m_sqlv2.h"
-
-/* $CompileFlags: exec("grep VERSION_NO /usr/include/tdsver.h 2>/dev/null | perl -e 'print "-D_TDSVER=".((<> =~ /freetds v(\d+\.\d+)/i) ? $1*100 : 0);'") */
-/* $LinkerFlags: -ltds */
-
-class SQLConn;
-class MsSQLResult;
-class ModuleMsSQL;
-
-typedef std::map<std::string, SQLConn*> ConnMap;
-typedef std::deque<MsSQLResult*> ResultQueue;
-
-unsigned long count(const char * const str, char a)
-{
- unsigned long n = 0;
- for (const char *p = str; *p; ++p)
- {
- if (*p == '?')
- ++n;
- }
- return n;
-}
-
-ConnMap connections;
-Mutex* ResultsMutex;
-Mutex* LoggingMutex;
-
-class QueryThread : public SocketThread
-{
- private:
- ModuleMsSQL* const Parent;
- public:
- QueryThread(ModuleMsSQL* mod) : Parent(mod) { }
- ~QueryThread() { }
- void Run();
- void OnNotify();
-};
-
-class MsSQLResult : public SQLresult
-{
- private:
- int currentrow;
- int rows;
- int cols;
-
- std::vector<std::string> colnames;
- std::vector<SQLfieldList> fieldlists;
- SQLfieldList emptyfieldlist;
-
- SQLfieldList* fieldlist;
- SQLfieldMap* fieldmap;
-
- public:
- MsSQLResult(Module* self, Module* to, unsigned int rid)
- : SQLresult(self, to, rid), currentrow(0), rows(0), cols(0), fieldlist(NULL), fieldmap(NULL)
- {
- }
-
- void AddRow(int colsnum, char **dat, char **colname)
- {
- colnames.clear();
- cols = colsnum;
- for (int i = 0; i < colsnum; i++)
- {
- fieldlists.resize(fieldlists.size()+1);
- colnames.push_back(colname[i]);
- SQLfield sf(dat[i] ? dat[i] : "", dat[i] ? false : true);
- fieldlists[rows].push_back(sf);
- }
- rows++;
- }
-
- void UpdateAffectedCount()
- {
- rows++;
- }
-
- int Rows()
- {
- return rows;
- }
-
- int Cols()
- {
- return cols;
- }
-
- std::string ColName(int column)
- {
- if (column < (int)colnames.size())
- {
- return colnames[column];
- }
- else
- {
- throw SQLbadColName();
- }
- return "";
- }
-
- int ColNum(const std::string &column)
- {
- for (unsigned int i = 0; i < colnames.size(); i++)
- {
- if (column == colnames[i])
- return i;
- }
- throw SQLbadColName();
- return 0;
- }
-
- SQLfield GetValue(int row, int column)
- {
- if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols()))
- {
- return fieldlists[row][column];
- }
-
- throw SQLbadColName();
-
- /* XXX: We never actually get here because of the throw */
- return SQLfield("",true);
- }
-
- SQLfieldList& GetRow()
- {
- if (currentrow < rows)
- return fieldlists[currentrow];
- else
- return emptyfieldlist;
- }
-
- SQLfieldMap& GetRowMap()
- {
- /* In an effort to reduce overhead we don't actually allocate the map
- * until the first time it's needed...so...
- */
- if(fieldmap)
- {
- fieldmap->clear();
- }
- else
- {
- fieldmap = new SQLfieldMap;
- }
-
- if (currentrow < rows)
- {
- for (int i = 0; i < Cols(); i++)
- {
- fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i)));
- }
- currentrow++;
- }
-
- return *fieldmap;
- }
-
- SQLfieldList* GetRowPtr()
- {
- fieldlist = new SQLfieldList();
-
- if (currentrow < rows)
- {
- for (int i = 0; i < Rows(); i++)
- {
- fieldlist->push_back(fieldlists[currentrow][i]);
- }
- currentrow++;
- }
- return fieldlist;
- }
-
- SQLfieldMap* GetRowMapPtr()
- {
- fieldmap = new SQLfieldMap();
-
- if (currentrow < rows)
- {
- for (int i = 0; i < Cols(); i++)
- {
- fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i)));
- }
- currentrow++;
- }
-
- return fieldmap;
- }
-
- void Free(SQLfieldMap* fm)
- {
- delete fm;
- }
-
- void Free(SQLfieldList* fl)
- {
- delete fl;
- }
-};
-
-class SQLConn : public classbase
-{
- private:
- ResultQueue results;
- Module* mod;
- SQLhost host;
- TDSLOGIN* login;
- TDSSOCKET* sock;
- TDSCONTEXT* context;
-
- public:
- QueryQueue queue;
-
- SQLConn(Module* m, const SQLhost& hi)
- : mod(m), host(hi), login(NULL), sock(NULL), context(NULL)
- {
- if (OpenDB())
- {
- std::string query("USE " + host.name);
- if (tds_submit_query(sock, query.c_str()) == TDS_SUCCEED)
- {
- if (tds_process_simple_query(sock) != TDS_SUCCEED)
- {
- LoggingMutex->Lock();
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
- LoggingMutex->Unlock();
- CloseDB();
- }
- }
- else
- {
- LoggingMutex->Lock();
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
- LoggingMutex->Unlock();
- CloseDB();
- }
- }
- else
- {
- LoggingMutex->Lock();
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not connect to DB with id: " + host.id);
- LoggingMutex->Unlock();
- CloseDB();
- }
- }
-
- ~SQLConn()
- {
- CloseDB();
- }
-
- SQLerror Query(SQLrequest* req)
- {
- if (!sock)
- return SQLerror(SQL_BAD_CONN, "Socket was NULL, check if SQL server is running.");
-
- /* Pointer to the buffer we screw around with substitution in */
- char* query;
-
- /* Pointer to the current end of query, where we append new stuff */
- char* queryend;
-
- /* Total length of the unescaped parameters */
- unsigned long maxparamlen, paramcount;
-
- /* The length of the longest parameter */
- maxparamlen = 0;
-
- for(ParamL::iterator i = req->query.p.begin(); i != req->query.p.end(); i++)
- {
- if (i->size() > maxparamlen)
- maxparamlen = i->size();
- }
-
- /* How many params are there in the query? */
- paramcount = count(req->query.q.c_str(), '?');
-
- /* This stores copy of params to be inserted with using numbered params 1;3B*/
- ParamL paramscopy(req->query.p);
-
- /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be.
- * sizeofquery + (maxtotalparamlength*2) + 1
- *
- * The +1 is for null-terminating the string
- */
-
- query = new char[req->query.q.length() + (maxparamlen*paramcount*2) + 1];
- queryend = query;
-
- for(unsigned long i = 0; i < req->query.q.length(); i++)
- {
- if(req->query.q[i] == '?')
- {
- /* We found a place to substitute..what fun.
- * use mssql calls to escape and write the
- * escaped string onto the end of our query buffer,
- * then we "just" need to make sure queryend is
- * pointing at the right place.
- */
-
- /* Is it numbered parameter?
- */
-
- bool numbered;
- numbered = false;
-
- /* Numbered parameter number :|
- */
- unsigned int paramnum;
- paramnum = 0;
-
- /* Let's check if it's a numbered param. And also calculate it's number.
- */
-
- while ((i < req->query.q.length() - 1) && (req->query.q[i+1] >= '0') && (req->query.q[i+1] <= '9'))
- {
- numbered = true;
- ++i;
- paramnum = paramnum * 10 + req->query.q[i] - '0';
- }
-
- if (paramnum > paramscopy.size() - 1)
- {
- /* index is out of range!
- */
- numbered = false;
- }
-
- if (numbered)
- {
- /* Custom escaping for this one. converting ' to '' should make SQL Server happy. Ugly but fast :]
- */
- char* escaped = new char[(paramscopy[paramnum].length() * 2) + 1];
- char* escend = escaped;
- for (std::string::iterator p = paramscopy[paramnum].begin(); p < paramscopy[paramnum].end(); p++)
- {
- if (*p == '\'')
- {
- *escend = *p;
- escend++;
- *escend = *p;
- }
- *escend = *p;
- escend++;
- }
- *escend = 0;
-
- for (char* n = escaped; *n; n++)
- {
- *queryend = *n;
- queryend++;
- }
- delete[] escaped;
- }
- else if (req->query.p.size())
- {
- /* Custom escaping for this one. converting ' to '' should make SQL Server happy. Ugly but fast :]
- */
- char* escaped = new char[(req->query.p.front().length() * 2) + 1];
- char* escend = escaped;
- for (std::string::iterator p = req->query.p.front().begin(); p < req->query.p.front().end(); p++)
- {
- if (*p == '\'')
- {
- *escend = *p;
- escend++;
- *escend = *p;
- }
- *escend = *p;
- escend++;
- }
- *escend = 0;
-
- for (char* n = escaped; *n; n++)
- {
- *queryend = *n;
- queryend++;
- }
- delete[] escaped;
- req->query.p.pop_front();
- }
- else
- break;
- }
- else
- {
- *queryend = req->query.q[i];
- queryend++;
- }
- }
- *queryend = 0;
- req->query.q = query;
-
- MsSQLResult* res = new MsSQLResult((Module*)mod, req->source, req->id);
- res->dbid = host.id;
- res->query = req->query.q;
-
- char* msquery = strdup(req->query.q.data());
- LoggingMutex->Lock();
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "doing Query: %s",msquery);
- LoggingMutex->Unlock();
- if (tds_submit_query(sock, msquery) != TDS_SUCCEED)
- {
- std::string error("failed to execute: "+std::string(req->query.q.data()));
- delete[] query;
- delete res;
- free(msquery);
- return SQLerror(SQL_QSEND_FAIL, error);
- }
- delete[] query;
- free(msquery);
-
- int tds_res;
- while (tds_process_tokens(sock, &tds_res, NULL, TDS_TOKEN_RESULTS) == TDS_SUCCEED)
- {
- //ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "<******> result type: %d", tds_res);
- //ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "AFFECTED ROWS: %d", sock->rows_affected);
- switch (tds_res)
- {
- case TDS_ROWFMT_RESULT:
- break;
-
- case TDS_DONE_RESULT:
- if (sock->rows_affected > -1)
- {
- for (int c = 0; c < sock->rows_affected; c++) res->UpdateAffectedCount();
- continue;
- }
- break;
-
- case TDS_ROW_RESULT:
- while (tds_process_tokens(sock, &tds_res, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW) == TDS_SUCCEED)
- {
- if (tds_res != TDS_ROW_RESULT)
- break;
-
- if (!sock->current_results)
- continue;
-
- if (sock->res_info->row_count > 0)
- {
- int cols = sock->res_info->num_cols;
- char** name = new char*[512];
- char** data = new char*[512];
- for (int j=0; j<cols; j++)
- {
- TDSCOLUMN* col = sock->current_results->columns[j];
- name[j] = col->column_name;
-
- int ctype;
- int srclen;
- unsigned char* src;
- CONV_RESULT dres;
- ctype = tds_get_conversion_type(col->column_type, col->column_size);
-#if _TDSVER >= 82
- src = col->column_data;
-#else
- src = &(sock->current_results->current_row[col->column_offset]);
-#endif
- srclen = col->column_cur_size;
- tds_convert(sock->tds_ctx, ctype, (TDS_CHAR *) src, srclen, SYBCHAR, &dres);
- data[j] = (char*)dres.ib;
- }
- ResultReady(res, cols, data, name);
- }
- }
- break;
-
- default:
- break;
- }
- }
- ResultsMutex->Lock();
- results.push_back(res);
- ResultsMutex->Unlock();
- return SQLerror();
- }
-
- static int HandleMessage(const TDSCONTEXT * pContext, TDSSOCKET * pTdsSocket, TDSMESSAGE * pMessage)
- {
- SQLConn* sc = (SQLConn*)pContext->parent;
- LoggingMutex->Lock();
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Message for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
- LoggingMutex->Unlock();
- return 0;
- }
-
- static int HandleError(const TDSCONTEXT * pContext, TDSSOCKET * pTdsSocket, TDSMESSAGE * pMessage)
- {
- SQLConn* sc = (SQLConn*)pContext->parent;
- LoggingMutex->Lock();
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
- LoggingMutex->Unlock();
- return 0;
- }
-
- void ResultReady(MsSQLResult *res, int cols, char **data, char **colnames)
- {
- res->AddRow(cols, data, colnames);
- }
-
- void AffectedReady(MsSQLResult *res)
- {
- res->UpdateAffectedCount();
- }
-
- bool OpenDB()
- {
- CloseDB();
-
- TDSCONNECTION* conn = NULL;
-
- login = tds_alloc_login();
- tds_set_app(login, "TSQL");
- tds_set_library(login,"TDS-Library");
- tds_set_host(login, "");
- tds_set_server(login, host.host.c_str());
- tds_set_server_addr(login, host.host.c_str());
- tds_set_user(login, host.user.c_str());
- tds_set_passwd(login, host.pass.c_str());
- tds_set_port(login, host.port);
- tds_set_packet(login, 512);
-
- context = tds_alloc_context(this);
- context->msg_handler = HandleMessage;
- context->err_handler = HandleError;
-
- sock = tds_alloc_socket(context, 512);
- tds_set_parent(sock, NULL);
-
- conn = tds_read_config_info(NULL, login, context->locale);
-
- if (tds_connect(sock, conn) == TDS_SUCCEED)
- {
- tds_free_connection(conn);
- return 1;
- }
- tds_free_connection(conn);
- return 0;
- }
-
- void CloseDB()
- {
- if (sock)
- {
- tds_free_socket(sock);
- sock = NULL;
- }
- if (context)
- {
- tds_free_context(context);
- context = NULL;
- }
- if (login)
- {
- tds_free_login(login);
- login = NULL;
- }
- }
-
- SQLhost GetConfHost()
- {
- return host;
- }
-
- void SendResults()
- {
- while (results.size())
- {
- MsSQLResult* res = results[0];
- ResultsMutex->Lock();
- if (res->dest)
- {
- res->Send();
- }
- else
- {
- /* If the client module is unloaded partway through a query then the provider will set
- * the pointer to NULL. We cannot just cancel the query as the result will still come
- * through at some point...and it could get messy if we play with invalid pointers...
- */
- delete res;
- }
- results.pop_front();
- ResultsMutex->Unlock();
- }
- }
-
- void ClearResults()
- {
- while (results.size())
- {
- MsSQLResult* res = results[0];
- delete res;
- results.pop_front();
- }
- }
-
- void DoLeadingQuery()
- {
- SQLrequest* req = queue.front();
- req->error = Query(req);
- }
-
-};
-
-
-class ModuleMsSQL : public Module
-{
- private:
- unsigned long currid;
- QueryThread* queryDispatcher;
- ServiceProvider sqlserv;
-
- public:
- ModuleMsSQL()
- : currid(0), sqlserv(this, "SQL/mssql", SERVICE_DATA)
- {
- LoggingMutex = new Mutex();
- ResultsMutex = new Mutex();
- queryDispatcher = new QueryThread(this);
- }
-
- void init() CXX11_OVERRIDE
- {
- ReadConf();
-
- ServerInstance->Threads->Start(queryDispatcher);
- }
-
- ~ModuleMsSQL()
- {
- queryDispatcher->join();
- delete queryDispatcher;
- ClearQueue();
- ClearAllConnections();
-
- delete LoggingMutex;
- delete ResultsMutex;
- }
-
- void SendQueue()
- {
- for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
- {
- iter->second->SendResults();
- }
- }
-
- void ClearQueue()
- {
- for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
- {
- iter->second->ClearResults();
- }
- }
-
- bool HasHost(const SQLhost &host)
- {
- for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
- {
- if (host == iter->second->GetConfHost())
- return true;
- }
- return false;
- }
-
- bool HostInConf(const SQLhost &h)
- {
- ConfigTagList tags = ServerInstance->Config->ConfTags("database");
- for (ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- SQLhost host;
- host.id = tag->getString("id");
- host.host = tag->getString("hostname");
- host.port = tag->getInt("port", 1433);
- host.name = tag->getString("name");
- host.user = tag->getString("username");
- host.pass = tag->getString("password");
- if (h == host)
- return true;
- }
- return false;
- }
-
- void ReadConf()
- {
- ClearOldConnections();
-
- ConfigTagList tags = ServerInstance->Config->ConfTags("database");
- for (ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- SQLhost host;
-
- host.id = tag->getString("id");
- host.host = tag->getString("hostname");
- host.port = tag->getInt("port", 1433);
- host.name = tag->getString("name");
- host.user = tag->getString("username");
- host.pass = tag->getString("password");
-
- if (HasHost(host))
- continue;
-
- this->AddConn(host);
- }
- }
-
- void AddConn(const SQLhost& hi)
- {
- if (HasHost(hi))
- {
- LoggingMutex->Lock();
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: A MsSQL connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str());
- LoggingMutex->Unlock();
- return;
- }
-
- SQLConn* newconn;
-
- newconn = new SQLConn(this, hi);
-
- connections.insert(std::make_pair(hi.id, newconn));
- }
-
- void ClearOldConnections()
- {
- ConnMap::iterator iter,safei;
- for (iter = connections.begin(); iter != connections.end(); iter++)
- {
- if (!HostInConf(iter->second->GetConfHost()))
- {
- delete iter->second;
- safei = iter;
- --iter;
- connections.erase(safei);
- }
- }
- }
-
- void ClearAllConnections()
- {
- for(ConnMap::iterator i = connections.begin(); i != connections.end(); ++i)
- delete i->second;
- connections.clear();
- }
-
- void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
- {
- queryDispatcher->LockQueue();
- ReadConf();
- queryDispatcher->UnlockQueueWakeup();
- }
-
- void OnRequest(Request& request) CXX11_OVERRIDE
- {
- if(strcmp(SQLREQID, request.id) == 0)
- {
- SQLrequest* req = (SQLrequest*)&request;
-
- queryDispatcher->LockQueue();
-
- ConnMap::iterator iter;
-
- if((iter = connections.find(req->dbid)) != connections.end())
- {
- req->id = NewID();
- iter->second->queue.push(new SQLrequest(*req));
- }
- else
- {
- req->error.Id(SQL_BAD_DBID);
- }
- queryDispatcher->UnlockQueueWakeup();
- }
- }
-
- unsigned long NewID()
- {
- if (currid+1 == 0)
- currid++;
-
- return ++currid;
- }
-
- Version GetVersion() CXX11_OVERRIDE
- {
- return Version("MsSQL provider", VF_VENDOR);
- }
-
-};
-
-void QueryThread::OnNotify()
-{
- Parent->SendQueue();
-}
-
-void QueryThread::Run()
-{
- this->LockQueue();
- while (this->GetExitFlag() == false)
- {
- SQLConn* conn = NULL;
- for (ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
- {
- if (i->second->queue.totalsize())
- {
- conn = i->second;
- break;
- }
- }
- if (conn)
- {
- this->UnlockQueue();
- conn->DoLeadingQuery();
- this->NotifyParent();
- this->LockQueue();
- conn->queue.pop();
- }
- else
- {
- this->WaitForQueue();
- }
- }
- this->UnlockQueue();
-}
-
-MODULE_INIT(ModuleMsSQL)
diff --git a/src/modules/extra/m_mysql.cpp b/src/modules/extra/m_mysql.cpp
index 3aed09416..39b0c369d 100644
--- a/src/modules/extra/m_mysql.cpp
+++ b/src/modules/extra/m_mysql.cpp
@@ -19,25 +19,32 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: execute("mysql_config --include" "MYSQL_CXXFLAGS")
+/// $LinkerFlags: execute("mysql_config --libs_r" "MYSQL_LDFLAGS" "-lmysqlclient")
-/* Stop mysql wanting to use long long */
-#define NO_CLIENT_LONG_LONG
+/// $PackageInfo: require_system("centos" "6.0" "6.99") mysql-devel
+/// $PackageInfo: require_system("centos" "7.0") mariadb-devel
+/// $PackageInfo: require_system("darwin") mysql-connector-c
+/// $PackageInfo: require_system("ubuntu") libmysqlclient-dev
+
+
+// Fix warnings about the use of `long long` on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+#endif
#include "inspircd.h"
#include <mysql.h>
#include "modules/sql.h"
#ifdef _WIN32
-# pragma comment(lib, "mysqlclient.lib")
-# pragma comment(lib, "advapi32.lib")
-# pragma comment(linker, "/NODEFAULTLIB:LIBCMT")
+# pragma comment(lib, "libmysql.lib")
#endif
/* VERSION 3 API: With nonblocking (threaded) requests */
-/* $CompileFlags: exec("mysql_config --include") */
-/* $LinkerFlags: exec("mysql_config --libs_r") rpath("mysql_config --libs_r") */
-
/* THE NONBLOCKING MYSQL API!
*
* MySQL provides no nonblocking (asyncronous) API of its own, and its developers recommend
@@ -91,7 +98,7 @@ struct RQueueItem
RQueueItem(SQLQuery* Q, MySQLresult* R) : q(Q), r(R) {}
};
-typedef std::map<std::string, SQLConnection*> ConnMap;
+typedef insp::flat_map<std::string, SQLConnection*> ConnMap;
typedef std::deque<QQueueItem> QueryQueue;
typedef std::deque<RQueueItem> ResultQueue;
@@ -257,6 +264,12 @@ class SQLConnection : public SQLProvider
bool rv = mysql_real_connect(connection, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), port, NULL, 0);
if (!rv)
return rv;
+
+ // Enable character set settings
+ std::string charset = config->getString("charset");
+ if ((!charset.empty()) && (mysql_set_character_set(connection, charset.c_str())))
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not set character set to \"%s\"", charset.c_str());
+
std::string initquery;
if (config->readString("initialquery", initquery))
{
@@ -380,7 +393,7 @@ ModuleSQL::ModuleSQL()
void ModuleSQL::init()
{
Dispatcher = new DispatcherThread(this);
- ServerInstance->Threads->Start(Dispatcher);
+ ServerInstance->Threads.Start(Dispatcher);
}
ModuleSQL::~ModuleSQL()
diff --git a/src/modules/extra/m_pgsql.cpp b/src/modules/extra/m_pgsql.cpp
index e6abbfcf9..5f6f6e30f 100644
--- a/src/modules/extra/m_pgsql.cpp
+++ b/src/modules/extra/m_pgsql.cpp
@@ -21,16 +21,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: -Iexecute("pg_config --includedir" "POSTGRESQL_INCLUDE_DIR")
+/// $LinkerFlags: -Lexecute("pg_config --libdir" "POSTGRESQL_LIBRARY_DIR") -lpq
+
+/// $PackageInfo: require_system("centos") postgresql-devel
+/// $PackageInfo: require_system("darwin") postgresql
+/// $PackageInfo: require_system("ubuntu") libpq-dev
+
#include "inspircd.h"
#include <cstdlib>
-#include <sstream>
#include <libpq-fe.h>
#include "modules/sql.h"
-/* $CompileFlags: -Iexec("pg_config --includedir") eval("my $s = `pg_config --version`;$s =~ /^.*?(\d+)\.(\d+)\.(\d+).*?$/;my $v = hex(sprintf("0x%02x%02x%02x", $1, $2, $3));print "-DPGSQL_HAS_ESCAPECONN" if(($v >= 0x080104) || ($v >= 0x07030F && $v < 0x070400) || ($v >= 0x07040D && $v < 0x080000) || ($v >= 0x080008 && $v < 0x080100));") */
-/* $LinkerFlags: -Lexec("pg_config --libdir") -lpq */
-
/* SQLConn rewritten by peavey to
* use EventHandler instead of
* BufferedSocket. This is much neater
@@ -42,7 +45,7 @@
class SQLConn;
class ModulePgSQL;
-typedef std::map<std::string, SQLConn*> ConnMap;
+typedef insp::flat_map<std::string, SQLConn*> ConnMap;
/* CREAD, Connecting and wants read event
* CWRITE, Connecting and wants write event
@@ -58,7 +61,7 @@ class ReconnectTimer : public Timer
private:
ModulePgSQL* mod;
public:
- ReconnectTimer(ModulePgSQL* m) : Timer(5, ServerInstance->Time(), false), mod(m)
+ ReconnectTimer(ModulePgSQL* m) : Timer(5, false), mod(m)
{
}
bool Tick(time_t TIME);
@@ -179,18 +182,19 @@ class SQLConn : public SQLProvider, public EventHandler
}
}
- void HandleEvent(EventType et, int errornum)
+ void OnEventHandlerRead() CXX11_OVERRIDE
{
- switch (et)
- {
- case EVENT_READ:
- case EVENT_WRITE:
- DoEvent();
- break;
+ DoEvent();
+ }
- case EVENT_ERROR:
- DelayReconnect();
- }
+ void OnEventHandlerWrite() CXX11_OVERRIDE
+ {
+ DoEvent();
+ }
+
+ void OnEventHandlerError(int errornum) CXX11_OVERRIDE
+ {
+ DelayReconnect();
}
std::string GetDSN()
@@ -412,14 +416,10 @@ restart:
{
std::string parm = p[param++];
std::vector<char> buffer(parm.length() * 2 + 1);
-#ifdef PGSQL_HAS_ESCAPECONN
int error;
size_t escapedsize = PQescapeStringConn(sql, &buffer[0], parm.data(), parm.length(), &error);
if (error)
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
-#else
- size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
-#endif
res.append(&buffer[0], escapedsize);
}
}
@@ -447,14 +447,10 @@ restart:
{
std::string parm = it->second;
std::vector<char> buffer(parm.length() * 2 + 1);
-#ifdef PGSQL_HAS_ESCAPECONN
int error;
size_t escapedsize = PQescapeStringConn(sql, &buffer[0], parm.data(), parm.length(), &error);
if (error)
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
-#else
- size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
-#endif
res.append(&buffer[0], escapedsize);
}
}
diff --git a/src/modules/extra/m_regex_pcre.cpp b/src/modules/extra/m_regex_pcre.cpp
index 9ae6719ba..e270ca039 100644
--- a/src/modules/extra/m_regex_pcre.cpp
+++ b/src/modules/extra/m_regex_pcre.cpp
@@ -17,14 +17,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: execute("pcre-config --cflags" "PCRE_CXXFLAGS")
+/// $LinkerFlags: execute("pcre-config --libs" "PCRE_LDFLAGS" "-lpcre")
+
+/// $PackageInfo: require_system("centos") pcre-devel pkgconfig
+/// $PackageInfo: require_system("darwin") pcre pkg-config
+/// $PackageInfo: require_system("ubuntu") libpcre3-dev pkg-config
+
#include "inspircd.h"
#include <pcre.h>
#include "modules/regex.h"
-/* $CompileFlags: exec("pcre-config --cflags") */
-/* $LinkerFlags: exec("pcre-config --libs") rpath("pcre-config --libs") -lpcre */
-
#ifdef _WIN32
# pragma comment(lib, "libpcre.lib")
#endif
diff --git a/src/modules/extra/m_regex_re2.cpp b/src/modules/extra/m_regex_re2.cpp
index 544e3060e..2f0ee2998 100644
--- a/src/modules/extra/m_regex_re2.cpp
+++ b/src/modules/extra/m_regex_re2.cpp
@@ -17,18 +17,26 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: find_compiler_flags("re2" "")
+/// $LinkerFlags: find_linker_flags("re2" "-lre2")
+
+/// $PackageInfo: require_system("darwin") pkg-config re2
+/// $PackageInfo: require_system("ubuntu" "15.10") libre2-dev pkg-config
-#if defined __GNUC__
-# pragma GCC diagnostic ignored "-Wshadow"
-#endif
#include "inspircd.h"
#include "modules/regex.h"
-#include <re2/re2.h>
+// Fix warnings about the use of `long long` on C++03 and
+// shadowing on GCC.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+# pragma GCC diagnostic ignored "-Wshadow"
+#endif
-/* $CompileFlags: -std=c++11 */
-/* $LinkerFlags: -lre2 */
+#include <re2/re2.h>
class RE2Regex : public Regex
{
diff --git a/src/modules/extra/m_regex_stdlib.cpp b/src/modules/extra/m_regex_stdlib.cpp
index 8e7bd0da2..7a888ed72 100644
--- a/src/modules/extra/m_regex_stdlib.cpp
+++ b/src/modules/extra/m_regex_stdlib.cpp
@@ -16,12 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: -std=c++11
+
+
#include "inspircd.h"
#include "modules/regex.h"
#include <regex>
-/* $CompileFlags: -std=c++11 */
-
class StdRegex : public Regex
{
std::regex regexcl;
diff --git a/src/modules/extra/m_regex_tre.cpp b/src/modules/extra/m_regex_tre.cpp
index 8a1d54248..e2eafcd01 100644
--- a/src/modules/extra/m_regex_tre.cpp
+++ b/src/modules/extra/m_regex_tre.cpp
@@ -17,15 +17,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: find_compiler_flags("tre")
+/// $LinkerFlags: find_linker_flags("tre" "-ltre")
+
+/// $PackageInfo: require_system("darwin") pkg-config tre
+/// $PackageInfo: require_system("ubuntu") libtre-dev pkg-config
#include "inspircd.h"
#include "modules/regex.h"
#include <sys/types.h>
#include <tre/regex.h>
-/* $CompileFlags: pkgconfincludes("tre","tre/regex.h","") */
-/* $LinkerFlags: pkgconflibs("tre","/libtre.so","-ltre") rpath("pkg-config --libs tre") */
-
class TRERegex : public Regex
{
regex_t regbuf;
diff --git a/src/modules/extra/m_sqlite3.cpp b/src/modules/extra/m_sqlite3.cpp
index 1c213e8e0..ac7146e38 100644
--- a/src/modules/extra/m_sqlite3.cpp
+++ b/src/modules/extra/m_sqlite3.cpp
@@ -19,20 +19,31 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: find_compiler_flags("sqlite3")
+/// $LinkerFlags: find_linker_flags("sqlite3" "-lsqlite3")
+
+/// $PackageInfo: require_system("centos") pkgconfig sqlite-devel
+/// $PackageInfo: require_system("darwin") pkg-config sqlite3
+/// $PackageInfo: require_system("ubuntu") libsqlite3-dev pkg-config
#include "inspircd.h"
-#include <sqlite3.h>
#include "modules/sql.h"
+// Fix warnings about the use of `long long` on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+#endif
+
+#include <sqlite3.h>
+
#ifdef _WIN32
# pragma comment(lib, "sqlite3.lib")
#endif
-/* $CompileFlags: pkgconfversion("sqlite3","3.3") pkgconfincludes("sqlite3","/sqlite3.h","") -Wno-pedantic */
-/* $LinkerFlags: pkgconflibs("sqlite3","/libsqlite3.so","-lsqlite3") */
-
class SQLConn;
-typedef std::map<std::string, SQLConn*> ConnMap;
+typedef insp::flat_map<std::string, SQLConn*> ConnMap;
class SQLite3Result : public SQLResult
{
@@ -83,15 +94,20 @@ class SQLConn : public SQLProvider
std::string host = tag->getString("hostname");
if (sqlite3_open_v2(host.c_str(), &conn, SQLITE_OPEN_READWRITE, 0) != SQLITE_OK)
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not open DB with id: " + tag->getString("id"));
+ // Even in case of an error conn must be closed
+ sqlite3_close(conn);
conn = NULL;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not open DB with id: " + tag->getString("id"));
}
}
~SQLConn()
{
- sqlite3_interrupt(conn);
- sqlite3_close(conn);
+ if (conn)
+ {
+ sqlite3_interrupt(conn);
+ sqlite3_close(conn);
+ }
}
void Query(SQLQuery* query, const std::string& q)
diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp
index a2c58cf86..c1ffdfb4c 100644
--- a/src/modules/extra/m_ssl_gnutls.cpp
+++ b/src/modules/extra/m_ssl_gnutls.cpp
@@ -20,63 +20,96 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/// $CompilerFlags: find_compiler_flags("gnutls")
+/// $CompilerFlags: require_version("gnutls" "1.0" "2.12") execute("libgcrypt-config --cflags" "LIBGCRYPT_CXXFLAGS")
+
+/// $LinkerFlags: find_linker_flags("gnutls" "-lgnutls")
+/// $LinkerFlags: require_version("gnutls" "1.0" "2.12") execute("libgcrypt-config --libs" "LIBGCRYPT_LDFLAGS")
+
+/// $PackageInfo: require_system("centos") gnutls-devel pkgconfig
+/// $PackageInfo: require_system("darwin") gnutls pkg-config
+/// $PackageInfo: require_system("ubuntu" "1.0" "13.10") libgcrypt11-dev
+/// $PackageInfo: require_system("ubuntu" "14.04") gnutls-bin libgnutls-dev pkg-config
#include "inspircd.h"
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
#include "modules/ssl.h"
#include <memory>
-#if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 9) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 9 && GNUTLS_VERSION_PATCH >= 8))
+// Fix warnings about the use of commas at end of enumerator lists on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-extensions"
+#elif defined __GNUC__
+# if __GNUC__ < 6
+# pragma GCC diagnostic ignored "-pedantic"
+# else
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+# endif
+#endif
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#ifndef GNUTLS_VERSION_NUMBER
+#define GNUTLS_VERSION_NUMBER LIBGNUTLS_VERSION_NUMBER
+#define GNUTLS_VERSION LIBGNUTLS_VERSION
+#endif
+
+// Check if the GnuTLS library is at least version major.minor.patch
+#define INSPIRCD_GNUTLS_HAS_VERSION(major, minor, patch) (GNUTLS_VERSION_NUMBER >= ((major << 16) | (minor << 8) | patch))
+
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 9, 8)
#define GNUTLS_HAS_MAC_GET_ID
#include <gnutls/crypto.h>
#endif
-#if (GNUTLS_VERSION_MAJOR > 2 || GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 12)
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 12, 0)
# define GNUTLS_HAS_RND
#else
# include <gcrypt.h>
#endif
#ifdef _WIN32
-# pragma comment(lib, "libgnutls.lib")
-# pragma comment(lib, "libgcrypt.lib")
-# pragma comment(lib, "libgpg-error.lib")
-# pragma comment(lib, "user32.lib")
-# pragma comment(lib, "advapi32.lib")
-# pragma comment(lib, "libgcc.lib")
-# pragma comment(lib, "libmingwex.lib")
-# pragma comment(lib, "gdi32.lib")
-#endif
-
-/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") eval("print `libgcrypt-config --cflags | tr -d \r` if `pkg-config --modversion gnutls 2>/dev/null | tr -d \r` lt '2.12'") -Wno-pedantic */
-/* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") eval("print `libgcrypt-config --libs | tr -d \r` if `pkg-config --modversion gnutls 2>/dev/null | tr -d \r` lt '2.12'") */
-
-#ifndef GNUTLS_VERSION_MAJOR
-#define GNUTLS_VERSION_MAJOR LIBGNUTLS_VERSION_MAJOR
-#define GNUTLS_VERSION_MINOR LIBGNUTLS_VERSION_MINOR
-#define GNUTLS_VERSION_PATCH LIBGNUTLS_VERSION_PATCH
+# pragma comment(lib, "libgnutls-30.lib")
#endif
// These don't exist in older GnuTLS versions
-#if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 1) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 1 && GNUTLS_VERSION_PATCH >= 7))
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 1, 7)
#define GNUTLS_NEW_PRIO_API
#endif
-#if(GNUTLS_VERSION_MAJOR < 2)
+#if (!INSPIRCD_GNUTLS_HAS_VERSION(2, 0, 0))
typedef gnutls_certificate_credentials_t gnutls_certificate_credentials;
typedef gnutls_dh_params_t gnutls_dh_params;
#endif
-enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED };
+enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_HANDSHAKEN };
-#if (GNUTLS_VERSION_MAJOR > 2 || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR >= 12))
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 12, 0)
+#define INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
#define GNUTLS_NEW_CERT_CALLBACK_API
typedef gnutls_retr2_st cert_cb_last_param_type;
#else
typedef gnutls_retr_st cert_cb_last_param_type;
#endif
+#if INSPIRCD_GNUTLS_HAS_VERSION(3, 3, 5)
+#define INSPIRCD_GNUTLS_HAS_RECV_PACKET
+#endif
+
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 99, 0)
+// The second parameter of gnutls_init() has changed in 2.99.0 from gnutls_connection_end_t to unsigned int
+// (it became a general flags parameter) and the enum has been deprecated and generates a warning on use.
+typedef unsigned int inspircd_gnutls_session_init_flags_t;
+#else
+typedef gnutls_connection_end_t inspircd_gnutls_session_init_flags_t;
+#endif
+
+#if INSPIRCD_GNUTLS_HAS_VERSION(3, 1, 9)
+#define INSPIRCD_GNUTLS_HAS_CORK
+#endif
+
+static Module* thismod;
+
class RandGen : public HandlerBase2<void, char*, size_t>
{
public:
@@ -158,6 +191,10 @@ namespace GnuTLS
hash = GNUTLS_DIG_MD5;
else if (hashname == "sha1")
hash = GNUTLS_DIG_SHA1;
+#ifdef INSPIRCD_GNUTLS_ENABLE_SHA256_FINGERPRINT
+ else if (hashname == "sha256")
+ hash = GNUTLS_DIG_SHA256;
+#endif
else
throw Exception("Unknown hash type " + hashname);
#endif
@@ -185,14 +222,6 @@ namespace GnuTLS
return dh;
}
- /** Generate */
- static std::auto_ptr<DHParams> Generate(unsigned int bits)
- {
- std::auto_ptr<DHParams> dh(new DHParams);
- ThrowOnError(gnutls_dh_params_generate2(dh->dh_params, bits), "Unable to generate DH params");
- return dh;
- }
-
~DHParams()
{
gnutls_dh_params_deinit(dh_params);
@@ -329,6 +358,40 @@ namespace GnuTLS
{
gnutls_priority_set(sess, priority);
}
+
+ static const char* GetDefault()
+ {
+ return "NORMAL:%SERVER_PRECEDENCE:-VERS-SSL3.0";
+ }
+
+ static std::string RemoveUnknownTokens(const std::string& prio)
+ {
+ std::string ret;
+ irc::sepstream ss(prio, ':');
+ for (std::string token; ss.GetToken(token); )
+ {
+ // Save current position so we can revert later if needed
+ const std::string::size_type prevpos = ret.length();
+ // Append next token
+ if (!ret.empty())
+ ret.push_back(':');
+ ret.append(token);
+
+ gnutls_priority_t test;
+ if (gnutls_priority_init(&test, ret.c_str(), NULL) < 0)
+ {
+ // The new token broke the priority string, revert to the previously working one
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Priority string token not recognized: \"%s\"", token.c_str());
+ ret.erase(prevpos);
+ }
+ else
+ {
+ // Worked
+ gnutls_priority_deinit(test);
+ }
+ }
+ return ret;
+ }
};
#else
/** Dummy class, used when gnutls_priority_set() is not available
@@ -338,7 +401,7 @@ namespace GnuTLS
public:
Priority(const std::string& priorities)
{
- if (priorities != "NORMAL")
+ if (priorities != GetDefault())
throw Exception("You've set a non-default priority string, but GnuTLS lacks support for it");
}
@@ -347,6 +410,17 @@ namespace GnuTLS
// Always set the default priorities
gnutls_set_default_priority(sess);
}
+
+ static const char* GetDefault()
+ {
+ return "NORMAL";
+ }
+
+ static std::string RemoveUnknownTokens(const std::string& prio)
+ {
+ // We don't do anything here because only NORMAL is accepted
+ return prio;
+ }
};
#endif
@@ -445,6 +519,51 @@ namespace GnuTLS
}
};
+ class DataReader
+ {
+ int retval;
+#ifdef INSPIRCD_GNUTLS_HAS_RECV_PACKET
+ gnutls_packet_t packet;
+
+ public:
+ DataReader(gnutls_session_t sess)
+ {
+ // Using the packet API avoids the final copy of the data which GnuTLS does if we supply
+ // our own buffer. Instead, we get the buffer containing the data from GnuTLS and copy it
+ // to the recvq directly from there in appendto().
+ retval = gnutls_record_recv_packet(sess, &packet);
+ }
+
+ void appendto(std::string& recvq)
+ {
+ // Copy data from GnuTLS buffers to recvq
+ gnutls_datum_t datum;
+ gnutls_packet_get(packet, &datum, NULL);
+ recvq.append(reinterpret_cast<const char*>(datum.data), datum.size);
+
+ gnutls_packet_deinit(packet);
+ }
+#else
+ char* const buffer;
+
+ public:
+ DataReader(gnutls_session_t sess)
+ : buffer(ServerInstance->GetReadBuffer())
+ {
+ // Read data from GnuTLS buffers into ReadBuffer
+ retval = gnutls_record_recv(sess, buffer, ServerInstance->Config->NetBufferSize);
+ }
+
+ void appendto(std::string& recvq)
+ {
+ // Copy data from ReadBuffer to recvq
+ recvq.append(buffer, retval);
+ }
+#endif
+
+ int ret() const { return retval; }
+ };
+
class Profile : public refcountbase
{
/** Name of this profile
@@ -467,14 +586,25 @@ namespace GnuTLS
*/
Priority priority;
+ /** Rough max size of records to send
+ */
+ const unsigned int outrecsize;
+
+ /** True to request a client certificate as a server
+ */
+ const bool requestclientcert;
+
Profile(const std::string& profilename, const std::string& certstr, const std::string& keystr,
std::auto_ptr<DHParams>& DH, unsigned int mindh, const std::string& hashstr,
- const std::string& priostr, std::auto_ptr<X509CertList>& CA, std::auto_ptr<X509CRL>& CRL)
+ const std::string& priostr, std::auto_ptr<X509CertList>& CA, std::auto_ptr<X509CRL>& CRL,
+ unsigned int recsize, bool Requestclientcert)
: name(profilename)
, x509cred(certstr, keystr)
, min_dh_bits(mindh)
, hash(hashstr)
, priority(priostr)
+ , outrecsize(recsize)
+ , requestclientcert(Requestclientcert)
{
x509cred.SetDH(DH);
x509cred.SetCA(CA, CRL);
@@ -489,24 +619,40 @@ namespace GnuTLS
return ret;
}
+ static std::string GetPrioStr(const std::string& profilename, ConfigTag* tag)
+ {
+ // Use default priority string if this tag does not specify one
+ std::string priostr = GnuTLS::Priority::GetDefault();
+ bool found = tag->readString("priority", priostr);
+ // If the prio string isn't set in the config don't be strict about the default one because it doesn't work on all versions of GnuTLS
+ if (!tag->getBool("strictpriority", found))
+ {
+ std::string stripped = GnuTLS::Priority::RemoveUnknownTokens(priostr);
+ if (stripped.empty())
+ {
+ // Stripping failed, act as if a prio string wasn't set
+ stripped = GnuTLS::Priority::RemoveUnknownTokens(GnuTLS::Priority::GetDefault());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Priority string for profile \"%s\" contains unknown tokens and stripping it didn't yield a working one either, falling back to \"%s\"", profilename.c_str(), stripped.c_str());
+ }
+ else if ((found) && (stripped != priostr))
+ {
+ // Prio string was set in the config and we ended up with something that works but different
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Priority string for profile \"%s\" contains unknown tokens, stripped to \"%s\"", profilename.c_str(), stripped.c_str());
+ }
+ priostr.swap(stripped);
+ }
+ return priostr;
+ }
+
public:
static reference<Profile> Create(const std::string& profilename, ConfigTag* tag)
{
std::string certstr = ReadFile(tag->getString("certfile", "cert.pem"));
std::string keystr = ReadFile(tag->getString("keyfile", "key.pem"));
- std::auto_ptr<DHParams> dh;
- int gendh = tag->getInt("gendh");
- if (gendh)
- {
- gendh = (gendh < 1024 ? 1024 : gendh);
- dh = DHParams::Generate(gendh);
- }
- else
- dh = DHParams::Import(ReadFile(tag->getString("dhfile", "dhparams.pem")));
+ std::auto_ptr<DHParams> dh = DHParams::Import(ReadFile(tag->getString("dhfile", "dhparams.pem")));
- // Use default priority string if this tag does not specify one
- std::string priostr = tag->getString("priority", "NORMAL");
+ std::string priostr = GetPrioStr(profilename, tag);
unsigned int mindh = tag->getInt("mindhbits", 1024);
std::string hashstr = tag->getString("hash", "md5");
@@ -523,7 +669,16 @@ namespace GnuTLS
crl.reset(new X509CRL(ReadFile(filename)));
}
- return new Profile(profilename, certstr, keystr, dh, mindh, hashstr, priostr, ca, crl);
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ // If cork support is available outrecsize represents the (rough) max amount of data we give GnuTLS while corked
+ unsigned int outrecsize = tag->getInt("outrecsize", 2048, 512);
+#else
+ unsigned int outrecsize = tag->getInt("outrecsize", 2048, 512, 16384);
+#endif
+
+ const bool requestclientcert = tag->getBool("requestclientcert", true);
+
+ return new Profile(profilename, certstr, keystr, dh, mindh, hashstr, priostr, ca, crl, outrecsize, requestclientcert);
}
/** Set up the given session with the settings in this profile
@@ -533,11 +688,16 @@ namespace GnuTLS
priority.SetupSession(sess);
x509cred.SetupSession(sess);
gnutls_dh_set_prime_bits(sess, min_dh_bits);
+
+ // Request client certificate if enabled and we are a server, no-op if we're a client
+ if (requestclientcert)
+ gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST);
}
const std::string& GetName() const { return name; }
X509Credentials& GetX509Credentials() { return x509cred; }
gnutls_digest_algorithm_t GetHash() const { return hash.get(); }
+ unsigned int GetOutgoingRecordSize() const { return outrecsize; }
};
}
@@ -547,19 +707,9 @@ class GnuTLSIOHook : public SSLIOHook
gnutls_session_t sess;
issl_status status;
reference<GnuTLS::Profile> profile;
-
- void InitSession(StreamSocket* user, bool me_server)
- {
- gnutls_init(&sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
-
- profile->SetupSession(sess);
- gnutls_transport_set_ptr(sess, reinterpret_cast<gnutls_transport_ptr_t>(user));
- gnutls_transport_set_push_function(sess, gnutls_push_wrapper);
- gnutls_transport_set_pull_function(sess, gnutls_pull_wrapper);
-
- if (me_server)
- gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
- }
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ size_t gbuffersize;
+#endif
void CloseSession()
{
@@ -573,7 +723,8 @@ class GnuTLSIOHook : public SSLIOHook
status = ISSL_NONE;
}
- bool Handshake(StreamSocket* user)
+ // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
+ int Handshake(StreamSocket* user)
{
int ret = gnutls_handshake(this->sess);
@@ -582,28 +733,27 @@ class GnuTLSIOHook : public SSLIOHook
if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
{
// Handshake needs resuming later, read() or write() would have blocked.
+ this->status = ISSL_HANDSHAKING;
if (gnutls_record_get_direction(this->sess) == 0)
{
// gnutls_handshake() wants to read() again.
- this->status = ISSL_HANDSHAKING_READ;
SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
}
else
{
// gnutls_handshake() wants to write() again.
- this->status = ISSL_HANDSHAKING_WRITE;
SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
}
+
+ return 0;
}
else
{
user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret)));
CloseSession();
- this->status = ISSL_CLOSING;
+ return -1;
}
-
- return false;
}
else
{
@@ -615,7 +765,7 @@ class GnuTLSIOHook : public SSLIOHook
// Finish writing, if any left
SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
- return true;
+ return 1;
}
}
@@ -685,11 +835,23 @@ class GnuTLSIOHook : public SSLIOHook
goto info_done_dealloc;
}
- gnutls_x509_crt_get_dn(cert, str, &name_size);
- certinfo->dn = str;
+ if (gnutls_x509_crt_get_dn(cert, str, &name_size) == 0)
+ {
+ std::string& dn = certinfo->dn;
+ dn = str;
+ // Make sure there are no chars in the string that we consider invalid
+ if (dn.find_first_of("\r\n") != std::string::npos)
+ dn.clear();
+ }
- gnutls_x509_crt_get_issuer_dn(cert, str, &name_size);
- certinfo->issuer = str;
+ name_size = sizeof(str);
+ if (gnutls_x509_crt_get_issuer_dn(cert, str, &name_size) == 0)
+ {
+ std::string& issuer = certinfo->issuer;
+ issuer = str;
+ if (issuer.find_first_of("\r\n") != std::string::npos)
+ issuer.clear();
+ }
if ((ret = gnutls_x509_crt_get_fingerprint(cert, profile->GetHash(), digest, &digest_size)) < 0)
{
@@ -711,6 +873,59 @@ info_done_dealloc:
gnutls_x509_crt_deinit(cert);
}
+ // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error
+ int PrepareIO(StreamSocket* sock)
+ {
+ if (status == ISSL_HANDSHAKEN)
+ return 1;
+ else if (status == ISSL_HANDSHAKING)
+ {
+ // The handshake isn't finished, try to finish it
+ return Handshake(sock);
+ }
+
+ CloseSession();
+ sock->SetError("No SSL session");
+ return -1;
+ }
+
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ int FlushBuffer(StreamSocket* sock)
+ {
+ // If GnuTLS has some data buffered, write it
+ if (gbuffersize)
+ return HandleWriteRet(sock, gnutls_record_uncork(this->sess, 0));
+ return 1;
+ }
+#endif
+
+ int HandleWriteRet(StreamSocket* sock, int ret)
+ {
+ if (ret > 0)
+ {
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ gbuffersize -= ret;
+ if (gbuffersize)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+#endif
+ return ret;
+ }
+ else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else // (ret < 0)
+ {
+ sock->SetError(gnutls_strerror(ret));
+ CloseSession();
+ return -1;
+ }
+ }
+
static const char* UnknownIfNULL(const char* str)
{
return str ? str : "UNKNOWN";
@@ -720,7 +935,7 @@ info_done_dealloc:
{
StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
#ifdef _WIN32
- GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook());
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
#endif
if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
@@ -752,11 +967,47 @@ info_done_dealloc:
return rv;
}
+#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+ static ssize_t VectorPush(gnutls_transport_ptr_t transportptr, const giovec_t* iov, int iovcnt)
+ {
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(transportptr);
+#ifdef _WIN32
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
+#endif
+
+ if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ {
+#ifdef _WIN32
+ gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+ errno = EAGAIN;
+#endif
+ return -1;
+ }
+
+ // Cast the giovec_t to iovec not to IOVector so the correct function is called on Windows
+ int ret = SocketEngine::WriteV(sock, reinterpret_cast<const iovec*>(iov), iovcnt);
+#ifdef _WIN32
+ // See the function above for more info about the usage of gnutls_transport_set_errno() on Windows
+ if (ret < 0)
+ gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+#endif
+
+ int size = 0;
+ for (int i = 0; i < iovcnt; i++)
+ size += iov[i].iov_len;
+
+ if (ret < size)
+ SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+ return ret;
+ }
+
+#else // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size)
{
StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
#ifdef _WIN32
- GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook());
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
#endif
if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
@@ -787,15 +1038,28 @@ info_done_dealloc:
SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
return rv;
}
+#endif // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
public:
- GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool outbound, const reference<GnuTLS::Profile>& sslprofile)
+ GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, inspircd_gnutls_session_init_flags_t flags, const reference<GnuTLS::Profile>& sslprofile)
: SSLIOHook(hookprov)
, sess(NULL)
, status(ISSL_NONE)
, profile(sslprofile)
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ , gbuffersize(0)
+#endif
{
- InitSession(sock, outbound);
+ gnutls_init(&sess, flags);
+ gnutls_transport_set_ptr(sess, reinterpret_cast<gnutls_transport_ptr_t>(sock));
+#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+ gnutls_transport_set_vec_push_function(sess, VectorPush);
+#else
+ gnutls_transport_set_push_function(sess, gnutls_push_wrapper);
+#endif
+ gnutls_transport_set_pull_function(sess, gnutls_pull_wrapper);
+ profile->SetupSession(sess);
+
sock->AddIOHook(this);
Handshake(sock);
}
@@ -807,35 +1071,21 @@ info_done_dealloc:
int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
{
- if (!this->sess)
- {
- CloseSession();
- user->SetError("No SSL session");
- return -1;
- }
-
- if (this->status == ISSL_HANDSHAKING_READ || this->status == ISSL_HANDSHAKING_WRITE)
- {
- // The handshake isn't finished, try to finish it.
-
- if (!Handshake(user))
- {
- if (this->status != ISSL_CLOSING)
- return 0;
- return -1;
- }
- }
+ // Finish handshake if needed
+ int prepret = PrepareIO(user);
+ if (prepret <= 0)
+ return prepret;
// If we resumed the handshake then this->status will be ISSL_HANDSHAKEN.
-
- if (this->status == ISSL_HANDSHAKEN)
{
- char* buffer = ServerInstance->GetReadBuffer();
- size_t bufsiz = ServerInstance->Config->NetBufferSize;
- int ret = gnutls_record_recv(this->sess, buffer, bufsiz);
+ GnuTLS::DataReader reader(sess);
+ int ret = reader.ret();
if (ret > 0)
{
- recvq.append(buffer, ret);
+ reader.appendto(recvq);
+ // Schedule a read if there is still data in the GnuTLS buffer
+ if (gnutls_record_check_pending(sess) > 0)
+ SocketEngine::ChangeEventMask(user, FD_ADD_TRIAL_READ);
return 1;
}
else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
@@ -855,81 +1105,83 @@ info_done_dealloc:
return -1;
}
}
- else if (this->status == ISSL_CLOSING)
- return -1;
-
- return 0;
}
- int OnStreamSocketWrite(StreamSocket* user, std::string& sendq) CXX11_OVERRIDE
+ int OnStreamSocketWrite(StreamSocket* user, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
{
- if (!this->sess)
- {
- CloseSession();
- user->SetError("No SSL session");
- return -1;
- }
+ // Finish handshake if needed
+ int prepret = PrepareIO(user);
+ if (prepret <= 0)
+ return prepret;
- if (this->status == ISSL_HANDSHAKING_WRITE || this->status == ISSL_HANDSHAKING_READ)
+ // Session is ready for transferring application data
+
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+ while (true)
{
- // The handshake isn't finished, try to finish it.
- Handshake(user);
- if (this->status != ISSL_CLOSING)
- return 0;
- return -1;
+ // If there is something in the GnuTLS buffer try to send() it
+ int ret = FlushBuffer(user);
+ if (ret <= 0)
+ return ret; // Couldn't flush entire buffer, retry later (or close on error)
+
+ // GnuTLS buffer is empty, if the sendq is empty as well then break to set FD_WANT_NO_WRITE
+ if (sendq.empty())
+ break;
+
+ // GnuTLS buffer is empty but sendq is not, begin sending data from the sendq
+ gnutls_record_cork(this->sess);
+ while ((!sendq.empty()) && (gbuffersize < profile->GetOutgoingRecordSize()))
+ {
+ const StreamSocket::SendQueue::Element& elem = sendq.front();
+ gbuffersize += elem.length();
+ ret = gnutls_record_send(this->sess, elem.data(), elem.length());
+ if (ret < 0)
+ {
+ CloseSession();
+ return -1;
+ }
+ sendq.pop_front();
+ }
}
-
+#else
int ret = 0;
- if (this->status == ISSL_HANDSHAKEN)
+ while (!sendq.empty())
{
- ret = gnutls_record_send(this->sess, sendq.data(), sendq.length());
+ FlattenSendQueue(sendq, profile->GetOutgoingRecordSize());
+ const StreamSocket::SendQueue::Element& buffer = sendq.front();
+ ret = HandleWriteRet(user, gnutls_record_send(this->sess, buffer.data(), buffer.length()));
- if (ret == (int)sendq.length())
- {
- SocketEngine::ChangeEventMask(user, FD_WANT_NO_WRITE);
- return 1;
- }
- else if (ret > 0)
- {
- sendq = sendq.substr(ret);
- SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
- return 0;
- }
- else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
+ if (ret <= 0)
+ return ret;
+ else if (ret < (int)buffer.length())
{
+ sendq.erase_front(ret);
SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
return 0;
}
- else // (ret < 0)
- {
- user->SetError(gnutls_strerror(ret));
- CloseSession();
- return -1;
- }
+
+ // Wrote entire record, continue sending
+ sendq.pop_front();
}
+#endif
- return 0;
+ SocketEngine::ChangeEventMask(user, FD_WANT_NO_WRITE);
+ return 1;
}
- void TellCiphersAndFingerprint(LocalUser* user)
+ void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
{
- if (sess)
- {
- std::string text = "*** You are connected using SSL cipher '";
-
- text += UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)));
- text.append("-").append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).append("-");
- text.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess)))).append("'");
-
- if (!certificate->fingerprint.empty())
- text += " and your SSL fingerprint is " + certificate->fingerprint;
-
- user->WriteNotice(text);
- }
+ if (!IsHandshakeDone())
+ return;
+ out.append(UnknownIfNULL(gnutls_protocol_get_name(gnutls_protocol_get_version(sess)))).push_back('-');
+ out.append(UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)))).push_back('-');
+ out.append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).push_back('-');
+ out.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess))));
}
GnuTLS::Profile* GetProfile() { return profile; }
+ bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); }
};
int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, cert_cb_last_param_type* st)
@@ -941,7 +1193,7 @@ int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_d
st->key_type = GNUTLS_PRIVKEY_X509;
#endif
StreamSocket* sock = reinterpret_cast<StreamSocket*>(gnutls_transport_get_ptr(sess));
- GnuTLS::X509Credentials& cred = static_cast<GnuTLSIOHook*>(sock->GetIOHook())->GetProfile()->GetX509Credentials();
+ GnuTLS::X509Credentials& cred = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod))->GetProfile()->GetX509Credentials();
st->ncerts = cred.certs.size();
st->cert.x509 = cred.certs.raw();
@@ -970,12 +1222,12 @@ class GnuTLSIOHookProvider : public refcountbase, public IOHookProvider
void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
{
- new GnuTLSIOHook(this, sock, true, profile);
+ new GnuTLSIOHook(this, sock, GNUTLS_SERVER, profile);
}
void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
{
- new GnuTLSIOHook(this, sock, false, profile);
+ new GnuTLSIOHook(this, sock, GNUTLS_CLIENT, profile);
}
};
@@ -1051,10 +1303,12 @@ class ModuleSSLGnuTLS : public Module
#ifndef GNUTLS_HAS_RND
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
#endif
+ thismod = this;
}
void init() CXX11_OVERRIDE
{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "GnuTLS lib version %s module was compiled for " GNUTLS_VERSION, gnutls_check_version(NULL));
ReadProfiles();
ServerInstance->GenRandom = &randhandler;
}
@@ -1085,7 +1339,7 @@ class ModuleSSLGnuTLS : public Module
{
LocalUser* user = IS_LOCAL(static_cast<User*>(item));
- if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == this)
+ if ((user) && (user->eh.GetModHook(this)))
{
// User is using SSL, they're a local user, and they're using one of *our* SSL ports.
// Potentially there could be multiple SSL modules loaded at once on different ports.
@@ -1099,11 +1353,12 @@ class ModuleSSLGnuTLS : public Module
return Version("Provides SSL support for clients", VF_VENDOR);
}
- void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
- IOHook* hook = user->eh.GetIOHook();
- if (hook && hook->prov->creator == this)
- static_cast<GnuTLSIOHook*>(hook)->TellCiphersAndFingerprint(user);
+ const GnuTLSIOHook* const iohook = static_cast<GnuTLSIOHook*>(user->eh.GetModHook(this));
+ if ((iohook) && (!iohook->IsHandshakeDone()))
+ return MOD_RES_DENY;
+ return MOD_RES_PASSTHRU;
}
};
diff --git a/src/modules/extra/m_ssl_mbedtls.cpp b/src/modules/extra/m_ssl_mbedtls.cpp
new file mode 100644
index 000000000..f3b5adfd5
--- /dev/null
+++ b/src/modules/extra/m_ssl_mbedtls.cpp
@@ -0,0 +1,935 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/// $LinkerFlags: -lmbedtls
+
+/// $PackageInfo: require_system("darwin") mbedtls
+/// $PackageInfo: require_system("ubuntu" "16.04") libmbedtls-dev
+
+
+#include "inspircd.h"
+#include "modules/ssl.h"
+
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/dhm.h>
+#include <mbedtls/ecp.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/error.h>
+#include <mbedtls/md.h>
+#include <mbedtls/pk.h>
+#include <mbedtls/ssl.h>
+#include <mbedtls/ssl_ciphersuites.h>
+#include <mbedtls/version.h>
+#include <mbedtls/x509.h>
+#include <mbedtls/x509_crt.h>
+#include <mbedtls/x509_crl.h>
+
+#ifdef INSPIRCD_MBEDTLS_LIBRARY_DEBUG
+#include <mbedtls/debug.h>
+#endif
+
+namespace mbedTLS
+{
+ class Exception : public ModuleException
+ {
+ public:
+ Exception(const std::string& reason)
+ : ModuleException(reason) { }
+ };
+
+ std::string ErrorToString(int errcode)
+ {
+ char buf[256];
+ mbedtls_strerror(errcode, buf, sizeof(buf));
+ return buf;
+ }
+
+ void ThrowOnError(int errcode, const char* msg)
+ {
+ if (errcode != 0)
+ {
+ std::string reason = msg;
+ reason.append(" :").append(ErrorToString(errcode));
+ throw Exception(reason);
+ }
+ }
+
+ template <typename T, void (*init)(T*), void (*deinit)(T*)>
+ class RAIIObj
+ {
+ T obj;
+
+ public:
+ RAIIObj()
+ {
+ init(&obj);
+ }
+
+ ~RAIIObj()
+ {
+ deinit(&obj);
+ }
+
+ T* get() { return &obj; }
+ const T* get() const { return &obj; }
+ };
+
+ typedef RAIIObj<mbedtls_entropy_context, mbedtls_entropy_init, mbedtls_entropy_free> Entropy;
+
+ class CTRDRBG : private RAIIObj<mbedtls_ctr_drbg_context, mbedtls_ctr_drbg_init, mbedtls_ctr_drbg_free>
+ {
+ public:
+ bool Seed(Entropy& entropy)
+ {
+ return (mbedtls_ctr_drbg_seed(get(), mbedtls_entropy_func, entropy.get(), NULL, 0) == 0);
+ }
+
+ void SetupConf(mbedtls_ssl_config* conf)
+ {
+ mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, get());
+ }
+ };
+
+ class DHParams : public RAIIObj<mbedtls_dhm_context, mbedtls_dhm_init, mbedtls_dhm_free>
+ {
+ public:
+ void set(const std::string& dhstr)
+ {
+ // Last parameter is buffer size, must include the terminating null
+ int ret = mbedtls_dhm_parse_dhm(get(), reinterpret_cast<const unsigned char*>(dhstr.c_str()), dhstr.size()+1);
+ ThrowOnError(ret, "Unable to import DH params");
+ }
+ };
+
+ class X509Key : public RAIIObj<mbedtls_pk_context, mbedtls_pk_init, mbedtls_pk_free>
+ {
+ public:
+ /** Import */
+ X509Key(const std::string& keystr)
+ {
+ int ret = mbedtls_pk_parse_key(get(), reinterpret_cast<const unsigned char*>(keystr.c_str()), keystr.size()+1, NULL, 0);
+ ThrowOnError(ret, "Unable to import private key");
+ }
+ };
+
+ class Ciphersuites
+ {
+ std::vector<int> list;
+
+ public:
+ Ciphersuites(const std::string& str)
+ {
+ // mbedTLS uses the ciphersuite format "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256" internally.
+ // This is a bit verbose, so we make life a bit simpler for admins by not requiring them to supply the static parts.
+ irc::sepstream ss(str, ':');
+ for (std::string token; ss.GetToken(token); )
+ {
+ // Prepend "TLS-" if not there
+ if (token.compare(0, 4, "TLS-", 4))
+ token.insert(0, "TLS-");
+
+ const int id = mbedtls_ssl_get_ciphersuite_id(token.c_str());
+ if (!id)
+ throw Exception("Unknown ciphersuite " + token);
+ list.push_back(id);
+ }
+ list.push_back(0);
+ }
+
+ const int* get() const { return &list.front(); }
+ bool empty() const { return (list.size() <= 1); }
+ };
+
+ class Curves
+ {
+ std::vector<mbedtls_ecp_group_id> list;
+
+ public:
+ Curves(const std::string& str)
+ {
+ irc::sepstream ss(str, ':');
+ for (std::string token; ss.GetToken(token); )
+ {
+ const mbedtls_ecp_curve_info* curve = mbedtls_ecp_curve_info_from_name(token.c_str());
+ if (!curve)
+ throw Exception("Unknown curve " + token);
+ list.push_back(curve->grp_id);
+ }
+ list.push_back(MBEDTLS_ECP_DP_NONE);
+ }
+
+ const mbedtls_ecp_group_id* get() const { return &list.front(); }
+ bool empty() const { return (list.size() <= 1); }
+ };
+
+ class X509CertList : public RAIIObj<mbedtls_x509_crt, mbedtls_x509_crt_init, mbedtls_x509_crt_free>
+ {
+ public:
+ /** Import or create empty */
+ X509CertList(const std::string& certstr, bool allowempty = false)
+ {
+ if ((allowempty) && (certstr.empty()))
+ return;
+ int ret = mbedtls_x509_crt_parse(get(), reinterpret_cast<const unsigned char*>(certstr.c_str()), certstr.size()+1);
+ ThrowOnError(ret, "Unable to load certificates");
+ }
+
+ bool empty() const { return (get()->raw.p != NULL); }
+ };
+
+ class X509CRL : public RAIIObj<mbedtls_x509_crl, mbedtls_x509_crl_init, mbedtls_x509_crl_free>
+ {
+ public:
+ X509CRL(const std::string& crlstr)
+ {
+ if (crlstr.empty())
+ return;
+ int ret = mbedtls_x509_crl_parse(get(), reinterpret_cast<const unsigned char*>(crlstr.c_str()), crlstr.size()+1);
+ ThrowOnError(ret, "Unable to load CRL");
+ }
+ };
+
+ class X509Credentials
+ {
+ /** Private key
+ */
+ X509Key key;
+
+ /** Certificate list, presented to the peer
+ */
+ X509CertList certs;
+
+ public:
+ X509Credentials(const std::string& certstr, const std::string& keystr)
+ : key(keystr)
+ , certs(certstr)
+ {
+ // Verify that one of the certs match the private key
+ bool found = false;
+ for (mbedtls_x509_crt* cert = certs.get(); cert; cert = cert->next)
+ {
+ if (mbedtls_pk_check_pair(&cert->pk, key.get()) == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ throw Exception("Public/private key pair does not match");
+ }
+
+ mbedtls_pk_context* getkey() { return key.get(); }
+ mbedtls_x509_crt* getcerts() { return certs.get(); }
+ };
+
+ class Context
+ {
+ mbedtls_ssl_config conf;
+
+#ifdef INSPIRCD_MBEDTLS_LIBRARY_DEBUG
+ static void DebugLogFunc(void* userptr, int level, const char* file, int line, const char* msg)
+ {
+ // Remove trailing \n
+ size_t len = strlen(msg);
+ if ((len > 0) && (msg[len-1] == '\n'))
+ len--;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s:%d %.*s", file, line, len, msg);
+ }
+#endif
+
+ public:
+ Context(CTRDRBG& ctrdrbg, unsigned int endpoint)
+ {
+ mbedtls_ssl_config_init(&conf);
+#ifdef INSPIRCD_MBEDTLS_LIBRARY_DEBUG
+ mbedtls_debug_set_threshold(INT_MAX);
+ mbedtls_ssl_conf_dbg(&conf, DebugLogFunc, NULL);
+#endif
+
+ // TODO: check ret of mbedtls_ssl_config_defaults
+ mbedtls_ssl_config_defaults(&conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
+ ctrdrbg.SetupConf(&conf);
+ }
+
+ ~Context()
+ {
+ mbedtls_ssl_config_free(&conf);
+ }
+
+ void SetMinDHBits(unsigned int mindh)
+ {
+ mbedtls_ssl_conf_dhm_min_bitlen(&conf, mindh);
+ }
+
+ void SetDHParams(DHParams& dh)
+ {
+ mbedtls_ssl_conf_dh_param_ctx(&conf, dh.get());
+ }
+
+ void SetX509CertAndKey(X509Credentials& x509cred)
+ {
+ mbedtls_ssl_conf_own_cert(&conf, x509cred.getcerts(), x509cred.getkey());
+ }
+
+ void SetCiphersuites(const Ciphersuites& ciphersuites)
+ {
+ mbedtls_ssl_conf_ciphersuites(&conf, ciphersuites.get());
+ }
+
+ void SetCurves(const Curves& curves)
+ {
+ mbedtls_ssl_conf_curves(&conf, curves.get());
+ }
+
+ void SetVersion(int minver, int maxver)
+ {
+ // SSL v3 support cannot be enabled
+ if (minver)
+ mbedtls_ssl_conf_min_version(&conf, MBEDTLS_SSL_MAJOR_VERSION_3, minver);
+ if (maxver)
+ mbedtls_ssl_conf_max_version(&conf, MBEDTLS_SSL_MAJOR_VERSION_3, maxver);
+ }
+
+ void SetCA(X509CertList& certs, X509CRL& crl)
+ {
+ mbedtls_ssl_conf_ca_chain(&conf, certs.get(), crl.get());
+ }
+
+ void SetOptionalVerifyCert()
+ {
+ mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+ }
+
+ const mbedtls_ssl_config* GetConf() const { return &conf; }
+ };
+
+ class Hash
+ {
+ const mbedtls_md_info_t* md;
+
+ /** Buffer where cert hashes are written temporarily
+ */
+ mutable std::vector<unsigned char> buf;
+
+ public:
+ Hash(std::string hashstr)
+ {
+ std::transform(hashstr.begin(), hashstr.end(), hashstr.begin(), ::toupper);
+ md = mbedtls_md_info_from_string(hashstr.c_str());
+ if (!md)
+ throw Exception("Unknown hash: " + hashstr);
+
+ buf.resize(mbedtls_md_get_size(md));
+ }
+
+ std::string hash(const unsigned char* input, size_t length) const
+ {
+ mbedtls_md(md, input, length, &buf.front());
+ return BinToHex(&buf.front(), buf.size());
+ }
+ };
+
+ class Profile : public refcountbase
+ {
+ /** Name of this profile
+ */
+ const std::string name;
+
+ X509Credentials x509cred;
+
+ /** Ciphersuites to use
+ */
+ Ciphersuites ciphersuites;
+
+ /** Curves accepted for use in ECDHE and in the peer's end-entity certificate
+ */
+ Curves curves;
+
+ Context serverctx;
+ Context clientctx;
+
+ DHParams dhparams;
+
+ X509CertList cacerts;
+
+ X509CRL crl;
+
+ /** Hashing algorithm to use when generating certificate fingerprints
+ */
+ Hash hash;
+
+ /** Rough max size of records to send
+ */
+ const unsigned int outrecsize;
+
+ Profile(const std::string& profilename, const std::string& certstr, const std::string& keystr,
+ const std::string& dhstr, unsigned int mindh, const std::string& hashstr,
+ const std::string& ciphersuitestr, const std::string& curvestr,
+ const std::string& castr, const std::string& crlstr,
+ unsigned int recsize,
+ CTRDRBG& ctrdrbg,
+ int minver, int maxver,
+ bool requestclientcert
+ )
+ : name(profilename)
+ , x509cred(certstr, keystr)
+ , ciphersuites(ciphersuitestr)
+ , curves(curvestr)
+ , serverctx(ctrdrbg, MBEDTLS_SSL_IS_SERVER)
+ , clientctx(ctrdrbg, MBEDTLS_SSL_IS_CLIENT)
+ , cacerts(castr, true)
+ , crl(crlstr)
+ , hash(hashstr)
+ , outrecsize(recsize)
+ {
+ serverctx.SetX509CertAndKey(x509cred);
+ clientctx.SetX509CertAndKey(x509cred);
+ clientctx.SetMinDHBits(mindh);
+
+ if (!ciphersuites.empty())
+ {
+ serverctx.SetCiphersuites(ciphersuites);
+ clientctx.SetCiphersuites(ciphersuites);
+ }
+
+ if (!curves.empty())
+ {
+ serverctx.SetCurves(curves);
+ clientctx.SetCurves(curves);
+ }
+
+ serverctx.SetVersion(minver, maxver);
+ clientctx.SetVersion(minver, maxver);
+
+ if (!dhstr.empty())
+ {
+ dhparams.set(dhstr);
+ serverctx.SetDHParams(dhparams);
+ }
+
+ clientctx.SetOptionalVerifyCert();
+ clientctx.SetCA(cacerts, crl);
+ // The default for servers is to not request a client certificate from the peer
+ if (requestclientcert)
+ {
+ serverctx.SetOptionalVerifyCert();
+ serverctx.SetCA(cacerts, crl);
+ }
+ }
+
+ static std::string ReadFile(const std::string& filename)
+ {
+ FileReader reader(filename);
+ std::string ret = reader.GetString();
+ if (ret.empty())
+ throw Exception("Cannot read file " + filename);
+ return ret;
+ }
+
+ public:
+ static reference<Profile> Create(const std::string& profilename, ConfigTag* tag, CTRDRBG& ctr_drbg)
+ {
+ const std::string certstr = ReadFile(tag->getString("certfile", "cert.pem"));
+ const std::string keystr = ReadFile(tag->getString("keyfile", "key.pem"));
+ const std::string dhstr = ReadFile(tag->getString("dhfile", "dhparams.pem"));
+
+ const std::string ciphersuitestr = tag->getString("ciphersuites");
+ const std::string curvestr = tag->getString("curves");
+ unsigned int mindh = tag->getInt("mindhbits", 2048);
+ std::string hashstr = tag->getString("hash", "sha256");
+
+ std::string crlstr;
+ std::string castr = tag->getString("cafile");
+ if (!castr.empty())
+ {
+ castr = ReadFile(castr);
+ crlstr = tag->getString("crlfile");
+ if (!crlstr.empty())
+ crlstr = ReadFile(crlstr);
+ }
+
+ int minver = tag->getInt("minver");
+ int maxver = tag->getInt("maxver");
+ unsigned int outrecsize = tag->getInt("outrecsize", 2048, 512, 16384);
+ const bool requestclientcert = tag->getBool("requestclientcert", true);
+ return new Profile(profilename, certstr, keystr, dhstr, mindh, hashstr, ciphersuitestr, curvestr, castr, crlstr, outrecsize, ctr_drbg, minver, maxver, requestclientcert);
+ }
+
+ /** Set up the given session with the settings in this profile
+ */
+ void SetupClientSession(mbedtls_ssl_context* sess)
+ {
+ mbedtls_ssl_setup(sess, clientctx.GetConf());
+ }
+
+ void SetupServerSession(mbedtls_ssl_context* sess)
+ {
+ mbedtls_ssl_setup(sess, serverctx.GetConf());
+ }
+
+ const std::string& GetName() const { return name; }
+ X509Credentials& GetX509Credentials() { return x509cred; }
+ unsigned int GetOutgoingRecordSize() const { return outrecsize; }
+ const Hash& GetHash() const { return hash; }
+ };
+}
+
+class mbedTLSIOHook : public SSLIOHook
+{
+ enum Status
+ {
+ ISSL_NONE,
+ ISSL_HANDSHAKING,
+ ISSL_HANDSHAKEN
+ };
+
+ mbedtls_ssl_context sess;
+ Status status;
+ reference<mbedTLS::Profile> profile;
+
+ void CloseSession()
+ {
+ if (status == ISSL_NONE)
+ return;
+
+ mbedtls_ssl_close_notify(&sess);
+ mbedtls_ssl_free(&sess);
+ certificate = NULL;
+ status = ISSL_NONE;
+ }
+
+ // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
+ int Handshake(StreamSocket* sock)
+ {
+ int ret = mbedtls_ssl_handshake(&sess);
+ if (ret == 0)
+ {
+ // Change the seesion state
+ this->status = ISSL_HANDSHAKEN;
+
+ VerifyCertificate();
+
+ // Finish writing, if any left
+ SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+
+ return 1;
+ }
+
+ this->status = ISSL_HANDSHAKING;
+ if (ret == MBEDTLS_ERR_SSL_WANT_READ)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ return 0;
+ }
+ else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+
+ sock->SetError("Handshake Failed - " + mbedTLS::ErrorToString(ret));
+ CloseSession();
+ return -1;
+ }
+
+ // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error
+ int PrepareIO(StreamSocket* sock)
+ {
+ if (status == ISSL_HANDSHAKEN)
+ return 1;
+ else if (status == ISSL_HANDSHAKING)
+ {
+ // The handshake isn't finished, try to finish it
+ return Handshake(sock);
+ }
+
+ CloseSession();
+ sock->SetError("No SSL session");
+ return -1;
+ }
+
+ void VerifyCertificate()
+ {
+ this->certificate = new ssl_cert;
+ const mbedtls_x509_crt* const cert = mbedtls_ssl_get_peer_cert(&sess);
+ if (!cert)
+ {
+ certificate->error = "No client certificate sent";
+ return;
+ }
+
+ // If there is a certificate we can always generate a fingerprint
+ certificate->fingerprint = profile->GetHash().hash(cert->raw.p, cert->raw.len);
+
+ // At this point mbedTLS verified the cert already, we just need to check the results
+ const uint32_t flags = mbedtls_ssl_get_verify_result(&sess);
+ if (flags == 0xFFFFFFFF)
+ {
+ certificate->error = "Internal error during verification";
+ return;
+ }
+
+ if (flags == 0)
+ {
+ // Verification succeeded
+ certificate->trusted = true;
+ }
+ else
+ {
+ // Verification failed
+ certificate->trusted = false;
+ if ((flags & MBEDTLS_X509_BADCERT_EXPIRED) || (flags & MBEDTLS_X509_BADCERT_FUTURE))
+ certificate->error = "Not activated, or expired certificate";
+ }
+
+ certificate->unknownsigner = (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED);
+ certificate->revoked = (flags & MBEDTLS_X509_BADCERT_REVOKED);
+ certificate->invalid = ((flags & MBEDTLS_X509_BADCERT_BAD_KEY) || (flags & MBEDTLS_X509_BADCERT_BAD_MD) || (flags & MBEDTLS_X509_BADCERT_BAD_PK));
+
+ GetDNString(&cert->subject, certificate->dn);
+ GetDNString(&cert->issuer, certificate->issuer);
+ }
+
+ static void GetDNString(const mbedtls_x509_name* x509name, std::string& out)
+ {
+ char buf[512];
+ const int ret = mbedtls_x509_dn_gets(buf, sizeof(buf), x509name);
+ if (ret <= 0)
+ return;
+
+ out.assign(buf, ret);
+ }
+
+ static int Pull(void* userptr, unsigned char* buffer, size_t size)
+ {
+ StreamSocket* const sock = reinterpret_cast<StreamSocket*>(userptr);
+ if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
+ return MBEDTLS_ERR_SSL_WANT_READ;
+
+ const int ret = SocketEngine::Recv(sock, reinterpret_cast<char*>(buffer), size, 0);
+ if (ret < (int)size)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+ if ((ret == -1) && (SocketEngine::IgnoreError()))
+ return MBEDTLS_ERR_SSL_WANT_READ;
+ }
+ return ret;
+ }
+
+ static int Push(void* userptr, const unsigned char* buffer, size_t size)
+ {
+ StreamSocket* const sock = reinterpret_cast<StreamSocket*>(userptr);
+ if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ return MBEDTLS_ERR_SSL_WANT_WRITE;
+
+ const int ret = SocketEngine::Send(sock, buffer, size, 0);
+ if (ret < (int)size)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+ if ((ret == -1) && (SocketEngine::IgnoreError()))
+ return MBEDTLS_ERR_SSL_WANT_WRITE;
+ }
+ return ret;
+ }
+
+ public:
+ mbedTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool isserver, mbedTLS::Profile* sslprofile)
+ : SSLIOHook(hookprov)
+ , status(ISSL_NONE)
+ , profile(sslprofile)
+ {
+ mbedtls_ssl_init(&sess);
+ if (isserver)
+ profile->SetupServerSession(&sess);
+ else
+ profile->SetupClientSession(&sess);
+
+ mbedtls_ssl_set_bio(&sess, reinterpret_cast<void*>(sock), Push, Pull, NULL);
+
+ sock->AddIOHook(this);
+ Handshake(sock);
+ }
+
+ void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ CloseSession();
+ }
+
+ int OnStreamSocketRead(StreamSocket* sock, std::string& recvq) CXX11_OVERRIDE
+ {
+ // Finish handshake if needed
+ int prepret = PrepareIO(sock);
+ if (prepret <= 0)
+ return prepret;
+
+ // If we resumed the handshake then this->status will be ISSL_HANDSHAKEN.
+ char* const readbuf = ServerInstance->GetReadBuffer();
+ const size_t readbufsize = ServerInstance->Config->NetBufferSize;
+ int ret = mbedtls_ssl_read(&sess, reinterpret_cast<unsigned char*>(readbuf), readbufsize);
+ if (ret > 0)
+ {
+ recvq.append(readbuf, ret);
+
+ // Schedule a read if there is still data in the mbedTLS buffer
+ if (mbedtls_ssl_get_bytes_avail(&sess) > 0)
+ SocketEngine::ChangeEventMask(sock, FD_ADD_TRIAL_READ);
+ return 1;
+ }
+ else if (ret == MBEDTLS_ERR_SSL_WANT_READ)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ);
+ return 0;
+ }
+ else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else if (ret == 0)
+ {
+ sock->SetError("Connection closed");
+ CloseSession();
+ return -1;
+ }
+ else // error or MBEDTLS_ERR_SSL_CLIENT_RECONNECT which we treat as an error
+ {
+ sock->SetError(mbedTLS::ErrorToString(ret));
+ CloseSession();
+ return -1;
+ }
+ }
+
+ int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
+ {
+ // Finish handshake if needed
+ int prepret = PrepareIO(sock);
+ if (prepret <= 0)
+ return prepret;
+
+ // Session is ready for transferring application data
+ while (!sendq.empty())
+ {
+ FlattenSendQueue(sendq, profile->GetOutgoingRecordSize());
+ const StreamSocket::SendQueue::Element& buffer = sendq.front();
+ int ret = mbedtls_ssl_write(&sess, reinterpret_cast<const unsigned char*>(buffer.data()), buffer.length());
+ if (ret == (int)buffer.length())
+ {
+ // Wrote entire record, continue sending
+ sendq.pop_front();
+ }
+ else if (ret > 0)
+ {
+ sendq.erase_front(ret);
+ SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else if (ret == 0)
+ {
+ sock->SetError("Connection closed");
+ CloseSession();
+ return -1;
+ }
+ else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else if (ret == MBEDTLS_ERR_SSL_WANT_READ)
+ {
+ SocketEngine::ChangeEventMask(sock, FD_WANT_POLL_READ);
+ return 0;
+ }
+ else
+ {
+ sock->SetError(mbedTLS::ErrorToString(ret));
+ CloseSession();
+ return -1;
+ }
+ }
+
+ SocketEngine::ChangeEventMask(sock, FD_WANT_NO_WRITE);
+ return 1;
+ }
+
+ void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
+ {
+ if (!IsHandshakeDone())
+ return;
+ out.append(mbedtls_ssl_get_version(&sess)).push_back('-');
+
+ // All mbedTLS ciphersuite names currently begin with "TLS-" which provides no useful information so skip it, but be prepared if it changes
+ const char* const ciphersuitestr = mbedtls_ssl_get_ciphersuite(&sess);
+ const char prefix[] = "TLS-";
+ unsigned int skip = sizeof(prefix)-1;
+ if (strncmp(ciphersuitestr, prefix, sizeof(prefix)-1))
+ skip = 0;
+ out.append(ciphersuitestr + skip);
+ }
+
+ bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); }
+};
+
+class mbedTLSIOHookProvider : public refcountbase, public IOHookProvider
+{
+ reference<mbedTLS::Profile> profile;
+
+ public:
+ mbedTLSIOHookProvider(Module* mod, mbedTLS::Profile* prof)
+ : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL)
+ , profile(prof)
+ {
+ ServerInstance->Modules->AddService(*this);
+ }
+
+ ~mbedTLSIOHookProvider()
+ {
+ ServerInstance->Modules->DelService(*this);
+ }
+
+ void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+ {
+ new mbedTLSIOHook(this, sock, true, profile);
+ }
+
+ void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ new mbedTLSIOHook(this, sock, false, profile);
+ }
+};
+
+class ModuleSSLmbedTLS : public Module
+{
+ typedef std::vector<reference<mbedTLSIOHookProvider> > ProfileList;
+
+ mbedTLS::Entropy entropy;
+ mbedTLS::CTRDRBG ctr_drbg;
+ ProfileList profiles;
+
+ void ReadProfiles()
+ {
+ // First, store all profiles in a new, temporary container. If no problems occur, swap the two
+ // containers; this way if something goes wrong we can go back and continue using the current profiles,
+ // avoiding unpleasant situations where no new SSL connections are possible.
+ ProfileList newprofiles;
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
+ if (tags.first == tags.second)
+ {
+ // No <sslprofile> tags found, create a profile named "mbedtls" from settings in the <mbedtls> block
+ const std::string defname = "mbedtls";
+ ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found; using settings from the <mbedtls> tag");
+
+ try
+ {
+ reference<mbedTLS::Profile> profile(mbedTLS::Profile::Create(defname, tag, ctr_drbg));
+ newprofiles.push_back(new mbedTLSIOHookProvider(this, profile));
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
+ }
+ }
+
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ if (tag->getString("provider") != "mbedtls")
+ continue;
+
+ std::string name = tag->getString("name");
+ if (name.empty())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
+ continue;
+ }
+
+ reference<mbedTLS::Profile> profile;
+ try
+ {
+ profile = mbedTLS::Profile::Create(name, tag, ctr_drbg);
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+ }
+
+ newprofiles.push_back(new mbedTLSIOHookProvider(this, profile));
+ }
+
+ // New profiles are ok, begin using them
+ // Old profiles are deleted when their refcount drops to zero
+ profiles.swap(newprofiles);
+ }
+
+ public:
+ void init() CXX11_OVERRIDE
+ {
+ char verbuf[16]; // Should be at least 9 bytes in size
+ mbedtls_version_get_string(verbuf);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "mbedTLS lib version %s module was compiled for " MBEDTLS_VERSION_STRING, verbuf);
+
+ if (!ctr_drbg.Seed(entropy))
+ throw ModuleException("CTR DRBG seed failed");
+ ReadProfiles();
+ }
+
+ void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
+ {
+ if (param != "ssl")
+ return;
+
+ try
+ {
+ ReadProfiles();
+ }
+ catch (ModuleException& ex)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
+ }
+ }
+
+ void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
+ {
+ if (target_type != TYPE_USER)
+ return;
+
+ LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+ if ((user) && (user->eh.GetModHook(this)))
+ {
+ // User is using SSL, they're a local user, and they're using our IOHook.
+ // Potentially there could be multiple SSL modules loaded at once on different ports.
+ ServerInstance->Users.QuitUser(user, "SSL module unloading");
+ }
+ }
+
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+ {
+ const mbedTLSIOHook* const iohook = static_cast<mbedTLSIOHook*>(user->eh.GetModHook(this));
+ if ((iohook) && (!iohook->IsHandshakeDone()))
+ return MOD_RES_DENY;
+ return MOD_RES_PASSTHRU;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides SSL support via mbedTLS (PolarSSL)", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleSSLmbedTLS)
diff --git a/src/modules/extra/m_ssl_openssl.cpp b/src/modules/extra/m_ssl_openssl.cpp
index 0ce36ed80..bda9180b7 100644
--- a/src/modules/extra/m_ssl_openssl.cpp
+++ b/src/modules/extra/m_ssl_openssl.cpp
@@ -21,37 +21,56 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
- /* HACK: This prevents OpenSSL on OS X 10.7 and later from spewing deprecation
- * warnings for every single function call. As far as I (SaberUK) know, Apple
- * have no plans to remove OpenSSL so this warning just causes needless spam.
- */
-#ifdef __APPLE__
-# define __AVAILABILITYMACROS__
-# define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
-#endif
+/// $CompilerFlags: find_compiler_flags("openssl")
+/// $LinkerFlags: find_linker_flags("openssl" "-lssl -lcrypto")
+
+/// $PackageInfo: require_system("centos") openssl-devel pkgconfig
+/// $PackageInfo: require_system("darwin") openssl pkg-config
+/// $PackageInfo: require_system("ubuntu" "16.04") libssl-dev openssl pkg-config
+
#include "inspircd.h"
#include "iohook.h"
+#include "modules/ssl.h"
+
+// Ignore OpenSSL deprecation warnings on OS X Lion and newer.
+#if defined __APPLE__
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+// Fix warnings about the use of `long long` on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+#endif
+
#include <openssl/ssl.h>
#include <openssl/err.h>
-#include "modules/ssl.h"
#ifdef _WIN32
-# pragma comment(lib, "libcrypto.lib")
-# pragma comment(lib, "libssl.lib")
-# pragma comment(lib, "user32.lib")
-# pragma comment(lib, "advapi32.lib")
-# pragma comment(lib, "libgcc.lib")
-# pragma comment(lib, "libmingwex.lib")
-# pragma comment(lib, "gdi32.lib")
+# pragma comment(lib, "ssleay32.lib")
+# pragma comment(lib, "libeay32.lib")
#endif
-/* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") -Wno-pedantic */
-/* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto") */
+#if ((OPENSSL_VERSION_NUMBER >= 0x10000000L) && (!(defined(OPENSSL_NO_ECDH))))
+// OpenSSL 0.9.8 includes some ECC support, but it's unfinished. Enable only for 1.0.0 and later.
+#define INSPIRCD_OPENSSL_ENABLE_ECDH
+#endif
+
+// BIO is opaque in OpenSSL 1.1 but the access API does not exist in 1.0 and older.
+#if ((defined LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L))
+# define BIO_get_data(BIO) BIO->ptr
+# define BIO_set_data(BIO, VALUE) BIO->ptr = VALUE;
+# define BIO_set_init(BIO, VALUE) BIO->init = VALUE;
+#else
+# define INSPIRCD_OPENSSL_OPAQUE_BIO
+#endif
enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
static bool SelfSigned = false;
+static int exdataindex;
char* get_error()
{
@@ -59,6 +78,7 @@ char* get_error()
}
static int OnVerify(int preverify_ok, X509_STORE_CTX* ctx);
+static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
namespace OpenSSL
{
@@ -76,12 +96,13 @@ namespace OpenSSL
public:
DHParams(const std::string& filename)
{
- FILE* dhpfile = fopen(filename.c_str(), "r");
+ BIO* dhpfile = BIO_new_file(filename.c_str(), "r");
if (dhpfile == NULL)
- throw Exception("Couldn't open DH file " + filename + ": " + strerror(errno));
+ throw Exception("Couldn't open DH file " + filename);
+
+ dh = PEM_read_bio_DHparams(dhpfile, NULL, NULL, NULL);
+ BIO_free(dhpfile);
- dh = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
- fclose(dhpfile);
if (!dh)
throw Exception("Couldn't read DH params from file " + filename);
}
@@ -100,16 +121,33 @@ namespace OpenSSL
class Context
{
SSL_CTX* const ctx;
+ long ctx_options;
public:
Context(SSL_CTX* context)
: ctx(context)
{
- SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+ // Sane default options for OpenSSL see https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
+ // and when choosing a cipher, use the server's preferences instead of the client preferences.
+ long opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_DH_USE;
+ // Only turn options on if they exist
+#ifdef SSL_OP_SINGLE_ECDH_USE
+ opts |= SSL_OP_SINGLE_ECDH_USE;
+#endif
+#ifdef SSL_OP_NO_TICKET
+ opts |= SSL_OP_NO_TICKET;
+#endif
+
+ ctx_options = SSL_CTX_set_options(ctx, opts);
- const unsigned char session_id[] = "inspircd";
- SSL_CTX_set_session_id_context(ctx, session_id, sizeof(session_id) - 1);
+ long mode = SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ mode |= SSL_MODE_RELEASE_BUFFERS;
+#endif
+ SSL_CTX_set_mode(ctx, mode);
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+ SSL_CTX_set_info_callback(ctx, StaticSSLInfoCallback);
}
~Context()
@@ -119,32 +157,85 @@ namespace OpenSSL
bool SetDH(DHParams& dh)
{
+ ERR_clear_error();
return (SSL_CTX_set_tmp_dh(ctx, dh.get()) >= 0);
}
+#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
+ void SetECDH(const std::string& curvename)
+ {
+ int nid = OBJ_sn2nid(curvename.c_str());
+ if (nid == 0)
+ throw Exception("Unknown curve: " + curvename);
+
+ EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
+ if (!eckey)
+ throw Exception("Unable to create EC key object");
+
+ ERR_clear_error();
+ bool ret = (SSL_CTX_set_tmp_ecdh(ctx, eckey) >= 0);
+ EC_KEY_free(eckey);
+ if (!ret)
+ throw Exception("Couldn't set ECDH parameters");
+ }
+#endif
+
bool SetCiphers(const std::string& ciphers)
{
+ ERR_clear_error();
return SSL_CTX_set_cipher_list(ctx, ciphers.c_str());
}
bool SetCerts(const std::string& filename)
{
+ ERR_clear_error();
return SSL_CTX_use_certificate_chain_file(ctx, filename.c_str());
}
bool SetPrivateKey(const std::string& filename)
{
+ ERR_clear_error();
return SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
}
bool SetCA(const std::string& filename)
{
+ ERR_clear_error();
return SSL_CTX_load_verify_locations(ctx, filename.c_str(), 0);
}
- SSL* CreateSession()
+ long GetDefaultContextOptions() const
+ {
+ return ctx_options;
+ }
+
+ long SetRawContextOptions(long setoptions, long clearoptions)
+ {
+ // Clear everything
+ SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
+
+ // Set the default options and what is in the conf
+ SSL_CTX_set_options(ctx, ctx_options | setoptions);
+ return SSL_CTX_clear_options(ctx, clearoptions);
+ }
+
+ void SetVerifyCert()
{
- return SSL_new(ctx);
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+ }
+
+ SSL* CreateServerSession()
+ {
+ SSL* sess = SSL_new(ctx);
+ SSL_set_accept_state(sess); // Act as server
+ return sess;
+ }
+
+ SSL* CreateClientSession()
+ {
+ SSL* sess = SSL_new(ctx);
+ SSL_set_connect_state(sess); // Act as client
+ return sess;
}
};
@@ -171,6 +262,14 @@ namespace OpenSSL
*/
std::string lasterr;
+ /** True if renegotiations are allowed, false if not
+ */
+ const bool allowrenego;
+
+ /** Rough max size of records to send
+ */
+ const unsigned int outrecsize;
+
static int error_callback(const char* str, size_t len, void* u)
{
Profile* profile = reinterpret_cast<Profile*>(u);
@@ -178,12 +277,40 @@ namespace OpenSSL
return 0;
}
+ /** Set raw OpenSSL context (SSL_CTX) options from a config tag
+ * @param ctxname Name of the context, client or server
+ * @param tag Config tag defining this profile
+ * @param context Context object to manipulate
+ */
+ void SetContextOptions(const std::string& ctxname, ConfigTag* tag, Context& context)
+ {
+ long setoptions = tag->getInt(ctxname + "setoptions");
+ long clearoptions = tag->getInt(ctxname + "clearoptions");
+#ifdef SSL_OP_NO_COMPRESSION
+ if (!tag->getBool("compression", false)) // Disable compression by default
+ setoptions |= SSL_OP_NO_COMPRESSION;
+#endif
+ if (!tag->getBool("sslv3", false)) // Disable SSLv3 by default
+ setoptions |= SSL_OP_NO_SSLv3;
+ if (!tag->getBool("tlsv1", true))
+ setoptions |= SSL_OP_NO_TLSv1;
+
+ if (!setoptions && !clearoptions)
+ return; // Nothing to do
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Setting %s %s context options, default: %ld set: %ld clear: %ld", name.c_str(), ctxname.c_str(), ctx.GetDefaultContextOptions(), setoptions, clearoptions);
+ long final = context.SetRawContextOptions(setoptions, clearoptions);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "%s %s context options: %ld", name.c_str(), ctxname.c_str(), final);
+ }
+
public:
Profile(const std::string& profilename, ConfigTag* tag)
: name(profilename)
, dh(ServerInstance->Config->Paths.PrependConfig(tag->getString("dhfile", "dh.pem")))
, ctx(SSL_CTX_new(SSLv23_server_method()))
, clictx(SSL_CTX_new(SSLv23_client_method()))
+ , allowrenego(tag->getBool("renegotiation")) // Disallow by default
+ , outrecsize(tag->getInt("outrecsize", 2048, 512, 16384))
{
if ((!ctx.SetDH(dh)) || (!clictx.SetDH(dh)))
throw Exception("Couldn't set DH parameters");
@@ -203,6 +330,15 @@ namespace OpenSSL
}
}
+#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
+ std::string curvename = tag->getString("ecdhcurve", "prime256v1");
+ if (!curvename.empty())
+ ctx.SetECDH(curvename);
+#endif
+
+ SetContextOptions("server", tag, ctx);
+ SetContextOptions("client", tag, clictx);
+
/* Load our keys and certificates
* NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
*/
@@ -227,15 +363,81 @@ namespace OpenSSL
ERR_print_errors_cb(error_callback, this);
ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", filename.c_str(), lasterr.c_str());
}
+
+ clictx.SetVerifyCert();
+ if (tag->getBool("requestclientcert", true))
+ ctx.SetVerifyCert();
}
const std::string& GetName() const { return name; }
- SSL* CreateServerSession() { return ctx.CreateSession(); }
- SSL* CreateClientSession() { return clictx.CreateSession(); }
+ SSL* CreateServerSession() { return ctx.CreateServerSession(); }
+ SSL* CreateClientSession() { return clictx.CreateClientSession(); }
const EVP_MD* GetDigest() { return digest; }
+ bool AllowRenegotiation() const { return allowrenego; }
+ unsigned int GetOutgoingRecordSize() const { return outrecsize; }
};
+
+ namespace BIOMethod
+ {
+ static int create(BIO* bio)
+ {
+ BIO_set_init(bio, 1);
+ return 1;
+ }
+
+ static int destroy(BIO* bio)
+ {
+ // XXX: Dummy function to avoid a memory leak in OpenSSL.
+ // The memory leak happens in BIO_free() (bio_lib.c) when the destroy func of the BIO is NULL.
+ // This is fixed in OpenSSL but some distros still ship the unpatched version hence we provide this workaround.
+ return 1;
+ }
+
+ static long ctrl(BIO* bio, int cmd, long num, void* ptr)
+ {
+ if (cmd == BIO_CTRL_FLUSH)
+ return 1;
+ return 0;
+ }
+
+ static int read(BIO* bio, char* buf, int len);
+ static int write(BIO* bio, const char* buf, int len);
+
+#ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
+ static BIO_METHOD* alloc()
+ {
+ BIO_METHOD* meth = BIO_meth_new(100 | BIO_TYPE_SOURCE_SINK, "inspircd");
+ BIO_meth_set_write(meth, OpenSSL::BIOMethod::write);
+ BIO_meth_set_read(meth, OpenSSL::BIOMethod::read);
+ BIO_meth_set_ctrl(meth, OpenSSL::BIOMethod::ctrl);
+ BIO_meth_set_create(meth, OpenSSL::BIOMethod::create);
+ BIO_meth_set_destroy(meth, OpenSSL::BIOMethod::destroy);
+ return meth;
+ }
+#endif
+ }
}
+// BIO_METHOD is opaque in OpenSSL 1.1 so we can't do this.
+// See OpenSSL::BIOMethod::alloc for the new method.
+#ifndef INSPIRCD_OPENSSL_OPAQUE_BIO
+static BIO_METHOD biomethods =
+{
+ (100 | BIO_TYPE_SOURCE_SINK),
+ "inspircd",
+ OpenSSL::BIOMethod::write,
+ OpenSSL::BIOMethod::read,
+ NULL, // puts
+ NULL, // gets
+ OpenSSL::BIOMethod::ctrl,
+ OpenSSL::BIOMethod::create,
+ OpenSSL::BIOMethod::destroy, // destroy, does nothing, see function body for more info
+ NULL // callback_ctrl
+};
+#else
+static BIO_METHOD* biomethods;
+#endif
+
static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
{
/* XXX: This will allow self signed certificates.
@@ -255,19 +457,14 @@ class OpenSSLIOHook : public SSLIOHook
private:
SSL* sess;
issl_status status;
- const bool outbound;
bool data_to_write;
reference<OpenSSL::Profile> profile;
- bool Handshake(StreamSocket* user)
+ // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
+ int Handshake(StreamSocket* user)
{
- int ret;
-
- if (outbound)
- ret = SSL_connect(sess);
- else
- ret = SSL_accept(sess);
-
+ ERR_clear_error();
+ int ret = SSL_do_handshake(sess);
if (ret < 0)
{
int err = SSL_get_error(sess, ret);
@@ -276,20 +473,19 @@ class OpenSSLIOHook : public SSLIOHook
{
SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
this->status = ISSL_HANDSHAKING;
- return true;
+ return 0;
}
else if (err == SSL_ERROR_WANT_WRITE)
{
SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
this->status = ISSL_HANDSHAKING;
- return true;
+ return 0;
}
else
{
CloseSession();
+ return -1;
}
-
- return false;
}
else if (ret > 0)
{
@@ -300,15 +496,13 @@ class OpenSSLIOHook : public SSLIOHook
SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
- return true;
+ return 1;
}
else if (ret == 0)
{
CloseSession();
- return true;
}
-
- return true;
+ return -1;
}
void CloseSession()
@@ -321,7 +515,6 @@ class OpenSSLIOHook : public SSLIOHook
sess = NULL;
certificate = NULL;
status = ISSL_NONE;
- errno = EIO;
}
void VerifyCertificate()
@@ -356,8 +549,14 @@ class OpenSSLIOHook : public SSLIOHook
char buf[512];
X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
certinfo->dn = buf;
+ // Make sure there are no chars in the string that we consider invalid
+ if (certinfo->dn.find_first_of("\r\n") != std::string::npos)
+ certinfo->dn.clear();
+
X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
certinfo->issuer = buf;
+ if (certinfo->issuer.find_first_of("\r\n") != std::string::npos)
+ certinfo->issuer.clear();
if (!X509_digest(cert, profile->GetDigest(), md, &n))
{
@@ -376,20 +575,69 @@ class OpenSSLIOHook : public SSLIOHook
X509_free(cert);
}
+ void SSLInfoCallback(int where, int rc)
+ {
+ if ((where & SSL_CB_HANDSHAKE_START) && (status == ISSL_OPEN))
+ {
+ if (profile->AllowRenegotiation())
+ return;
+
+ // The other side is trying to renegotiate, kill the connection and change status
+ // to ISSL_NONE so CheckRenego() closes the session
+ status = ISSL_NONE;
+ BIO* bio = SSL_get_rbio(sess);
+ EventHandler* eh = static_cast<StreamSocket*>(BIO_get_data(bio));
+ SocketEngine::Shutdown(eh, 2);
+ }
+ }
+
+ bool CheckRenego(StreamSocket* sock)
+ {
+ if (status != ISSL_NONE)
+ return true;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Session %p killed, attempted to renegotiate", (void*)sess);
+ CloseSession();
+ sock->SetError("Renegotiation is not allowed");
+ return false;
+ }
+
+ // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error
+ int PrepareIO(StreamSocket* sock)
+ {
+ if (status == ISSL_OPEN)
+ return 1;
+ else if (status == ISSL_HANDSHAKING)
+ {
+ // The handshake isn't finished, try to finish it
+ return Handshake(sock);
+ }
+
+ CloseSession();
+ return -1;
+ }
+
+ // Calls our private SSLInfoCallback()
+ friend void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
+
public:
- OpenSSLIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool is_outbound, SSL* session, const reference<OpenSSL::Profile>& sslprofile)
+ OpenSSLIOHook(IOHookProvider* hookprov, StreamSocket* sock, SSL* session, const reference<OpenSSL::Profile>& sslprofile)
: SSLIOHook(hookprov)
, sess(session)
, status(ISSL_NONE)
- , outbound(is_outbound)
, data_to_write(false)
, profile(sslprofile)
{
- if (sess == NULL)
- return;
- if (SSL_set_fd(sess, sock->GetFd()) == 0)
- throw ModuleException("Can't set fd with SSL_set_fd: " + ConvToStr(sock->GetFd()));
+ // Create BIO instance and store a pointer to the socket in it which will be used by the read and write functions
+#ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
+ BIO* bio = BIO_new(biomethods);
+#else
+ BIO* bio = BIO_new(&biomethods);
+#endif
+ BIO_set_data(bio, sock);
+ SSL_set_bio(sess, bio, bio);
+ SSL_set_ex_data(sess, exdataindex, this);
sock->AddIOHook(this);
Handshake(sock);
}
@@ -401,37 +649,32 @@ class OpenSSLIOHook : public SSLIOHook
int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
{
- if (!sess)
- {
- CloseSession();
- return -1;
- }
-
- if (status == ISSL_HANDSHAKING)
- {
- // The handshake isn't finished and it wants to read, try to finish it.
- if (!Handshake(user))
- {
- // Couldn't resume handshake.
- if (status == ISSL_NONE)
- return -1;
- return 0;
- }
- }
+ // Finish handshake if needed
+ int prepret = PrepareIO(user);
+ if (prepret <= 0)
+ return prepret;
// If we resumed the handshake then this->status will be ISSL_OPEN
-
- if (status == ISSL_OPEN)
{
+ ERR_clear_error();
char* buffer = ServerInstance->GetReadBuffer();
size_t bufsiz = ServerInstance->Config->NetBufferSize;
int ret = SSL_read(sess, buffer, bufsiz);
+ if (!CheckRenego(user))
+ return -1;
+
if (ret > 0)
{
recvq.append(buffer, ret);
+ int mask = 0;
+ // Schedule a read if there is still data in the OpenSSL buffer
+ if (SSL_pending(sess) > 0)
+ mask |= FD_ADD_TRIAL_READ;
if (data_to_write)
- SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
+ mask |= FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE;
+ if (mask != 0)
+ SocketEngine::ChangeEventMask(user, mask);
return 1;
}
else if (ret == 0)
@@ -441,7 +684,7 @@ class OpenSSLIOHook : public SSLIOHook
user->SetError("Connection closed");
return -1;
}
- else if (ret < 0)
+ else // if (ret < 0)
{
int err = SSL_get_error(sess, ret);
@@ -462,43 +705,36 @@ class OpenSSLIOHook : public SSLIOHook
}
}
}
-
- return 0;
}
- int OnStreamSocketWrite(StreamSocket* user, std::string& buffer) CXX11_OVERRIDE
+ int OnStreamSocketWrite(StreamSocket* user, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
{
- if (!sess)
- {
- CloseSession();
- return -1;
- }
+ // Finish handshake if needed
+ int prepret = PrepareIO(user);
+ if (prepret <= 0)
+ return prepret;
data_to_write = true;
- if (status == ISSL_HANDSHAKING)
- {
- if (!Handshake(user))
- {
- // Couldn't resume handshake.
- if (status == ISSL_NONE)
- return -1;
- return 0;
- }
- }
-
- if (status == ISSL_OPEN)
+ // Session is ready for transferring application data
+ while (!sendq.empty())
{
+ ERR_clear_error();
+ FlattenSendQueue(sendq, profile->GetOutgoingRecordSize());
+ const StreamSocket::SendQueue::Element& buffer = sendq.front();
int ret = SSL_write(sess, buffer.data(), buffer.size());
+
+ if (!CheckRenego(user))
+ return -1;
+
if (ret == (int)buffer.length())
{
- data_to_write = false;
- SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
- return 1;
+ // Wrote entire record, continue sending
+ sendq.pop_front();
}
else if (ret > 0)
{
- buffer = buffer.substr(ret);
+ sendq.erase_front(ret);
SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
return 0;
}
@@ -507,7 +743,7 @@ class OpenSSLIOHook : public SSLIOHook
CloseSession();
return -1;
}
- else if (ret < 0)
+ else // if (ret < 0)
{
int err = SSL_get_error(sess, ret);
@@ -528,23 +764,75 @@ class OpenSSLIOHook : public SSLIOHook
}
}
}
- return 0;
+
+ data_to_write = false;
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ return 1;
}
- void TellCiphersAndFingerprint(LocalUser* user)
+ void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
{
- if (sess)
- {
- std::string text = "*** You are connected using SSL cipher '" + std::string(SSL_get_cipher(sess)) + "'";
- const std::string& fingerprint = certificate->fingerprint;
- if (!fingerprint.empty())
- text += " and your SSL fingerprint is " + fingerprint;
-
- user->WriteNotice(text);
- }
+ if (!IsHandshakeDone())
+ return;
+ out.append(SSL_get_version(sess)).push_back('-');
+ out.append(SSL_get_cipher(sess));
}
+
+ bool IsHandshakeDone() const { return (status == ISSL_OPEN); }
};
+static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc)
+{
+ OpenSSLIOHook* hook = static_cast<OpenSSLIOHook*>(SSL_get_ex_data(ssl, exdataindex));
+ hook->SSLInfoCallback(where, rc);
+}
+
+static int OpenSSL::BIOMethod::write(BIO* bio, const char* buffer, int size)
+{
+ BIO_clear_retry_flags(bio);
+
+ StreamSocket* sock = static_cast<StreamSocket*>(BIO_get_data(bio));
+ if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ {
+ // Writes blocked earlier, don't retry syscall
+ BIO_set_retry_write(bio);
+ return -1;
+ }
+
+ int ret = SocketEngine::Send(sock, buffer, size, 0);
+ if ((ret < size) && ((ret > 0) || (SocketEngine::IgnoreError())))
+ {
+ // Blocked, set retry flag for OpenSSL
+ SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+ BIO_set_retry_write(bio);
+ }
+
+ return ret;
+}
+
+static int OpenSSL::BIOMethod::read(BIO* bio, char* buffer, int size)
+{
+ BIO_clear_retry_flags(bio);
+
+ StreamSocket* sock = static_cast<StreamSocket*>(BIO_get_data(bio));
+ if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
+ {
+ // Reads blocked earlier, don't retry syscall
+ BIO_set_retry_read(bio);
+ return -1;
+ }
+
+ int ret = SocketEngine::Recv(sock, buffer, size, 0);
+ if ((ret < size) && ((ret > 0) || (SocketEngine::IgnoreError())))
+ {
+ // Blocked, set retry flag for OpenSSL
+ SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+ BIO_set_retry_read(bio);
+ }
+
+ return ret;
+}
+
class OpenSSLIOHookProvider : public refcountbase, public IOHookProvider
{
reference<OpenSSL::Profile> profile;
@@ -564,12 +852,12 @@ class OpenSSLIOHookProvider : public refcountbase, public IOHookProvider
void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
{
- new OpenSSLIOHook(this, sock, false, profile->CreateServerSession(), profile);
+ new OpenSSLIOHook(this, sock, profile->CreateServerSession(), profile);
}
void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
{
- new OpenSSLIOHook(this, sock, true, profile->CreateClientSession(), profile);
+ new OpenSSLIOHook(this, sock, profile->CreateClientSession(), profile);
}
};
@@ -636,10 +924,26 @@ class ModuleSSLOpenSSL : public Module
// Initialize OpenSSL
SSL_library_init();
SSL_load_error_strings();
+#ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
+ biomethods = OpenSSL::BIOMethod::alloc();
+ }
+
+ ~ModuleSSLOpenSSL()
+ {
+ BIO_meth_free(biomethods);
+#endif
}
void init() CXX11_OVERRIDE
{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "OpenSSL lib version \"%s\" module was compiled for \"" OPENSSL_VERSION_TEXT "\"", SSLeay_version(SSLEAY_VERSION));
+
+ // Register application specific data
+ char exdatastr[] = "inspircd";
+ exdataindex = SSL_get_ex_new_index(0, exdatastr, NULL, NULL, NULL);
+ if (exdataindex < 0)
+ throw ModuleException("Failed to register application specific data");
+
ReadProfiles();
}
@@ -658,20 +962,13 @@ class ModuleSSLOpenSSL : public Module
}
}
- void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
- {
- IOHook* hook = user->eh.GetIOHook();
- if (hook && hook->prov->creator == this)
- static_cast<OpenSSLIOHook*>(hook)->TellCiphersAndFingerprint(user);
- }
-
void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
{
if (target_type == TYPE_USER)
{
LocalUser* user = IS_LOCAL((User*)item);
- if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == this)
+ if ((user) && (user->eh.GetModHook(this)))
{
// User is using SSL, they're a local user, and they're using one of *our* SSL ports.
// Potentially there could be multiple SSL modules loaded at once on different ports.
@@ -680,6 +977,14 @@ class ModuleSSLOpenSSL : public Module
}
}
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+ {
+ const OpenSSLIOHook* const iohook = static_cast<OpenSSLIOHook*>(user->eh.GetModHook(this));
+ if ((iohook) && (!iohook->IsHandshakeDone()))
+ return MOD_RES_DENY;
+ return MOD_RES_PASSTHRU;
+ }
+
Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides SSL support for clients", VF_VENDOR);
diff --git a/src/modules/m_abbreviation.cpp b/src/modules/m_abbreviation.cpp
index f69d26749..85709080f 100644
--- a/src/modules/m_abbreviation.cpp
+++ b/src/modules/m_abbreviation.cpp
@@ -42,13 +42,14 @@ class ModuleAbbreviation : public Module
size_t clen = command.length() - 1;
std::string foundcommand, matchlist;
bool foundmatch = false;
- for (Commandtable::iterator n = ServerInstance->Parser->cmdlist.begin(); n != ServerInstance->Parser->cmdlist.end(); ++n)
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator n = commands.begin(); n != commands.end(); ++n)
{
if (!command.compare(0, clen, n->first, 0, clen))
{
if (matchlist.length() > 450)
{
- user->WriteNumeric(420, ":Ambiguous abbreviation and too many possible matches.");
+ user->WriteNumeric(420, "Ambiguous abbreviation and too many possible matches.");
return MOD_RES_DENY;
}
@@ -66,7 +67,7 @@ class ModuleAbbreviation : public Module
/* Ambiguous command, list the matches */
if (!matchlist.empty())
{
- user->WriteNumeric(420, ":Ambiguous abbreviation, posssible matches: %s%s", foundcommand.c_str(), matchlist.c_str());
+ user->WriteNumeric(420, InspIRCd::Format("Ambiguous abbreviation, possible matches: %s%s", foundcommand.c_str(), matchlist.c_str()));
return MOD_RES_DENY;
}
diff --git a/src/modules/m_alias.cpp b/src/modules/m_alias.cpp
index 764761099..c6e53f0cf 100644
--- a/src/modules/m_alias.cpp
+++ b/src/modules/m_alias.cpp
@@ -57,13 +57,13 @@ class Alias
class ModuleAlias : public Module
{
- char fprefix;
+ std::string fprefix;
/* We cant use a map, there may be multiple aliases with the same name.
* We can, however, use a fancy invention: the multimap. Maps a key to one or more values.
* -- w00t
- */
- typedef std::multimap<std::string, Alias, irc::insensitive_swo> AliasMap;
+ */
+ typedef insp::flat_multimap<std::string, Alias, irc::insensitive_swo> AliasMap;
AliasMap Aliases;
@@ -76,8 +76,8 @@ class ModuleAlias : public Module
{
ConfigTag* fantasy = ServerInstance->Config->ConfValue("fantasy");
AllowBots = fantasy->getBool("allowbots", false);
- std::string fpre = fantasy->getString("prefix", "!");
- fprefix = fpre.empty() ? '!' : fpre[0];
+ std::string fpre = fantasy->getString("prefix");
+ fprefix = fpre.empty() ? "!" : fpre;
Aliases.clear();
ConfigTagList tags = ServerInstance->Config->ConfTags("alias");
@@ -148,7 +148,7 @@ class ModuleAlias : public Module
return MOD_RES_PASSTHRU;
/* The parameters for the command in their original form, with the command stripped off */
- std::string compare = original_line.substr(command.length());
+ std::string compare(original_line, command.length());
while (*(compare.c_str()) == ' ')
compare.erase(compare.begin());
@@ -193,26 +193,26 @@ class ModuleAlias : public Module
irc::spacesepstream ss(text);
ss.GetToken(scommand);
- if (scommand.empty())
+ if (scommand.size() <= fprefix.size())
{
return; // wtfbbq
}
// we don't want to touch non-fantasy stuff
- if (*scommand.c_str() != fprefix)
+ if (scommand.compare(0, fprefix.size(), fprefix) != 0)
{
return;
}
// nor do we give a shit about the prefix
- scommand.erase(scommand.begin());
+ scommand.erase(0, fprefix.size());
std::pair<AliasMap::iterator, AliasMap::iterator> iters = Aliases.equal_range(scommand);
if (iters.first == iters.second)
return;
/* The parameters for the command in their original form, with the command stripped off */
- std::string compare = text.substr(scommand.length() + 1);
+ std::string compare(text, scommand.length() + fprefix.size());
while (*(compare.c_str()) == ' ')
compare.erase(compare.begin());
@@ -220,8 +220,8 @@ class ModuleAlias : public Module
{
if (i->second.ChannelCommand)
{
- // We use substr(1) here to remove the fantasy prefix
- if (DoAlias(user, c, &(i->second), compare, text.substr(1)))
+ // We use substr here to remove the fantasy prefix
+ if (DoAlias(user, c, &(i->second), compare, text.substr(fprefix.size())))
return;
}
}
@@ -253,14 +253,14 @@ class ModuleAlias : public Module
User* u = ServerInstance->FindNick(a->RequiredNick);
if (!u)
{
- user->WriteNumeric(ERR_NOSUCHNICK, a->RequiredNick + " :is currently unavailable. Please try again later.");
+ user->WriteNumeric(ERR_NOSUCHNICK, a->RequiredNick, "is currently unavailable. Please try again later.");
return 1;
}
if ((a->ULineOnly) && (!u->server->IsULine()))
{
ServerInstance->SNO->WriteToSnoMask('a', "NOTICE -- Service "+a->RequiredNick+" required by alias "+a->AliasedCommand+" is not on a u-lined server, possibly underhanded antics detected!");
- user->WriteNumeric(ERR_NOSUCHNICK, a->RequiredNick + " :is an imposter! Please inform an IRC operator as soon as possible.");
+ user->WriteNumeric(ERR_NOSUCHNICK, a->RequiredNick, "is an imposter! Please inform an IRC operator as soon as possible.");
return 1;
}
}
@@ -271,7 +271,7 @@ class ModuleAlias : public Module
if (crlf == std::string::npos)
{
- DoCommand(a->ReplaceFormat, user, c, safe);
+ DoCommand(a->ReplaceFormat, user, c, safe, a);
return 1;
}
else
@@ -280,13 +280,13 @@ class ModuleAlias : public Module
std::string scommand;
while (commands.GetToken(scommand))
{
- DoCommand(scommand, user, c, safe);
+ DoCommand(scommand, user, c, safe, a);
}
return 1;
}
}
- void DoCommand(const std::string& newline, User* user, Channel *chan, const std::string &original_line)
+ void DoCommand(const std::string& newline, User* user, Channel *chan, const std::string &original_line, Alias* a)
{
std::string result;
result.reserve(newline.length());
@@ -328,6 +328,11 @@ class ModuleAlias : public Module
result.append(user->dhost);
i += 5;
}
+ else if (!newline.compare(i, 12, "$requirement", 12))
+ {
+ result.append(a->RequiredNick);
+ i += 11;
+ }
else
result.push_back(c);
}
@@ -344,14 +349,14 @@ class ModuleAlias : public Module
{
pars.push_back(token);
}
- ServerInstance->Parser->CallHandler(command, pars, user);
+ ServerInstance->Parser.CallHandler(command, pars, user);
}
void Prioritize()
{
// Prioritise after spanningtree so that channel aliases show the alias before the effects.
Module* linkmod = ServerInstance->Modules->Find("m_spanningtree.so");
- ServerInstance->Modules->SetPriority(this, I_OnUserMessage, PRIORITY_AFTER, &linkmod);
+ ServerInstance->Modules->SetPriority(this, I_OnUserMessage, PRIORITY_AFTER, linkmod);
}
};
diff --git a/src/modules/m_allowinvite.cpp b/src/modules/m_allowinvite.cpp
index 05e76113a..6a4db1822 100644
--- a/src/modules/m_allowinvite.cpp
+++ b/src/modules/m_allowinvite.cpp
@@ -47,7 +47,7 @@ class ModuleAllowInvite : public Module
if (res == MOD_RES_DENY)
{
// Matching extban, explicitly deny /invite
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You are banned from using INVITE", channel->name.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You are banned from using INVITE");
return res;
}
if (channel->IsModeSet(ni) || res == MOD_RES_ALLOW)
diff --git a/src/modules/m_alltime.cpp b/src/modules/m_alltime.cpp
index 58f7c4fb5..73c0fa994 100644
--- a/src/modules/m_alltime.cpp
+++ b/src/modules/m_alltime.cpp
@@ -31,13 +31,11 @@ class CommandAlltime : public Command
CmdResult Handle(const std::vector<std::string> &parameters, User *user)
{
- char fmtdate[64];
- time_t now = ServerInstance->Time();
- strftime(fmtdate, sizeof(fmtdate), "%Y-%m-%d %H:%M:%S", gmtime(&now));
+ const std::string fmtdate = InspIRCd::TimeString(ServerInstance->Time(), "%Y-%m-%d %H:%M:%S", true);
- std::string msg = ":" + ServerInstance->Config->ServerName + " NOTICE " + user->nick + " :System time is " + fmtdate + " (" + ConvToStr(ServerInstance->Time()) + ") on " + ServerInstance->Config->ServerName;
+ std::string msg = "System time is " + fmtdate + " (" + ConvToStr(ServerInstance->Time()) + ") on " + ServerInstance->Config->ServerName;
- user->SendText(msg);
+ user->WriteRemoteNotice(msg);
/* we want this routed out! */
return CMD_SUCCESS;
diff --git a/src/modules/m_auditorium.cpp b/src/modules/m_auditorium.cpp
index 60bdd2582..6f9eeb252 100644
--- a/src/modules/m_auditorium.cpp
+++ b/src/modules/m_auditorium.cpp
@@ -103,8 +103,8 @@ class ModuleAuditorium : public Module
if (IsVisible(memb))
return;
- const UserMembList* users = memb->chan->GetUsers();
- for(UserMembCIter i = users->begin(); i != users->end(); i++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
{
if (IS_LOCAL(i->first) && !CanSee(i->first, memb))
excepts.insert(i->first);
@@ -140,8 +140,8 @@ class ModuleAuditorium : public Module
// this channel should not be considered when listing my neighbors
i = include.erase(i);
// however, that might hide me from ops that can see me...
- const UserMembList* users = memb->chan->GetUsers();
- for(UserMembCIter j = users->begin(); j != users->end(); j++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for(Channel::MemberMap::const_iterator j = users.begin(); j != users.end(); ++j)
{
if (IS_LOCAL(j->first) && CanSee(j->first, memb))
exception[j->first] = true;
@@ -149,15 +149,15 @@ class ModuleAuditorium : public Module
}
}
- void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
+ ModResult OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
if (!memb)
- return;
+ return MOD_RES_PASSTHRU;
if (IsVisible(memb))
- return;
+ return MOD_RES_PASSTHRU;
if (CanSee(source, memb))
- return;
- line.clear();
+ return MOD_RES_PASSTHRU;
+ return MOD_RES_DENY;
}
};
diff --git a/src/modules/m_autoop.cpp b/src/modules/m_autoop.cpp
index 828bef14c..8c7f300da 100644
--- a/src/modules/m_autoop.cpp
+++ b/src/modules/m_autoop.cpp
@@ -47,13 +47,12 @@ class AutoOpList : public ListModeBase
if (pos == 0 || pos == std::string::npos)
return adding ? MOD_RES_DENY : MOD_RES_PASSTHRU;
unsigned int mylevel = channel->GetPrefixValue(source);
- std::string mid = parameter.substr(0, pos);
+ std::string mid(parameter, 0, pos);
PrefixMode* mh = FindMode(mid);
if (adding && !mh)
{
- source->WriteNumeric(415, "%s :Cannot find prefix mode '%s' for autoop",
- mid.c_str(), mid.c_str());
+ source->WriteNumeric(415, mid, InspIRCd::Format("Cannot find prefix mode '%s' for autoop", mid.c_str()));
return MOD_RES_DENY;
}
else if (!mh)
@@ -64,8 +63,7 @@ class AutoOpList : public ListModeBase
return MOD_RES_DENY;
if (mh->GetLevelRequired() > mylevel)
{
- source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be able to set mode '%s' to include it in an autoop",
- channel->name.c_str(), mid.c_str());
+ source->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, InspIRCd::Format("You must be able to set mode '%s' to include it in an autoop", mid.c_str()));
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
@@ -89,9 +87,7 @@ class ModuleAutoOp : public Module
ListModeBase::ModeList* list = mh.GetList(memb->chan);
if (list)
{
- std::string modeline("+");
- std::vector<std::string> modechange;
- modechange.push_back(memb->chan->name);
+ Modes::ChangeList changelist;
for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
{
std::string::size_type colon = it->mask.find(':');
@@ -101,14 +97,10 @@ class ModuleAutoOp : public Module
{
PrefixMode* given = mh.FindMode(it->mask.substr(0, colon));
if (given)
- modeline.push_back(given->GetModeChar());
+ changelist.push_add(given, memb->user->nick);
}
}
- modechange.push_back(modeline);
- for(std::string::size_type i = modeline.length(); i > 1; --i) // we use "i > 1" instead of "i" so we skip the +
- modechange.push_back(memb->user->nick);
- if(modechange.size() >= 3)
- ServerInstance->Modes->Process(modechange, ServerInstance->FakeClient);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, memb->chan, NULL, changelist);
}
}
diff --git a/src/modules/m_banredirect.cpp b/src/modules/m_banredirect.cpp
index 1a123e580..f98cbd420 100644
--- a/src/modules/m_banredirect.cpp
+++ b/src/modules/m_banredirect.cpp
@@ -50,7 +50,7 @@ class BanRedirect : public ModeWatcher
BanRedirect(Module* parent)
: ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
, ban(parent, "ban")
- , extItem("banredirect", parent)
+ , extItem("banredirect", ExtensionItem::EXT_CHANNEL, parent)
{
}
@@ -74,12 +74,15 @@ class BanRedirect : public ModeWatcher
if (param.length() >= 2 && param[1] == ':')
return true;
+ if (param.find('#') == std::string::npos)
+ return true;
+
ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
unsigned int maxbans = banlm->GetLimit(channel);
ListModeBase::ModeList* list = banlm->GetList(channel);
if ((list) && (adding) && (maxbans <= list->size()))
{
- source->WriteNumeric(ERR_BANLISTFULL, "%s :Channel ban list for %s is full (maximum entries for this channel is %u)", channel->name.c_str(), channel->name.c_str(), maxbans);
+ source->WriteNumeric(ERR_BANLISTFULL, channel->name, InspIRCd::Format("Channel ban list for %s is full (maximum entries for this channel is %u)", channel->name.c_str(), maxbans));
return false;
}
@@ -123,6 +126,14 @@ class BanRedirect : public ModeWatcher
mask[NICK].swap(mask[IDENT]);
}
+ if (!mask[NICK].empty() && mask[IDENT].empty() && mask[HOST].empty())
+ {
+ if (mask[NICK].find('.') != std::string::npos || mask[NICK].find(':') != std::string::npos)
+ {
+ mask[NICK].swap(mask[HOST]);
+ }
+ }
+
for(int i = 0; i < 3; i++)
{
if(mask[i].empty())
@@ -139,25 +150,25 @@ class BanRedirect : public ModeWatcher
{
if (!ServerInstance->IsChannel(mask[CHAN]))
{
- source->WriteNumeric(ERR_NOSUCHCHANNEL, "%s :Invalid channel name in redirection (%s)", channel->name.c_str(), mask[CHAN].c_str());
+ source->WriteNumeric(ERR_NOSUCHCHANNEL, channel->name, InspIRCd::Format("Invalid channel name in redirection (%s)", mask[CHAN].c_str()));
return false;
}
Channel *c = ServerInstance->FindChan(mask[CHAN]);
if (!c)
{
- source->WriteNumeric(690, ":Target channel %s must exist to be set as a redirect.", mask[CHAN].c_str());
+ source->WriteNumeric(690, InspIRCd::Format("Target channel %s must exist to be set as a redirect.", mask[CHAN].c_str()));
return false;
}
else if (adding && c->GetPrefixValue(source) < OP_VALUE)
{
- source->WriteNumeric(690, ":You must be opped on %s to set it as a redirect.", mask[CHAN].c_str());
+ source->WriteNumeric(690, InspIRCd::Format("You must be opped on %s to set it as a redirect.", mask[CHAN].c_str()));
return false;
}
- if (assign(channel->name) == mask[CHAN])
+ if (irc::equals(channel->name, mask[CHAN]))
{
- source->WriteNumeric(690, "%s :You cannot set a ban redirection to the channel the ban is on", channel->name.c_str());
+ source->WriteNumeric(690, channel->name, "You cannot set a ban redirection to the channel the ban is on");
return false;
}
}
@@ -188,8 +199,7 @@ class BanRedirect : public ModeWatcher
for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++)
{
- /* Ugly as fuck */
- if((irc::string(redir->targetchan.c_str()) == irc::string(mask[CHAN].c_str())) && (irc::string(redir->banmask.c_str()) == irc::string(param.c_str())))
+ if ((irc::equals(redir->targetchan, mask[CHAN])) && (irc::equals(redir->banmask, param)))
{
redirects->erase(redir);
@@ -238,26 +248,16 @@ class ModuleBanRedirect : public Module
if(redirects)
{
- irc::modestacker modestack(false);
+ ModeHandler* ban = ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL);
+ Modes::ChangeList changelist;
for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
- {
- modestack.Push('b', i->targetchan.insert(0, i->banmask));
- }
+ changelist.push_remove(ban, i->targetchan.insert(0, i->banmask));
for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
- {
- modestack.PushPlus();
- modestack.Push('b', i->banmask);
- }
+ changelist.push_add(ban, i->banmask);
- std::vector<std::string> stackresult;
- stackresult.push_back(chan->name);
- while (modestack.GetStackedLine(stackresult))
- {
- ServerInstance->Modes->Process(stackresult, ServerInstance->FakeClient, ModeParser::MODE_LOCALONLY);
- stackresult.erase(stackresult.begin() + 1, stackresult.end());
- }
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist, ModeParser::MODE_LOCALONLY);
}
}
}
@@ -310,13 +310,13 @@ class ModuleBanRedirect : public Module
if(destchan && destchan->IsModeSet(redirectmode) && !destlimit.empty() && (destchan->GetUserCounter() >= atoi(destlimit.c_str())))
{
- user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Cannot join channel (You are banned)", chan->name.c_str());
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, chan->name, "Cannot join channel (You are banned)");
return MOD_RES_DENY;
}
else
{
- user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Cannot join channel (You are banned)", chan->name.c_str());
- user->WriteNumeric(470, "%s %s :You are banned from this channel, so you are automatically transferred to the redirected channel.", chan->name.c_str(), redir->targetchan.c_str());
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, chan->name, "Cannot join channel (You are banned)");
+ user->WriteNumeric(470, chan->name, redir->targetchan, "You are banned from this channel, so you are automatically transferred to the redirected channel.");
nofollow = true;
Channel::JoinUser(user, redir->targetchan);
nofollow = false;
diff --git a/src/modules/m_bcrypt.cpp b/src/modules/m_bcrypt.cpp
new file mode 100644
index 000000000..8a025a0d6
--- /dev/null
+++ b/src/modules/m_bcrypt.cpp
@@ -0,0 +1,987 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Most of the code in this file is taken from
+ * http://openwall.com/crypt/crypt_blowfish-1.3.tar.gz
+ */
+
+/*
+ * The crypt_blowfish homepage is:
+ *
+ * http://www.openwall.com/crypt/
+ *
+ * This code comes from John the Ripper password cracker, with reentrant
+ * and crypt(3) interfaces added, but optimizations specific to password
+ * cracking removed.
+ *
+ * Written by Solar Designer <solar at openwall.com> in 1998-2014.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * It is my intent that you should be able to use this on your system,
+ * as part of a software package, or anywhere else to improve security,
+ * ensure compatibility, or for any other purpose. I would appreciate
+ * it if you give credit where it is due and keep your modifications in
+ * the public domain as well, but I don't require that in order to let
+ * you place this code and any modifications you make under a license
+ * of your choice.
+ *
+ * This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
+ * "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
+ * some of his ideas. The password hashing algorithm was designed by David
+ * Mazieres <dm at lcs.mit.edu>. For information on the level of
+ * compatibility for bcrypt hash prefixes other than "$2b$", please refer to
+ * the comments in BF_set_key() below and to the included crypt(3) man page.
+ *
+ * There's a paper on the algorithm that explains its design decisions:
+ *
+ * http://www.usenix.org/events/usenix99/provos.html
+ *
+ * Some of the tricks in BF_ROUND might be inspired by Eric Young's
+ * Blowfish library (I can't be sure if I would think of something if I
+ * hadn't seen his code).
+ */
+
+#include <string.h>
+
+#ifdef __i386__
+#define BF_SCALE 1
+#elif defined(__x86_64__) || defined(__alpha__) || defined(__hppa__)
+#define BF_SCALE 1
+#else
+#define BF_SCALE 0
+#endif
+
+typedef unsigned int BF_word;
+typedef signed int BF_word_signed;
+
+/* Number of Blowfish rounds, this is also hardcoded into a few places */
+#define BF_N 16
+
+typedef BF_word BF_key[BF_N + 2];
+
+typedef struct {
+ BF_word S[4][0x100];
+ BF_key P;
+} BF_ctx;
+
+/*
+ * Magic IV for 64 Blowfish encryptions that we do at the end.
+ * The string is "OrpheanBeholderScryDoubt" on big-endian.
+ */
+static BF_word BF_magic_w[6] = {
+ 0x4F727068, 0x65616E42, 0x65686F6C,
+ 0x64657253, 0x63727944, 0x6F756274
+};
+
+/*
+ * P-box and S-box tables initialized with digits of Pi.
+ */
+static BF_ctx BF_init_state = {
+ {
+ {
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
+ }, {
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
+ }, {
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
+ }, {
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
+ }
+ }, {
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b
+ }
+};
+
+static unsigned char BF_itoa64[64 + 1] =
+ "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+static unsigned char BF_atoi64[0x60] = {
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64,
+ 64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64,
+ 64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64
+};
+
+#define BF_safe_atoi64(dst, src) \
+{ \
+ tmp = (unsigned char)(src); \
+ if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
+ tmp = BF_atoi64[tmp]; \
+ if (tmp > 63) return -1; \
+ (dst) = tmp; \
+}
+
+static int BF_decode(BF_word *dst, const char *src, int size)
+{
+ unsigned char *dptr = (unsigned char *)dst;
+ unsigned char *end = dptr + size;
+ const unsigned char *sptr = (const unsigned char *)src;
+ unsigned int tmp, c1, c2, c3, c4;
+
+ do {
+ BF_safe_atoi64(c1, *sptr++);
+ BF_safe_atoi64(c2, *sptr++);
+ *dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4);
+ if (dptr >= end) break;
+
+ BF_safe_atoi64(c3, *sptr++);
+ *dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2);
+ if (dptr >= end) break;
+
+ BF_safe_atoi64(c4, *sptr++);
+ *dptr++ = ((c3 & 0x03) << 6) | c4;
+ } while (dptr < end);
+
+ return 0;
+}
+
+static void BF_encode(char *dst, const BF_word *src, int size)
+{
+ const unsigned char *sptr = (const unsigned char *)src;
+ const unsigned char *end = sptr + size;
+ unsigned char *dptr = (unsigned char *)dst;
+ unsigned int c1, c2;
+
+ do {
+ c1 = *sptr++;
+ *dptr++ = BF_itoa64[c1 >> 2];
+ c1 = (c1 & 0x03) << 4;
+ if (sptr >= end) {
+ *dptr++ = BF_itoa64[c1];
+ break;
+ }
+
+ c2 = *sptr++;
+ c1 |= c2 >> 4;
+ *dptr++ = BF_itoa64[c1];
+ c1 = (c2 & 0x0f) << 2;
+ if (sptr >= end) {
+ *dptr++ = BF_itoa64[c1];
+ break;
+ }
+
+ c2 = *sptr++;
+ c1 |= c2 >> 6;
+ *dptr++ = BF_itoa64[c1];
+ *dptr++ = BF_itoa64[c2 & 0x3f];
+ } while (sptr < end);
+}
+
+static void BF_swap(BF_word *x, int count)
+{
+ static int endianness_check = 1;
+ char *is_little_endian = (char *)&endianness_check;
+ BF_word tmp;
+
+ if (*is_little_endian)
+ do {
+ tmp = *x;
+ tmp = (tmp << 16) | (tmp >> 16);
+ *x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF);
+ } while (--count);
+}
+
+#if BF_SCALE
+/* Architectures which can shift addresses left by 2 bits with no extra cost */
+#define BF_ROUND(L, R, N) \
+ tmp1 = L & 0xFF; \
+ tmp2 = L >> 8; \
+ tmp2 &= 0xFF; \
+ tmp3 = L >> 16; \
+ tmp3 &= 0xFF; \
+ tmp4 = L >> 24; \
+ tmp1 = data.ctx.S[3][tmp1]; \
+ tmp2 = data.ctx.S[2][tmp2]; \
+ tmp3 = data.ctx.S[1][tmp3]; \
+ tmp3 += data.ctx.S[0][tmp4]; \
+ tmp3 ^= tmp2; \
+ R ^= data.ctx.P[N + 1]; \
+ tmp3 += tmp1; \
+ R ^= tmp3;
+#else
+/* Architectures with no complicated addressing modes supported */
+#define BF_INDEX(S, i) \
+ (*((BF_word *)(((unsigned char *)S) + (i))))
+#define BF_ROUND(L, R, N) \
+ tmp1 = L & 0xFF; \
+ tmp1 <<= 2; \
+ tmp2 = L >> 6; \
+ tmp2 &= 0x3FC; \
+ tmp3 = L >> 14; \
+ tmp3 &= 0x3FC; \
+ tmp4 = L >> 22; \
+ tmp4 &= 0x3FC; \
+ tmp1 = BF_INDEX(data.ctx.S[3], tmp1); \
+ tmp2 = BF_INDEX(data.ctx.S[2], tmp2); \
+ tmp3 = BF_INDEX(data.ctx.S[1], tmp3); \
+ tmp3 += BF_INDEX(data.ctx.S[0], tmp4); \
+ tmp3 ^= tmp2; \
+ R ^= data.ctx.P[N + 1]; \
+ tmp3 += tmp1; \
+ R ^= tmp3;
+#endif
+
+/*
+ * Encrypt one block, BF_N is hardcoded here.
+ */
+#define BF_ENCRYPT \
+ L ^= data.ctx.P[0]; \
+ BF_ROUND(L, R, 0); \
+ BF_ROUND(R, L, 1); \
+ BF_ROUND(L, R, 2); \
+ BF_ROUND(R, L, 3); \
+ BF_ROUND(L, R, 4); \
+ BF_ROUND(R, L, 5); \
+ BF_ROUND(L, R, 6); \
+ BF_ROUND(R, L, 7); \
+ BF_ROUND(L, R, 8); \
+ BF_ROUND(R, L, 9); \
+ BF_ROUND(L, R, 10); \
+ BF_ROUND(R, L, 11); \
+ BF_ROUND(L, R, 12); \
+ BF_ROUND(R, L, 13); \
+ BF_ROUND(L, R, 14); \
+ BF_ROUND(R, L, 15); \
+ tmp4 = R; \
+ R = L; \
+ L = tmp4 ^ data.ctx.P[BF_N + 1];
+
+#define BF_body() \
+ L = R = 0; \
+ ptr = data.ctx.P; \
+ do { \
+ ptr += 2; \
+ BF_ENCRYPT; \
+ *(ptr - 2) = L; \
+ *(ptr - 1) = R; \
+ } while (ptr < &data.ctx.P[BF_N + 2]); \
+\
+ ptr = data.ctx.S[0]; \
+ do { \
+ ptr += 2; \
+ BF_ENCRYPT; \
+ *(ptr - 2) = L; \
+ *(ptr - 1) = R; \
+ } while (ptr < &data.ctx.S[3][0xFF]);
+
+static void BF_set_key(const char *key, BF_key expanded, BF_key initial,
+ unsigned char flags)
+{
+ const char *ptr = key;
+ unsigned int bug, i, j;
+ BF_word safety, sign, diff, tmp[2];
+
+/*
+ * There was a sign extension bug in older revisions of this function. While
+ * we would have liked to simply fix the bug and move on, we have to provide
+ * a backwards compatibility feature (essentially the bug) for some systems and
+ * a safety measure for some others. The latter is needed because for certain
+ * multiple inputs to the buggy algorithm there exist easily found inputs to
+ * the correct algorithm that produce the same hash. Thus, we optionally
+ * deviate from the correct algorithm just enough to avoid such collisions.
+ * While the bug itself affected the majority of passwords containing
+ * characters with the 8th bit set (although only a percentage of those in a
+ * collision-producing way), the anti-collision safety measure affects
+ * only a subset of passwords containing the '\xff' character (not even all of
+ * those passwords, just some of them). This character is not found in valid
+ * UTF-8 sequences and is rarely used in popular 8-bit character encodings.
+ * Thus, the safety measure is unlikely to cause much annoyance, and is a
+ * reasonable tradeoff to use when authenticating against existing hashes that
+ * are not reliably known to have been computed with the correct algorithm.
+ *
+ * We use an approach that tries to minimize side-channel leaks of password
+ * information - that is, we mostly use fixed-cost bitwise operations instead
+ * of branches or table lookups. (One conditional branch based on password
+ * length remains. It is not part of the bug aftermath, though, and is
+ * difficult and possibly unreasonable to avoid given the use of C strings by
+ * the caller, which results in similar timing leaks anyway.)
+ *
+ * For actual implementation, we set an array index in the variable "bug"
+ * (0 means no bug, 1 means sign extension bug emulation) and a flag in the
+ * variable "safety" (bit 16 is set when the safety measure is requested).
+ * Valid combinations of settings are:
+ *
+ * Prefix "$2a$": bug = 0, safety = 0x10000
+ * Prefix "$2b$": bug = 0, safety = 0
+ * Prefix "$2x$": bug = 1, safety = 0
+ * Prefix "$2y$": bug = 0, safety = 0
+ */
+ bug = (unsigned int)flags & 1;
+ safety = ((BF_word)flags & 2) << 15;
+
+ sign = diff = 0;
+
+ for (i = 0; i < BF_N + 2; i++) {
+ tmp[0] = tmp[1] = 0;
+ for (j = 0; j < 4; j++) {
+ tmp[0] <<= 8;
+ tmp[0] |= (unsigned char)*ptr; /* correct */
+ tmp[1] <<= 8;
+ tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */
+/*
+ * Sign extension in the first char has no effect - nothing to overwrite yet,
+ * and those extra 24 bits will be fully shifted out of the 32-bit word. For
+ * chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
+ * extension in tmp[1] occurs. Once this flag is set, it remains set.
+ */
+ if (j)
+ sign |= tmp[1] & 0x80;
+ if (!*ptr)
+ ptr = key;
+ else
+ ptr++;
+ }
+ diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */
+
+ expanded[i] = tmp[bug];
+ initial[i] = BF_init_state.P[i] ^ tmp[bug];
+ }
+
+/*
+ * At this point, "diff" is zero iff the correct and buggy algorithms produced
+ * exactly the same result. If so and if "sign" is non-zero, which indicates
+ * that there was a non-benign sign extension, this means that we have a
+ * collision between the correctly computed hash for this password and a set of
+ * passwords that could be supplied to the buggy algorithm. Our safety measure
+ * is meant to protect from such many-buggy to one-correct collisions, by
+ * deviating from the correct algorithm in such cases. Let's check for this.
+ */
+ diff |= diff >> 16; /* still zero iff exact match */
+ diff &= 0xffff; /* ditto */
+ diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
+ sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
+ sign &= ~diff & safety; /* action needed? */
+
+/*
+ * If we have determined that we need to deviate from the correct algorithm,
+ * flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but
+ * let's stick to it now. It came out of the approach we used above, and it's
+ * not any worse than any other choice we could make.)
+ *
+ * It is crucial that we don't do the same to the expanded key used in the main
+ * Eksblowfish loop. By doing it to only one of these two, we deviate from a
+ * state that could be directly specified by a password to the buggy algorithm
+ * (and to the fully correct one as well, but that's a side-effect).
+ */
+ initial[0] ^= sign;
+}
+
+static const unsigned char flags_by_subtype[26] =
+ {2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
+
+static char *BF_crypt(const char *key, const char *setting,
+ char *output, int size,
+ BF_word min)
+{
+ struct {
+ BF_ctx ctx;
+ BF_key expanded_key;
+ union {
+ BF_word salt[4];
+ BF_word output[6];
+ } binary;
+ } data;
+ BF_word L, R;
+ BF_word tmp1, tmp2, tmp3, tmp4;
+ BF_word *ptr;
+ BF_word count;
+ int i;
+
+ if (size < 7 + 22 + 31 + 1) {
+ return NULL;
+ }
+
+ if (setting[0] != '$' ||
+ setting[1] != '2' ||
+ setting[2] < 'a' || setting[2] > 'z' ||
+ !flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] ||
+ setting[3] != '$' ||
+ setting[4] < '0' || setting[4] > '3' ||
+ setting[5] < '0' || setting[5] > '9' ||
+ (setting[4] == '3' && setting[5] > '1') ||
+ setting[6] != '$') {
+ return NULL;
+ }
+
+ count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
+ if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) {
+ return NULL;
+ }
+ BF_swap(data.binary.salt, 4);
+
+ BF_set_key(key, data.expanded_key, data.ctx.P,
+ flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']);
+
+ memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
+
+ L = R = 0;
+ for (i = 0; i < BF_N + 2; i += 2) {
+ L ^= data.binary.salt[i & 2];
+ R ^= data.binary.salt[(i & 2) + 1];
+ BF_ENCRYPT;
+ data.ctx.P[i] = L;
+ data.ctx.P[i + 1] = R;
+ }
+
+ ptr = data.ctx.S[0];
+ do {
+ ptr += 4;
+ L ^= data.binary.salt[(BF_N + 2) & 3];
+ R ^= data.binary.salt[(BF_N + 3) & 3];
+ BF_ENCRYPT;
+ *(ptr - 4) = L;
+ *(ptr - 3) = R;
+
+ L ^= data.binary.salt[(BF_N + 4) & 3];
+ R ^= data.binary.salt[(BF_N + 5) & 3];
+ BF_ENCRYPT;
+ *(ptr - 2) = L;
+ *(ptr - 1) = R;
+ } while (ptr < &data.ctx.S[3][0xFF]);
+
+ do {
+ int done;
+
+ for (i = 0; i < BF_N + 2; i += 2) {
+ data.ctx.P[i] ^= data.expanded_key[i];
+ data.ctx.P[i + 1] ^= data.expanded_key[i + 1];
+ }
+
+ done = 0;
+ do {
+ BF_body();
+ if (done)
+ break;
+ done = 1;
+
+ tmp1 = data.binary.salt[0];
+ tmp2 = data.binary.salt[1];
+ tmp3 = data.binary.salt[2];
+ tmp4 = data.binary.salt[3];
+ for (i = 0; i < BF_N; i += 4) {
+ data.ctx.P[i] ^= tmp1;
+ data.ctx.P[i + 1] ^= tmp2;
+ data.ctx.P[i + 2] ^= tmp3;
+ data.ctx.P[i + 3] ^= tmp4;
+ }
+ data.ctx.P[16] ^= tmp1;
+ data.ctx.P[17] ^= tmp2;
+ } while (1);
+ } while (--count);
+
+ for (i = 0; i < 6; i += 2) {
+ L = BF_magic_w[i];
+ R = BF_magic_w[i + 1];
+
+ count = 64;
+ do {
+ BF_ENCRYPT;
+ } while (--count);
+
+ data.binary.output[i] = L;
+ data.binary.output[i + 1] = R;
+ }
+
+ memcpy(output, setting, 7 + 22 - 1);
+ output[7 + 22 - 1] = BF_itoa64[(int)
+ BF_atoi64[(int)setting[7 + 22 - 1] - 0x20] & 0x30];
+
+/* This has to be bug-compatible with the original implementation, so
+ * only encode 23 of the 24 bytes. :-) */
+ BF_swap(data.binary.output, 6);
+ BF_encode(&output[7 + 22], data.binary.output, 23);
+ output[7 + 22 + 31] = '\0';
+
+ return output;
+}
+
+static int _crypt_output_magic(const char *setting, char *output, int size)
+{
+ if (size < 3)
+ return -1;
+
+ output[0] = '*';
+ output[1] = '0';
+ output[2] = '\0';
+
+ if (setting[0] == '*' && setting[1] == '0')
+ output[1] = '1';
+
+ return 0;
+}
+
+/*
+ * Please preserve the runtime self-test. It serves two purposes at once:
+ *
+ * 1. We really can't afford the risk of producing incompatible hashes e.g.
+ * when there's something like gcc bug 26587 again, whereas an application or
+ * library integrating this code might not also integrate our external tests or
+ * it might not run them after every build. Even if it does, the miscompile
+ * might only occur on the production build, but not on a testing build (such
+ * as because of different optimization settings). It is painful to recover
+ * from incorrectly-computed hashes - merely fixing whatever broke is not
+ * enough. Thus, a proactive measure like this self-test is needed.
+ *
+ * 2. We don't want to leave sensitive data from our actual password hash
+ * computation on the stack or in registers. Previous revisions of the code
+ * would do explicit cleanups, but simply running the self-test after hash
+ * computation is more reliable.
+ *
+ * The performance cost of this quick self-test is around 0.6% at the "$2a$08"
+ * setting.
+ */
+static char *_crypt_blowfish_rn(const char *key, const char *setting,
+ char *output, int size)
+{
+ const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
+ const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu";
+ static const char * const test_hashes[2] =
+ {"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */
+ "VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"}; /* 'x' */
+ const char *test_hash = test_hashes[0];
+ char *retval;
+ const char *p;
+ int ok;
+ struct {
+ char s[7 + 22 + 1];
+ char o[7 + 22 + 31 + 1 + 1 + 1];
+ } buf;
+
+/* Hash the supplied password */
+ _crypt_output_magic(setting, output, size);
+ retval = BF_crypt(key, setting, output, size, 16);
+
+/*
+ * Do a quick self-test. It is important that we make both calls to BF_crypt()
+ * from the same scope such that they likely use the same stack locations,
+ * which makes the second call overwrite the first call's sensitive data on the
+ * stack and makes it more likely that any alignment related issues would be
+ * detected by the self-test.
+ */
+ memcpy(buf.s, test_setting, sizeof(buf.s));
+ if (retval) {
+ unsigned int flags = flags_by_subtype[
+ (unsigned int)(unsigned char)setting[2] - 'a'];
+ test_hash = test_hashes[flags & 1];
+ buf.s[2] = setting[2];
+ }
+ memset(buf.o, 0x55, sizeof(buf.o));
+ buf.o[sizeof(buf.o) - 1] = 0;
+ p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
+
+ ok = (p == buf.o &&
+ !memcmp(p, buf.s, 7 + 22) &&
+ !memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1));
+
+ {
+ const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
+ BF_key ae, ai, ye, yi;
+ BF_set_key(k, ae, ai, 2); /* $2a$ */
+ BF_set_key(k, ye, yi, 4); /* $2y$ */
+ ai[0] ^= 0x10000; /* undo the safety (for comparison) */
+ ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 &&
+ !memcmp(ae, ye, sizeof(ae)) &&
+ !memcmp(ai, yi, sizeof(ai));
+ }
+
+ if (ok)
+ return retval;
+
+/* Should not happen */
+ _crypt_output_magic(setting, output, size);
+ /* pretend we don't support this hash type */
+ return NULL;
+}
+
+static char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ if (size < 16 || output_size < 7 + 22 + 1 ||
+ (count && (count < 4 || count > 31)) ||
+ prefix[0] != '$' || prefix[1] != '2' ||
+ (prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y')) {
+ if (output_size > 0) output[0] = '\0';
+ return NULL;
+ }
+
+ if (!count) count = 5;
+
+ output[0] = '$';
+ output[1] = '2';
+ output[2] = prefix[2];
+ output[3] = '$';
+ output[4] = '0' + count / 10;
+ output[5] = '0' + count % 10;
+ output[6] = '$';
+
+ BF_encode(&output[7], (const BF_word *)input, 16);
+ output[7 + 22] = '\0';
+
+ return output;
+}
+
+// Start inspircd-specific code
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+class BCryptProvider : public HashProvider
+{
+ private:
+ std::string Salt()
+ {
+ char entropy[16];
+ for (unsigned int i = 0; i < sizeof(entropy); ++i)
+ entropy[i] = ServerInstance->GenRandomInt(0xFF);
+
+ char salt[32];
+ if (!_crypt_gensalt_blowfish_rn("$2a$", rounds, entropy, sizeof(entropy), salt, sizeof(salt)))
+ throw ModuleException("Could not generate salt - this should never happen");
+
+ return salt;
+ }
+
+ public:
+ unsigned int rounds;
+
+ std::string Generate(const std::string& data, const std::string& salt)
+ {
+ char hash[64];
+ _crypt_blowfish_rn(data.c_str(), salt.c_str(), hash, sizeof(hash));
+ return hash;
+ }
+
+ std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
+ {
+ return Generate(data, Salt());
+ }
+
+ bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
+ {
+ std::string ret = Generate(input, hash);
+ if (ret.empty())
+ return false;
+
+ if (ret == hash)
+ return true;
+ return false;
+ }
+
+ std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
+ {
+ return raw;
+ }
+
+ BCryptProvider(Module* parent)
+ : HashProvider(parent, "bcrypt", 60)
+ , rounds(10)
+ {
+ }
+};
+
+class ModuleBCrypt : public Module
+{
+ BCryptProvider bcrypt;
+
+ public:
+ ModuleBCrypt() : bcrypt(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* conf = ServerInstance->Config->ConfValue("bcrypt");
+ bcrypt.rounds = conf->getInt("rounds", 10, 1);
+ }
+
+ Version GetVersion()
+ {
+ return Version("Implements bcrypt hashing", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleBCrypt)
diff --git a/src/modules/m_blockamsg.cpp b/src/modules/m_blockamsg.cpp
index 833828233..266497b90 100644
--- a/src/modules/m_blockamsg.cpp
+++ b/src/modules/m_blockamsg.cpp
@@ -37,11 +37,11 @@ class BlockedMessage
{
public:
std::string message;
- irc::string target;
+ std::string target;
time_t sent;
- BlockedMessage(const std::string &msg, const irc::string &tgt, time_t when)
- : message(msg), target(tgt), sent(when)
+ BlockedMessage(const std::string& msg, const std::string& tgt, time_t when)
+ : message(msg), target(tgt), sent(when)
{
}
};
@@ -53,7 +53,8 @@ class ModuleBlockAmsg : public Module
SimpleExtItem<BlockedMessage> blockamsg;
public:
- ModuleBlockAmsg() : blockamsg("blockamsg", this)
+ ModuleBlockAmsg()
+ : blockamsg("blockamsg", ExtensionItem::EXT_USER, this)
{
}
@@ -68,13 +69,13 @@ class ModuleBlockAmsg : public Module
ForgetDelay = tag->getInt("delay", -1);
std::string act = tag->getString("action");
- if(act == "notice")
+ if (act == "notice")
action = IBLOCK_NOTICE;
- else if(act == "noticeopers")
+ else if (act == "noticeopers")
action = IBLOCK_NOTICEOPERS;
- else if(act == "silent")
+ else if (act == "silent")
action = IBLOCK_SILENT;
- else if(act == "kill")
+ else if (act == "kill")
action = IBLOCK_KILL;
else
action = IBLOCK_KILLOPERS;
@@ -88,33 +89,24 @@ class ModuleBlockAmsg : public Module
if ((validated) && (parameters.size() >= 2) && ((command == "PRIVMSG") || (command == "NOTICE")))
{
- // parameters[0] should have the target(s) in it.
- // I think it will be faster to first check if there are any commas, and if there are then try and parse it out.
- // Most messages have a single target so...
+ // parameters[0] is the target list, count how many channels are there
+ unsigned int targets = 0;
+ // Is the first target a channel?
+ if (*parameters[0].c_str() == '#')
+ targets = 1;
- int targets = 1;
- int userchans = 0;
-
- if(*parameters[0].c_str() != '#')
+ for (const char* c = parameters[0].c_str(); *c; c++)
{
- // Decrement if the first target wasn't a channel.
- targets--;
- }
-
- for(const char* c = parameters[0].c_str(); *c; c++)
- if((*c == ',') && *(c+1) && (*(c+1) == '#'))
+ if ((*c == ',') && (*(c+1) == '#'))
targets++;
+ }
/* targets should now contain the number of channel targets the msg/notice was pointed at.
* If the msg/notice was a PM there should be no channel targets and 'targets' should = 0.
* We don't want to block PMs so...
*/
- if(targets == 0)
- {
+ if (targets == 0)
return MOD_RES_PASSTHRU;
- }
-
- userchans = user->chans.size();
// Check that this message wasn't already sent within a few seconds.
BlockedMessage* m = blockamsg.get(user);
@@ -124,30 +116,30 @@ class ModuleBlockAmsg : public Module
// OR
// The number of target channels is equal to the number of channels the sender is on..a little suspicious.
// Check it's more than 1 too, or else users on one channel would have fun.
- if((m && (m->message == parameters[1]) && (m->target != parameters[0]) && (ForgetDelay != -1) && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == userchans)))
+ if ((m && (m->message == parameters[1]) && (!irc::equals(m->target, parameters[0])) && (ForgetDelay != -1) && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == user->chans.size())))
{
// Block it...
- if(action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS)
+ if (action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS)
ServerInstance->SNO->WriteToSnoMask('a', "%s had an /amsg or /ame denied", user->nick.c_str());
- if(action == IBLOCK_KILL || action == IBLOCK_KILLOPERS)
+ if (action == IBLOCK_KILL || action == IBLOCK_KILLOPERS)
ServerInstance->Users->QuitUser(user, "Attempted to global message (/amsg or /ame)");
- else if(action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS)
- user->WriteServ( "NOTICE %s :Global message (/amsg or /ame) denied", user->nick.c_str());
+ else if (action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS)
+ user->WriteNotice("Global message (/amsg or /ame) denied");
return MOD_RES_DENY;
}
- if(m)
+ if (m)
{
// If there's already a BlockedMessage allocated, use it.
m->message = parameters[1];
- m->target = parameters[0].c_str();
+ m->target = parameters[0];
m->sent = ServerInstance->Time();
}
else
{
- m = new BlockedMessage(parameters[1], parameters[0].c_str(), ServerInstance->Time());
+ m = new BlockedMessage(parameters[1], parameters[0], ServerInstance->Time());
blockamsg.set(user, m);
}
}
diff --git a/src/modules/m_blockcaps.cpp b/src/modules/m_blockcaps.cpp
index 0a64a75b5..cd7698d69 100644
--- a/src/modules/m_blockcaps.cpp
+++ b/src/modules/m_blockcaps.cpp
@@ -74,7 +74,7 @@ public:
if (((caps * 100) / text.length()) >= percent)
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Your message cannot contain more than %d%% capital letters if it's longer than %d characters", c->name.c_str(), percent, minlen);
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, InspIRCd::Format("Your message cannot contain %d%% or more capital letters if it's longer than %d characters", percent, minlen));
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_blockcolor.cpp b/src/modules/m_blockcolor.cpp
index a08ad7c6f..567bdb249 100644
--- a/src/modules/m_blockcolor.cpp
+++ b/src/modules/m_blockcolor.cpp
@@ -67,7 +67,7 @@ class ModuleBlockColor : public Module
case 21:
case 22:
case 31:
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Can't send colors to channel (+c set)", c->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send colors to channel (+c set)");
return MOD_RES_DENY;
break;
}
diff --git a/src/modules/m_botmode.cpp b/src/modules/m_botmode.cpp
index 67f692b86..e0236bc17 100644
--- a/src/modules/m_botmode.cpp
+++ b/src/modules/m_botmode.cpp
@@ -29,12 +29,13 @@ class BotMode : public SimpleUserModeHandler
BotMode(Module* Creator) : SimpleUserModeHandler(Creator, "bot", 'B') { }
};
-class ModuleBotMode : public Module
+class ModuleBotMode : public Module, public Whois::EventListener
{
BotMode bm;
public:
ModuleBotMode()
- : bm(this)
+ : Whois::EventListener(this)
+ , bm(this)
{
}
@@ -43,11 +44,11 @@ class ModuleBotMode : public Module
return Version("Provides user mode +B to mark the user as a bot",VF_VENDOR);
}
- void OnWhois(User* src, User* dst) CXX11_OVERRIDE
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- if (dst->IsModeSet(bm))
+ if (whois.GetTarget()->IsModeSet(bm))
{
- ServerInstance->SendWhoisLine(src, dst, 335, dst->nick+" :is a bot on "+ServerInstance->Config->Network);
+ whois.SendLine(335, "is a bot on " + ServerInstance->Config->Network);
}
}
};
diff --git a/src/modules/m_callerid.cpp b/src/modules/m_callerid.cpp
index 6f2c67300..e11b326de 100644
--- a/src/modules/m_callerid.cpp
+++ b/src/modules/m_callerid.cpp
@@ -37,15 +37,18 @@ enum
class callerid_data
{
public:
+ typedef insp::flat_set<User*> UserSet;
+ typedef std::vector<callerid_data*> CallerIdDataSet;
+
time_t lastnotify;
/** Users I accept messages from
*/
- std::set<User*> accepting;
+ UserSet accepting;
/** Users who list me as accepted
*/
- std::list<callerid_data *> wholistsme;
+ CallerIdDataSet wholistsme;
callerid_data() : lastnotify(0) { }
@@ -53,7 +56,7 @@ class callerid_data
{
std::ostringstream oss;
oss << lastnotify;
- for (std::set<User*>::const_iterator i = accepting.begin(); i != accepting.end(); ++i)
+ for (UserSet::const_iterator i = accepting.begin(); i != accepting.end(); ++i)
{
User* u = *i;
// Encode UIDs.
@@ -66,7 +69,7 @@ class callerid_data
struct CallerIDExtInfo : public ExtensionItem
{
CallerIDExtInfo(Module* parent)
- : ExtensionItem("callerid_data", parent)
+ : ExtensionItem("callerid_data", ExtensionItem::EXT_USER, parent)
{
}
@@ -86,7 +89,12 @@ struct CallerIDExtInfo : public ExtensionItem
if (format == FORMAT_NETWORK)
return;
+ void* old = get_raw(container);
+ if (old)
+ this->free(old);
callerid_data* dat = new callerid_data;
+ set_raw(container, dat);
+
irc::commasepstream s(value);
std::string tok;
if (s.GetToken(tok))
@@ -95,7 +103,7 @@ struct CallerIDExtInfo : public ExtensionItem
while (s.GetToken(tok))
{
User *u = ServerInstance->FindNick(tok);
- if ((u) && (u->registered == REG_ALL) && (!u->quitting) && (!IS_SERVER(u)))
+ if ((u) && (u->registered == REG_ALL) && (!u->quitting))
{
if (dat->accepting.insert(u).second)
{
@@ -104,10 +112,6 @@ struct CallerIDExtInfo : public ExtensionItem
}
}
}
-
- void* old = set_raw(container, dat);
- if (old)
- this->free(old);
}
callerid_data* get(User* user, bool create)
@@ -126,7 +130,7 @@ struct CallerIDExtInfo : public ExtensionItem
callerid_data* dat = static_cast<callerid_data*>(item);
// We need to walk the list of users on our accept list, and remove ourselves from their wholistsme.
- for (std::set<User *>::iterator it = dat->accepting.begin(); it != dat->accepting.end(); it++)
+ for (callerid_data::UserSet::iterator it = dat->accepting.begin(); it != dat->accepting.end(); ++it)
{
callerid_data *targ = this->get(*it, false);
@@ -136,10 +140,7 @@ struct CallerIDExtInfo : public ExtensionItem
continue; // shouldn't happen, but oh well.
}
- std::list<callerid_data*>::iterator it2 = std::find(targ->wholistsme.begin(), targ->wholistsme.end(), dat);
- if (it2 != targ->wholistsme.end())
- targ->wholistsme.erase(it2);
- else
+ if (!stdalgo::vector::swaperase(targ->wholistsme, dat))
ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (2)");
}
delete dat;
@@ -170,7 +171,7 @@ class CommandAccept : public Command
else
target = ServerInstance->FindNickOnly(tok);
- if ((!target) || (target->registered != REG_ALL) || (target->quitting) || (IS_SERVER(target)))
+ if ((!target) || (target->registered != REG_ALL) || (target->quitting))
target = NULL;
return std::make_pair(target, !remove);
@@ -183,7 +184,7 @@ public:
extInfo(Creator)
{
allow_empty_last_param = false;
- syntax = "{[+|-]<nicks>}|*}";
+ syntax = "*|(+|-)<nick>[,(+|-)<nick> ...]";
TRANSLATE1(TR_CUSTOM);
}
@@ -224,7 +225,7 @@ public:
ACCEPTAction action = GetTargetAndAction(tok, user);
if (!action.first)
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", tok.c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(tok));
return CMD_FAILURE;
}
@@ -267,10 +268,10 @@ public:
callerid_data* dat = extInfo.get(user, false);
if (dat)
{
- for (std::set<User*>::iterator i = dat->accepting.begin(); i != dat->accepting.end(); ++i)
+ for (callerid_data::UserSet::iterator i = dat->accepting.begin(); i != dat->accepting.end(); ++i)
user->WriteNumeric(RPL_ACCEPTLIST, (*i)->nick);
}
- user->WriteNumeric(RPL_ENDOFACCEPT, ":End of ACCEPT list");
+ user->WriteNumeric(RPL_ENDOFACCEPT, "End of ACCEPT list");
}
bool AddAccept(User* user, User* whotoadd)
@@ -279,12 +280,12 @@ public:
callerid_data* dat = extInfo.get(user, true);
if (dat->accepting.size() >= maxaccepts)
{
- user->WriteNumeric(ERR_ACCEPTFULL, ":Accept list is full (limit is %d)", maxaccepts);
+ user->WriteNumeric(ERR_ACCEPTFULL, InspIRCd::Format("Accept list is full (limit is %d)", maxaccepts));
return false;
}
if (!dat->accepting.insert(whotoadd).second)
{
- user->WriteNumeric(ERR_ACCEPTEXIST, "%s :is already on your accept list", whotoadd->nick.c_str());
+ user->WriteNumeric(ERR_ACCEPTEXIST, whotoadd->nick, "is already on your accept list");
return false;
}
@@ -302,18 +303,15 @@ public:
callerid_data* dat = extInfo.get(user, false);
if (!dat)
{
- user->WriteNumeric(ERR_ACCEPTNOT, "%s :is not on your accept list", whotoremove->nick.c_str());
+ user->WriteNumeric(ERR_ACCEPTNOT, whotoremove->nick, "is not on your accept list");
return false;
}
- std::set<User*>::iterator i = dat->accepting.find(whotoremove);
- if (i == dat->accepting.end())
+ if (!dat->accepting.erase(whotoremove))
{
- user->WriteNumeric(ERR_ACCEPTNOT, "%s :is not on your accept list", whotoremove->nick.c_str());
+ user->WriteNumeric(ERR_ACCEPTNOT, whotoremove->nick, "is not on your accept list");
return false;
}
- dat->accepting.erase(i);
-
// Look up their list to remove me.
callerid_data *dat2 = extInfo.get(whotoremove, false);
if (!dat2)
@@ -323,11 +321,7 @@ public:
return false;
}
- std::list<callerid_data*>::iterator it = std::find(dat2->wholistsme.begin(), dat2->wholistsme.end(), dat);
- if (it != dat2->wholistsme.end())
- // Found me!
- dat2->wholistsme.erase(it);
- else
+ if (!stdalgo::vector::swaperase(dat2->wholistsme, dat))
ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (4)");
@@ -357,16 +351,12 @@ class ModuleCallerID : public Module
return;
// Iterate over the list of people who accept me, and remove all entries
- for (std::list<callerid_data *>::iterator it = userdata->wholistsme.begin(); it != userdata->wholistsme.end(); it++)
+ for (callerid_data::CallerIdDataSet::iterator it = userdata->wholistsme.begin(); it != userdata->wholistsme.end(); ++it)
{
callerid_data *dat = *(it);
// Find me on their callerid list
- std::set<User *>::iterator it2 = dat->accepting.find(who);
-
- if (it2 != dat->accepting.end())
- dat->accepting.erase(it2);
- else
+ if (!dat->accepting.erase(who))
ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (5)");
}
@@ -401,18 +391,16 @@ public:
return MOD_RES_PASSTHRU;
callerid_data* dat = cmd.extInfo.get(dest, true);
- std::set<User*>::iterator i = dat->accepting.find(user);
-
- if (i == dat->accepting.end())
+ if (!dat->accepting.count(user))
{
time_t now = ServerInstance->Time();
/* +g and *not* accepted */
- user->WriteNumeric(ERR_TARGUMODEG, "%s :is in +g mode (server-side ignore).", dest->nick.c_str());
+ user->WriteNumeric(ERR_TARGUMODEG, dest->nick, "is in +g mode (server-side ignore).");
if (now > (dat->lastnotify + (time_t)notify_cooldown))
{
- user->WriteNumeric(RPL_TARGNOTIFY, "%s :has been informed that you messaged them.", dest->nick.c_str());
- dest->SendText(":%s %03d %s %s %s@%s :is messaging you, and you have umode +g. Use /ACCEPT +%s to allow.",
- ServerInstance->Config->ServerName.c_str(), RPL_UMODEGMSG, dest->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), user->nick.c_str());
+ user->WriteNumeric(RPL_TARGNOTIFY, dest->nick, "has been informed that you messaged them.");
+ dest->WriteRemoteNumeric(RPL_UMODEGMSG, user->nick, InspIRCd::Format("%s@%s", user->ident.c_str(), user->dhost.c_str()), InspIRCd::Format("is messaging you, and you have umode +g. Use /ACCEPT +%s to allow.",
+ user->nick.c_str()));
dat->lastnotify = now;
}
return MOD_RES_DENY;
@@ -439,6 +427,12 @@ public:
tracknick = tag->getBool("tracknick");
notify_cooldown = tag->getInt("cooldown", 60);
}
+
+ void Prioritize() CXX11_OVERRIDE
+ {
+ // Want to be after modules like silence or services_account
+ ServerInstance->Modules->SetPriority(this, I_OnUserPreMessage, PRIORITY_LAST);
+ }
};
MODULE_INIT(ModuleCallerID)
diff --git a/src/modules/m_cap.cpp b/src/modules/m_cap.cpp
index bc79e59ec..868294fe4 100644
--- a/src/modules/m_cap.cpp
+++ b/src/modules/m_cap.cpp
@@ -1,8 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -19,98 +18,388 @@
#include "inspircd.h"
+#include "modules/reload.h"
#include "modules/cap.h"
-/*
-CAP LS
-:alfred.staticbox.net CAP * LS :multi-prefix sasl
-CAP REQ :multi-prefix
-:alfred.staticbox.net CAP * ACK :multi-prefix
-CAP CLEAR
-:alfred.staticbox.net CAP * ACK :-multi-prefix
-CAP REQ :multi-prefix
-:alfred.staticbox.net CAP * ACK :multi-prefix
-CAP LIST
-:alfred.staticbox.net CAP * LIST :multi-prefix
-CAP END
-*/
-
-/** Handle /CAP
- */
-class CommandCAP : public Command
+namespace Cap
{
- public:
- LocalIntExt reghold;
- CommandCAP (Module* mod) : Command(mod, "CAP", 1),
- reghold("CAP_REGHOLD", mod)
+ class ManagerImpl;
+}
+
+static Cap::ManagerImpl* managerimpl;
+
+class Cap::ManagerImpl : public Cap::Manager, public ReloadModule::EventListener
+{
+ /** Stores the cap state of a module being reloaded
+ */
+ struct CapModData
{
- works_before_reg = true;
+ struct Data
+ {
+ std::string name;
+ std::vector<std::string> users;
+
+ Data(Capability* cap)
+ : name(cap->GetName())
+ {
+ }
+ };
+ std::vector<Data> caps;
+ };
+
+ typedef insp::flat_map<std::string, Capability*, irc::insensitive_swo> CapMap;
+
+ ExtItem capext;
+ CapMap caps;
+ Events::ModuleEventProvider& evprov;
+
+ static bool CanRequest(LocalUser* user, Ext usercaps, Capability* cap, bool adding)
+ {
+ const bool hascap = ((usercaps & cap->GetMask()) != 0);
+ if (hascap == adding)
+ return true;
+
+ return cap->OnRequest(user, adding);
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ Capability::Bit AllocateBit() const
{
- std::string subcommand(parameters[0].length(), ' ');
- std::transform(parameters[0].begin(), parameters[0].end(), subcommand.begin(), ::toupper);
+ Capability::Bit used = 0;
+ for (CapMap::const_iterator i = caps.begin(); i != caps.end(); ++i)
+ {
+ Capability* cap = i->second;
+ used |= cap->GetMask();
+ }
- if (subcommand == "REQ")
+ for (unsigned int i = 0; i < MAX_CAPS; i++)
{
- if (parameters.size() < 2)
- return CMD_FAILURE;
+ Capability::Bit bit = (1 << i);
+ if (!(used & bit))
+ return bit;
+ }
+ throw ModuleException("Too many caps");
+ }
- CapEvent Data(creator, user, CapEvent::CAPEVENT_REQ);
+ void OnReloadModuleSave(Module* mod, ReloadModule::CustomData& cd) CXX11_OVERRIDE
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnReloadModuleSave()");
+ if (mod == creator)
+ return;
- // tokenize the input into a nice list of requested caps
- std::string cap_;
- irc::spacesepstream cap_stream(parameters[1]);
+ CapModData* capmoddata = new CapModData;
+ cd.add(this, capmoddata);
- while (cap_stream.GetToken(cap_))
+ for (CapMap::iterator i = caps.begin(); i != caps.end(); ++i)
+ {
+ Capability* cap = i->second;
+ // Only save users of caps that belong to the module being reloaded
+ if (cap->creator != mod)
+ continue;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Module being reloaded implements cap %s, saving cap users", cap->GetName().c_str());
+ capmoddata->caps.push_back(CapModData::Data(cap));
+ CapModData::Data& capdata = capmoddata->caps.back();
+
+ // Populate list with uuids of users who are using the cap
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator j = list.begin(); j != list.end(); ++j)
{
- std::transform(cap_.begin(), cap_.end(), cap_.begin(), ::tolower);
- Data.wanted.push_back(cap_);
+ LocalUser* user = *j;
+ if (cap->get(user))
+ capdata.users.push_back(user->uuid);
}
+ }
+ }
- reghold.set(user, 1);
- Data.Send();
-
- if (Data.ack.size() > 0)
+ void OnReloadModuleRestore(Module* mod, void* data) CXX11_OVERRIDE
+ {
+ CapModData* capmoddata = static_cast<CapModData*>(data);
+ for (std::vector<CapModData::Data>::const_iterator i = capmoddata->caps.begin(); i != capmoddata->caps.end(); ++i)
+ {
+ const CapModData::Data& capdata = *i;
+ Capability* cap = ManagerImpl::Find(capdata.name);
+ if (!cap)
{
- std::string AckResult = irc::stringjoiner(Data.ack);
- user->WriteCommand("CAP", "ACK :" + AckResult);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cap %s is no longer available after reload", capdata.name.c_str());
+ continue;
}
- if (Data.wanted.size() > 0)
+ // Set back the cap for all users who were using it before the reload
+ for (std::vector<std::string>::const_iterator j = capdata.users.begin(); j != capdata.users.end(); ++j)
{
- std::string NakResult = irc::stringjoiner(Data.wanted);
- user->WriteCommand("CAP", "NAK :" + NakResult);
+ const std::string& uuid = *j;
+ User* user = ServerInstance->FindUUID(uuid);
+ if (!user)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone when trying to restore cap %s", uuid.c_str(), capdata.name.c_str());
+ continue;
+ }
+
+ cap->set(user, true);
}
}
- else if (subcommand == "END")
+ delete capmoddata;
+ }
+
+ public:
+ ManagerImpl(Module* mod, Events::ModuleEventProvider& evprovref)
+ : Cap::Manager(mod)
+ , ReloadModule::EventListener(mod)
+ , capext(mod)
+ , evprov(evprovref)
+ {
+ managerimpl = this;
+ }
+
+ ~ManagerImpl()
+ {
+ for (CapMap::iterator i = caps.begin(); i != caps.end(); ++i)
{
- reghold.set(user, 0);
+ Capability* cap = i->second;
+ cap->Unregister();
}
- else if ((subcommand == "LS") || (subcommand == "LIST"))
+ }
+
+ void AddCap(Cap::Capability* cap) CXX11_OVERRIDE
+ {
+ // No-op if the cap is already registered.
+ // This allows modules to call SetActive() on a cap without checking if it's active first.
+ if (cap->IsRegistered())
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Registering cap %s", cap->GetName().c_str());
+ cap->bit = AllocateBit();
+ cap->extitem = &capext;
+ caps.insert(std::make_pair(cap->GetName(), cap));
+ ServerInstance->Modules.AddReferent("cap/" + cap->GetName(), cap);
+
+ FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapAddDel, (cap, true));
+ }
+
+ void DelCap(Cap::Capability* cap) CXX11_OVERRIDE
+ {
+ // No-op if the cap is not registered, see AddCap() above
+ if (!cap->IsRegistered())
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unregistering cap %s", cap->GetName().c_str());
+
+ // Fire the event first so modules can still see who is using the cap which is being unregistered
+ FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapAddDel, (cap, false));
+
+ // Turn off the cap for all users
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
- CapEvent Data(creator, user, subcommand == "LS" ? CapEvent::CAPEVENT_LS : CapEvent::CAPEVENT_LIST);
+ LocalUser* user = *i;
+ cap->set(user, false);
+ }
+
+ ServerInstance->Modules.DelReferent(cap);
+ cap->Unregister();
+ caps.erase(cap->GetName());
+ }
+
+ Capability* Find(const std::string& capname) const CXX11_OVERRIDE
+ {
+ CapMap::const_iterator it = caps.find(capname);
+ if (it != caps.end())
+ return it->second;
+ return NULL;
+ }
+
+ void NotifyValueChange(Capability* cap) CXX11_OVERRIDE
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cap %s changed value", cap->GetName().c_str());
+ FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapValueChange, (cap));
+ }
- reghold.set(user, 1);
- Data.Send();
+ Protocol GetProtocol(LocalUser* user) const
+ {
+ return ((capext.get(user) & CAP_302_BIT) ? CAP_302 : CAP_LEGACY);
+ }
- std::string Result = irc::stringjoiner(Data.wanted);
- user->WriteCommand("CAP", subcommand + " :" + Result);
+ void Set302Protocol(LocalUser* user)
+ {
+ capext.set(user, capext.get(user) | CAP_302_BIT);
+ }
+
+ bool HandleReq(LocalUser* user, const std::string& reqlist)
+ {
+ Ext usercaps = capext.get(user);
+ irc::spacesepstream ss(reqlist);
+ for (std::string capname; ss.GetToken(capname); )
+ {
+ bool remove = (capname[0] == '-');
+ if (remove)
+ capname.erase(capname.begin());
+
+ Capability* cap = ManagerImpl::Find(capname);
+ if ((!cap) || (!CanRequest(user, usercaps, cap, !remove)))
+ return false;
+
+ if (remove)
+ usercaps = cap->DelFromMask(usercaps);
+ else
+ usercaps = cap->AddToMask(usercaps);
}
- else if (subcommand == "CLEAR")
+
+ capext.set(user, usercaps);
+ return true;
+ }
+
+ void HandleList(std::string& out, LocalUser* user, bool show_all, bool show_values, bool minus_prefix = false) const
+ {
+ Ext show_caps = (show_all ? ~0 : capext.get(user));
+
+ for (CapMap::const_iterator i = caps.begin(); i != caps.end(); ++i)
+ {
+ Capability* cap = i->second;
+ if (!(show_caps & cap->GetMask()))
+ continue;
+
+ if ((show_all) && (!cap->OnList(user)))
+ continue;
+
+ if (minus_prefix)
+ out.push_back('-');
+ out.append(cap->GetName());
+
+ if (show_values)
+ {
+ const std::string* capvalue = cap->GetValue(user);
+ if ((capvalue) && (!capvalue->empty()) && (capvalue->find(' ') == std::string::npos))
+ {
+ out.push_back('=');
+ out.append(*capvalue, 0, MAX_VALUE_LENGTH);
+ }
+ }
+ out.push_back(' ');
+ }
+ }
+
+ void HandleClear(LocalUser* user, std::string& result)
+ {
+ HandleList(result, user, false, false, true);
+ capext.unset(user);
+ }
+};
+
+Cap::ExtItem::ExtItem(Module* mod)
+ : LocalIntExt("caps", ExtensionItem::EXT_USER, mod)
+{
+}
+
+std::string Cap::ExtItem::serialize(SerializeFormat format, const Extensible* container, void* item) const
+{
+ std::string ret;
+ // XXX: Cast away the const because IS_LOCAL() doesn't handle it
+ LocalUser* user = IS_LOCAL(const_cast<User*>(static_cast<const User*>(container)));
+ if ((format == FORMAT_NETWORK) || (!user))
+ return ret;
+
+ // List requested caps
+ managerimpl->HandleList(ret, user, false, false);
+
+ // Serialize cap protocol version. If building a human-readable string append a new token, otherwise append only a single character indicating the version.
+ Protocol protocol = managerimpl->GetProtocol(user);
+ if (format == FORMAT_USER)
+ ret.append("capversion=3.");
+ else if (!ret.empty())
+ ret.erase(ret.length()-1);
+
+ if (protocol == CAP_302)
+ ret.push_back('2');
+ else
+ ret.push_back('1');
+
+ return ret;
+}
+
+void Cap::ExtItem::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+ if (format == FORMAT_NETWORK)
+ return;
+
+ LocalUser* user = IS_LOCAL(static_cast<User*>(container));
+ if (!user)
+ return; // Can't happen
+
+ // Process the cap protocol version which is a single character at the end of the serialized string
+ const char verchar = *value.rbegin();
+ if (verchar == '2')
+ managerimpl->Set302Protocol(user);
+
+ // Remove the version indicator from the string passed to HandleReq
+ std::string caplist(value, 0, value.size()-1);
+ managerimpl->HandleReq(user, caplist);
+}
+
+class CommandCap : public SplitCommand
+{
+ Events::ModuleEventProvider evprov;
+ Cap::ManagerImpl manager;
+
+ static void DisplayResult(LocalUser* user, std::string& result)
+ {
+ if (*result.rbegin() == ' ')
+ result.erase(result.end()-1);
+ user->WriteCommand("CAP", result);
+ }
+
+ public:
+ LocalIntExt holdext;
+
+ CommandCap(Module* mod)
+ : SplitCommand(mod, "CAP", 1)
+ , evprov(mod, "event/cap")
+ , manager(mod, evprov)
+ , holdext("cap_hold", ExtensionItem::EXT_USER, mod)
+ {
+ works_before_reg = true;
+ }
+
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user) CXX11_OVERRIDE
+ {
+ if (user->registered != REG_ALL)
+ holdext.set(user, 1);
+
+ std::string subcommand(parameters[0].length(), ' ');
+ std::transform(parameters[0].begin(), parameters[0].end(), subcommand.begin(), ::toupper);
+
+ if (subcommand == "REQ")
{
- CapEvent Data(creator, user, CapEvent::CAPEVENT_CLEAR);
+ if (parameters.size() < 2)
+ return CMD_FAILURE;
- reghold.set(user, 1);
- Data.Send();
+ std::string result = (manager.HandleReq(user, parameters[1]) ? "ACK :" : "NAK :");
+ result.append(parameters[1]);
+ user->WriteCommand("CAP", result);
+ }
+ else if (subcommand == "END")
+ {
+ holdext.unset(user);
+ }
+ else if ((subcommand == "LS") || (subcommand == "LIST"))
+ {
+ const bool is_ls = (subcommand.length() == 2);
+ if ((is_ls) && (parameters.size() > 1) && (parameters[1] == "302"))
+ manager.Set302Protocol(user);
- std::string Result = irc::stringjoiner(Data.ack);
- user->WriteCommand("CAP", "ACK :" + Result);
+ std::string result = subcommand + " :";
+ // Show values only if supports v3.2 and doing LS
+ manager.HandleList(result, user, is_ls, ((is_ls) && (manager.GetProtocol(user) != Cap::CAP_LEGACY)));
+ DisplayResult(user, result);
+ }
+ else if ((subcommand == "CLEAR") && (manager.GetProtocol(user) == Cap::CAP_LEGACY))
+ {
+ std::string result = "ACK :";
+ manager.HandleClear(user, result);
+ DisplayResult(user, result);
}
else
{
- user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, "%s :Invalid CAP subcommand", subcommand.c_str());
+ user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, subcommand, "Invalid CAP subcommand");
return CMD_FAILURE;
}
@@ -118,28 +407,25 @@ class CommandCAP : public Command
}
};
-class ModuleCAP : public Module
+class ModuleCap : public Module
{
- CommandCAP cmd;
+ CommandCap cmd;
+
public:
- ModuleCAP()
+ ModuleCap()
: cmd(this)
{
}
ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
- /* Users in CAP state get held until CAP END */
- if (cmd.reghold.get(user))
- return MOD_RES_DENY;
-
- return MOD_RES_PASSTHRU;
+ return (cmd.holdext.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU);
}
Version GetVersion() CXX11_OVERRIDE
{
- return Version("Client CAP extension support", VF_VENDOR);
+ return Version("Provides support for CAP capability negotiation", VF_VENDOR);
}
};
-MODULE_INIT(ModuleCAP)
+MODULE_INIT(ModuleCap)
diff --git a/src/modules/m_cban.cpp b/src/modules/m_cban.cpp
index 4fb0653a9..42cff2850 100644
--- a/src/modules/m_cban.cpp
+++ b/src/modules/m_cban.cpp
@@ -28,15 +28,13 @@
class CBan : public XLine
{
private:
- std::string displaytext;
- irc::string matchtext;
+ std::string matchtext;
public:
CBan(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& ch)
: XLine(s_time, d, src, re, "CBAN")
+ , matchtext(ch)
{
- this->displaytext = ch;
- this->matchtext = ch.c_str();
}
// XXX I shouldn't have to define this
@@ -47,14 +45,12 @@ public:
bool Matches(const std::string &s)
{
- if (matchtext == s)
- return true;
- return false;
+ return irc::equals(matchtext, s);
}
const std::string& Displayable()
{
- return displaytext;
+ return matchtext;
}
};
@@ -165,12 +161,12 @@ class ModuleCBan : public Module
ServerInstance->XLines->UnregisterFactory(&f);
}
- ModResult OnStats(char symbol, User* user, string_list &out) CXX11_OVERRIDE
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'C')
+ if (stats.GetSymbol() != 'C')
return MOD_RES_PASSTHRU;
- ServerInstance->XLines->InvokeStats("CBAN", 210, user, out);
+ ServerInstance->XLines->InvokeStats("CBAN", 210, stats);
return MOD_RES_DENY;
}
@@ -181,7 +177,7 @@ class ModuleCBan : public Module
if (rl)
{
// Channel is banned.
- user->WriteNumeric(384, "%s :Cannot join channel, CBANed (%s)", cname.c_str(), rl->reason.c_str());
+ user->WriteNumeric(384, cname, InspIRCd::Format("Cannot join channel, CBANed (%s)", rl->reason.c_str()));
ServerInstance->SNO->WriteGlobalSno('a', "%s tried to join %s which is CBANed (%s)",
user->nick.c_str(), cname.c_str(), rl->reason.c_str());
return MOD_RES_DENY;
diff --git a/src/modules/m_censor.cpp b/src/modules/m_censor.cpp
index 209d61d4a..d2a60275a 100644
--- a/src/modules/m_censor.cpp
+++ b/src/modules/m_censor.cpp
@@ -22,7 +22,7 @@
#include "inspircd.h"
-typedef std::map<irc::string,irc::string> censor_t;
+typedef insp::flat_map<irc::string, irc::string> censor_t;
/** Handles usermode +G
*/
@@ -79,11 +79,11 @@ class ModuleCensor : public Module
{
if (index->second.empty())
{
- user->WriteNumeric(ERR_WORDFILTERED, "%s %s :Your message contained a censored word, and was blocked", ((Channel*)dest)->name.c_str(), index->first.c_str());
+ user->WriteNumeric(ERR_WORDFILTERED, ((target_type == TYPE_CHANNEL) ? ((Channel*)dest)->name : ((User*)dest)->nick), index->first.c_str(), "Your message contained a censored word, and was blocked");
return MOD_RES_DENY;
}
- SearchAndReplace(text2, index->first, index->second);
+ stdalgo::string::replace_all(text2, index->first, index->second);
}
}
text = text2.c_str();
diff --git a/src/modules/m_cgiirc.cpp b/src/modules/m_cgiirc.cpp
index 23dc90ef8..5eba5ce35 100644
--- a/src/modules/m_cgiirc.cpp
+++ b/src/modules/m_cgiirc.cpp
@@ -74,8 +74,10 @@ class CommandWebirc : public Command
CGIHostlist Hosts;
CommandWebirc(Module* Creator)
: Command(Creator, "WEBIRC", 4),
- realhost("cgiirc_realhost", Creator), realip("cgiirc_realip", Creator)
+ realhost("cgiirc_realhost", ExtensionItem::EXT_USER, Creator)
+ , realip("cgiirc_realip", ExtensionItem::EXT_USER, Creator)
{
+ allow_empty_last_param = false;
works_before_reg = true;
this->syntax = "password client hostname ip";
}
@@ -84,6 +86,14 @@ class CommandWebirc : public Command
if(user->registered == REG_ALL)
return CMD_FAILURE;
+ irc::sockets::sockaddrs ipaddr;
+ if (!irc::sockets::aptosa(parameters[3], 0, ipaddr))
+ {
+ IS_LOCAL(user)->CommandFloodPenalty += 5000;
+ ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s tried to use WEBIRC but gave an invalid IP address.", user->GetFullRealHost().c_str());
+ return CMD_FAILURE;
+ }
+
for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
{
if(InspIRCd::Match(user->host, iter->hostmask, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(user->GetIPString(), iter->hostmask, ascii_case_insensitive_map))
@@ -104,12 +114,14 @@ class CommandWebirc : public Command
ChangeIP(user, parameters[3]);
// And follow this up by changing their host
user->host = user->dhost = newhost;
+ user->InvalidateCache();
return CMD_SUCCESS;
}
}
}
+ IS_LOCAL(user)->CommandFloodPenalty += 5000;
ServerInstance->SNO->WriteGlobalSno('w', "Connecting user %s tried to use WEBIRC, but didn't match any configured webirc blocks.", user->GetFullRealHost().c_str());
return CMD_FAILURE;
}
@@ -224,7 +236,7 @@ class ModuleCgiIRC : public Module
public:
ModuleCgiIRC()
: cmd(this)
- , waiting("cgiirc-delay", this)
+ , waiting("cgiirc-delay", ExtensionItem::EXT_USER, this)
, DNS(this, "DNS")
{
}
@@ -253,7 +265,7 @@ public:
{
if (type == "webirc" && password.empty())
{
- ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Missing password in config: %s", hostmask.c_str());
}
else
{
@@ -269,7 +281,7 @@ public:
else
{
cgitype = PASS;
- ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Invalid <cgihost:type> value in config: %s, setting it to \"pass\"", type.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Invalid <cgihost:type> value in config: %s, setting it to \"pass\"", type.c_str());
}
cmd.Hosts.push_back(CGIhost(hostmask, cgitype, password));
@@ -277,7 +289,7 @@ public:
}
else
{
- ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
continue;
}
}
diff --git a/src/modules/m_chanfilter.cpp b/src/modules/m_chanfilter.cpp
index 53428a5a8..a7bc21557 100644
--- a/src/modules/m_chanfilter.cpp
+++ b/src/modules/m_chanfilter.cpp
@@ -37,7 +37,7 @@ class ChanFilter : public ListModeBase
{
if (word.length() > 35)
{
- user->WriteNumeric(935, "%s %s :word is too long for censor list", chan->name.c_str(), word.c_str());
+ user->WriteNumeric(935, chan->name, word, "%word is too long for censor list");
return false;
}
@@ -46,17 +46,17 @@ class ChanFilter : public ListModeBase
void TellListTooLong(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(939, "%s %s :Channel spamfilter list is full", chan->name.c_str(), word.c_str());
+ user->WriteNumeric(939, chan->name, word, "Channel spamfilter list is full");
}
void TellAlreadyOnList(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(937, "%s :The word %s is already on the spamfilter list", chan->name.c_str(), word.c_str());
+ user->WriteNumeric(937, chan->name, InspIRCd::Format("The word %s is already on the spamfilter list", word.c_str()));
}
void TellNotSet(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(938, "%s :No such spamfilter word is set", chan->name.c_str());
+ user->WriteNumeric(938, chan->name, "No such spamfilter word is set");
}
};
@@ -98,9 +98,9 @@ class ModuleChanFilter : public Module
if (InspIRCd::Match(text, i->mask))
{
if (hidemask)
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (your message contained a censored word)", chan->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (your message contained a censored word)");
else
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s %s :Cannot send to channel (your message contained a censored word)", chan->name.c_str(), i->mask.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, i->mask, "Cannot send to channel (your message contained a censored word)");
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_chanhistory.cpp b/src/modules/m_chanhistory.cpp
index f6e7ea40e..a0929a0d0 100644
--- a/src/modules/m_chanhistory.cpp
+++ b/src/modules/m_chanhistory.cpp
@@ -65,7 +65,7 @@ class HistoryMode : public ParamMode<HistoryMode, SimpleExtItem<HistoryList> >
if (colon == std::string::npos)
return MODEACTION_DENY;
- std::string duration = parameter.substr(colon+1);
+ std::string duration(parameter, colon+1);
if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!IsValidDuration(duration))))
return MODEACTION_DENY;
diff --git a/src/modules/m_chanlog.cpp b/src/modules/m_chanlog.cpp
index 736285be8..f618a539c 100644
--- a/src/modules/m_chanlog.cpp
+++ b/src/modules/m_chanlog.cpp
@@ -25,7 +25,7 @@ class ModuleChanLog : public Module
/*
* Multimap so people can redirect a snomask to multiple channels.
*/
- typedef std::multimap<char, std::string> ChanLogTargets;
+ typedef insp::flat_multimap<char, std::string> ChanLogTargets;
ChanLogTargets logstreams;
public:
@@ -44,7 +44,7 @@ class ModuleChanLog : public Module
if (channel.empty() || snomasks.empty())
{
- ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Malformed chanlog tag, ignoring");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Malformed chanlog tag, ignoring");
continue;
}
diff --git a/src/modules/m_channames.cpp b/src/modules/m_channames.cpp
index 5a38fbbc2..7513cb33a 100644
--- a/src/modules/m_channames.cpp
+++ b/src/modules/m_channames.cpp
@@ -64,6 +64,8 @@ class ModuleChannelNames : public Module
void ValidateChans()
{
+ Modes::ChangeList removepermchan;
+
badchan = true;
const chan_hash& chans = ServerInstance->GetChans();
for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); )
@@ -76,20 +78,19 @@ class ModuleChannelNames : public Module
if (c->IsModeSet(permchannelmode) && c->GetUserCounter())
{
- std::vector<std::string> modes;
- modes.push_back(c->name);
- modes.push_back(std::string("-") + permchannelmode->GetModeChar());
-
- ServerInstance->Modes->Process(modes, ServerInstance->FakeClient);
+ removepermchan.clear();
+ removepermchan.push_remove(*permchannelmode);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, c, NULL, removepermchan);
}
- const UserMembList* users = c->GetUsers();
- for(UserMembCIter j = users->begin(); j != users->end(); )
+
+ Channel::MemberMap& users = c->userlist;
+ for (Channel::MemberMap::iterator j = users.begin(); j != users.end(); )
{
if (IS_LOCAL(j->first))
{
// KickUser invalidates the iterator
- UserMembCIter it = j++;
- c->KickUser(ServerInstance->FakeClient, it->first, "Channel name no longer valid");
+ Channel::MemberMap::iterator it = j++;
+ c->KickUser(ServerInstance->FakeClient, it, "Channel name no longer valid");
}
else
++j;
@@ -132,8 +133,8 @@ class ModuleChannelNames : public Module
{
if (badchan)
{
- const UserMembList* users = memb->chan->GetUsers();
- for(UserMembCIter i = users->begin(); i != users->end(); i++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
if (i->first != memb->user)
except_list.insert(i->first);
}
diff --git a/src/modules/m_channelban.cpp b/src/modules/m_channelban.cpp
index 300caa123..ffb43eef1 100644
--- a/src/modules/m_channelban.cpp
+++ b/src/modules/m_channelban.cpp
@@ -32,19 +32,19 @@ class ModuleBadChannelExtban : public Module
{
if ((mask.length() > 2) && (mask[0] == 'j') && (mask[1] == ':'))
{
- std::string rm = mask.substr(2);
+ std::string rm(mask, 2);
char status = 0;
- ModeHandler* mh = ServerInstance->Modes->FindPrefix(rm[0]);
+ const PrefixMode* const mh = ServerInstance->Modes->FindPrefix(rm[0]);
if (mh)
{
- rm = mask.substr(3);
+ rm.assign(mask, 3, std::string::npos);
status = mh->GetModeChar();
}
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); i++)
{
if (InspIRCd::Match((*i)->chan->name, rm))
{
- if (!status || (*i)->hasMode(status))
+ if ((!status) || ((*i)->HasMode(mh)))
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_check.cpp b/src/modules/m_check.cpp
index 35901f8d5..2cb45ad43 100644
--- a/src/modules/m_check.cpp
+++ b/src/modules/m_check.cpp
@@ -23,6 +23,75 @@
#include "inspircd.h"
#include "listmode.h"
+enum
+{
+ RPL_CHECK = 802
+};
+
+class CheckContext
+{
+ User* const user;
+ const std::string& target;
+
+ public:
+ CheckContext(User* u, const std::string& targetstr)
+ : user(u)
+ , target(targetstr)
+ {
+ Write("START", target);
+ }
+
+ ~CheckContext()
+ {
+ Write("END", target);
+ }
+
+ void Write(const std::string& type, const std::string& text)
+ {
+ user->WriteRemoteNumeric(RPL_CHECK, type, text);
+ }
+
+ User* GetUser() const { return user; }
+
+ void DumpListMode(const ListModeBase::ModeList* list)
+ {
+ if (!list)
+ return;
+
+ CheckContext::List modelist(*this, "modelist");
+ for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+ modelist.Add(i->mask);
+
+ modelist.Flush();
+ }
+
+ void DumpExt(Extensible* ext)
+ {
+ CheckContext::List extlist(*this, "metadata");
+ for(Extensible::ExtensibleStore::const_iterator i = ext->GetExtList().begin(); i != ext->GetExtList().end(); ++i)
+ {
+ ExtensionItem* item = i->first;
+ std::string value = item->serialize(FORMAT_USER, ext, i->second);
+ if (!value.empty())
+ Write("meta:" + item->name, value);
+ else if (!item->name.empty())
+ extlist.Add(item->name);
+ }
+
+ extlist.Flush();
+ }
+
+ class List : public Numeric::GenericBuilder<' ', false, Numeric::WriteRemoteNumericSink>
+ {
+ public:
+ List(CheckContext& context, const char* checktype)
+ : Numeric::GenericBuilder<' ', false, Numeric::WriteRemoteNumericSink>(Numeric::WriteRemoteNumericSink(context.GetUser()), RPL_CHECK, false, (IS_LOCAL(context.GetUser()) ? context.GetUser()->nick.length() : ServerInstance->Config->Limits.NickMax) + strlen(checktype) + 1)
+ {
+ GetNumeric().push(checktype).push(std::string());
+ }
+ };
+};
+
/** Handle /CHECK
*/
class CommandCheck : public Command
@@ -40,25 +109,17 @@ class CommandCheck : public Command
return ret;
}
- static void dumpListMode(User* user, const std::string& checkstr, const ListModeBase::ModeList* list)
+ static std::string GetAllowedOperOnlyModes(LocalUser* user, ModeType modetype)
{
- if (!list)
- return;
-
- std::string buf = checkstr + " modelist";
- const std::string::size_type headlen = buf.length();
- const size_t maxline = ServerInstance->Config->Limits.MaxLine;
- for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+ std::string ret;
+ const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes.GetModes(modetype);
+ for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); ++i)
{
- if (buf.size() + i->mask.size() + 1 > maxline)
- {
- user->SendText(buf);
- buf.erase(headlen);
- }
- buf.append(" ").append(i->mask);
+ const ModeHandler* const mh = i->second;
+ if ((mh->NeedsOper()) && (user->HasModePermission(mh)))
+ ret.push_back(mh->GetModeChar());
}
- if (buf.length() > headlen)
- user->SendText(buf);
+ return ret;
}
public:
@@ -73,177 +134,145 @@ class CommandCheck : public Command
{
char timebuf[60];
struct tm *mytime = gmtime(&time);
- strftime(timebuf, 59, "%Y-%m-%d %H:%M:%S UTC (%s)", mytime);
- return std::string(timebuf);
- }
-
- void dumpExt(User* user, const std::string& checkstr, Extensible* ext)
- {
- std::stringstream dumpkeys;
- for(Extensible::ExtensibleStore::const_iterator i = ext->GetExtList().begin(); i != ext->GetExtList().end(); i++)
- {
- ExtensionItem* item = i->first;
- std::string value = item->serialize(FORMAT_USER, ext, i->second);
- if (!value.empty())
- user->SendText(checkstr + " meta:" + item->name + " " + value);
- else if (!item->name.empty())
- dumpkeys << " " << item->name;
- }
- if (!dumpkeys.str().empty())
- user->SendText(checkstr + " metadata", dumpkeys);
+ strftime(timebuf, 59, "%Y-%m-%d %H:%M:%S UTC (", mytime);
+ std::string ret(timebuf);
+ ret.append(ConvToStr(time)).push_back(')');
+ return ret;
}
CmdResult Handle (const std::vector<std::string> &parameters, User *user)
{
- if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName.c_str())
+ if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
return CMD_SUCCESS;
User *targuser;
Channel *targchan;
- std::string checkstr;
std::string chliststr;
- checkstr = ":" + ServerInstance->Config->ServerName + " 304 " + user->nick + " :CHECK";
-
targuser = ServerInstance->FindNick(parameters[0]);
targchan = ServerInstance->FindChan(parameters[0]);
/*
* Syntax of a /check reply:
- * :server.name 304 target :CHECK START <target>
- * :server.name 304 target :CHECK <field> <value>
- * :server.name 304 target :CHECK END
+ * :server.name 802 target START <target>
+ * :server.name 802 target <field> :<value>
+ * :server.name 802 target END <target>
*/
- user->SendText(checkstr + " START " + parameters[0]);
+ // Constructor sends START, destructor sends END
+ CheckContext context(user, parameters[0]);
if (targuser)
{
LocalUser* loctarg = IS_LOCAL(targuser);
/* /check on a user */
- user->SendText(checkstr + " nuh " + targuser->GetFullHost());
- user->SendText(checkstr + " realnuh " + targuser->GetFullRealHost());
- user->SendText(checkstr + " realname " + targuser->fullname);
- user->SendText(checkstr + " modes +" + targuser->FormatModes());
- user->SendText(checkstr + " snomasks " + GetSnomasks(targuser));
- user->SendText(checkstr + " server " + targuser->server->GetName());
- user->SendText(checkstr + " uid " + targuser->uuid);
- user->SendText(checkstr + " signon " + timestring(targuser->signon));
- user->SendText(checkstr + " nickts " + timestring(targuser->age));
+ context.Write("nuh", targuser->GetFullHost());
+ context.Write("realnuh", targuser->GetFullRealHost());
+ context.Write("realname", targuser->fullname);
+ context.Write("modes", targuser->GetModeLetters());
+ context.Write("snomasks", GetSnomasks(targuser));
+ context.Write("server", targuser->server->GetName());
+ context.Write("uid", targuser->uuid);
+ context.Write("signon", timestring(targuser->signon));
+ context.Write("nickts", timestring(targuser->age));
if (loctarg)
- user->SendText(checkstr + " lastmsg " + timestring(loctarg->idle_lastmsg));
+ context.Write("lastmsg", timestring(loctarg->idle_lastmsg));
if (targuser->IsAway())
{
/* user is away */
- user->SendText(checkstr + " awaytime " + timestring(targuser->awaytime));
- user->SendText(checkstr + " awaymsg " + targuser->awaymsg);
+ context.Write("awaytime", timestring(targuser->awaytime));
+ context.Write("awaymsg", targuser->awaymsg);
}
if (targuser->IsOper())
{
OperInfo* oper = targuser->oper;
/* user is an oper of type ____ */
- user->SendText(checkstr + " opertype " + oper->name);
+ context.Write("opertype", oper->name);
if (loctarg)
{
- std::string umodes;
- std::string cmodes;
- for(char c='A'; c < 'z'; c++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
- if (mh && mh->NeedsOper() && loctarg->HasModePermission(c, MODETYPE_USER))
- umodes.push_back(c);
- mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
- if (mh && mh->NeedsOper() && loctarg->HasModePermission(c, MODETYPE_CHANNEL))
- cmodes.push_back(c);
- }
- user->SendText(checkstr + " modeperms user=" + umodes + " channel=" + cmodes);
- std::string opcmds;
- for(std::set<std::string>::iterator i = oper->AllowedOperCommands.begin(); i != oper->AllowedOperCommands.end(); i++)
- {
- opcmds.push_back(' ');
- opcmds.append(*i);
- }
- std::stringstream opcmddump(opcmds);
- user->SendText(checkstr + " commandperms", opcmddump);
- std::string privs;
- for(std::set<std::string>::iterator i = oper->AllowedPrivs.begin(); i != oper->AllowedPrivs.end(); i++)
- {
- privs.push_back(' ');
- privs.append(*i);
- }
- std::stringstream privdump(privs);
- user->SendText(checkstr + " permissions", privdump);
+ std::string umodes = GetAllowedOperOnlyModes(loctarg, MODETYPE_USER);
+ std::string cmodes = GetAllowedOperOnlyModes(loctarg, MODETYPE_CHANNEL);
+ context.Write("modeperms", "user=" + umodes + " channel=" + cmodes);
+
+ CheckContext::List opcmdlist(context, "commandperms");
+ for (OperInfo::PrivSet::const_iterator i = oper->AllowedOperCommands.begin(); i != oper->AllowedOperCommands.end(); ++i)
+ opcmdlist.Add(*i);
+ opcmdlist.Flush();
+ CheckContext::List privlist(context, "permissions");
+ for (OperInfo::PrivSet::const_iterator i = oper->AllowedPrivs.begin(); i != oper->AllowedPrivs.end(); ++i)
+ privlist.Add(*i);
+ privlist.Flush();
}
}
if (loctarg)
{
- user->SendText(checkstr + " clientaddr " + loctarg->client_sa.str());
- user->SendText(checkstr + " serveraddr " + loctarg->server_sa.str());
+ context.Write("clientaddr", loctarg->client_sa.str());
+ context.Write("serveraddr", loctarg->server_sa.str());
std::string classname = loctarg->GetClass()->name;
if (!classname.empty())
- user->SendText(checkstr + " connectclass " + classname);
+ context.Write("connectclass", classname);
}
else
- user->SendText(checkstr + " onip " + targuser->GetIPString());
+ context.Write("onip", targuser->GetIPString());
- for (UCListIter i = targuser->chans.begin(); i != targuser->chans.end(); i++)
+ CheckContext::List chanlist(context, "onchans");
+ for (User::ChanList::iterator i = targuser->chans.begin(); i != targuser->chans.end(); i++)
{
Membership* memb = *i;
Channel* c = memb->chan;
char prefix = memb->GetPrefixChar();
if (prefix)
chliststr.push_back(prefix);
- chliststr.append(c->name).push_back(' ');
+ chliststr.append(c->name);
+ chanlist.Add(chliststr);
+ chliststr.clear();
}
- std::stringstream dump(chliststr);
-
- user->SendText(checkstr + " onchans", dump);
+ chanlist.Flush();
- dumpExt(user, checkstr, targuser);
+ context.DumpExt(targuser);
}
else if (targchan)
{
/* /check on a channel */
- user->SendText(checkstr + " timestamp " + timestring(targchan->age));
+ context.Write("timestamp", timestring(targchan->age));
- if (targchan->topic[0] != 0)
+ if (!targchan->topic.empty())
{
/* there is a topic, assume topic related information exists */
- user->SendText(checkstr + " topic " + targchan->topic);
- user->SendText(checkstr + " topic_setby " + targchan->setby);
- user->SendText(checkstr + " topic_setat " + timestring(targchan->topicset));
+ context.Write("topic", targchan->topic);
+ context.Write("topic_setby", targchan->setby);
+ context.Write("topic_setat", timestring(targchan->topicset));
}
- user->SendText(checkstr + " modes " + targchan->ChanModes(true));
- user->SendText(checkstr + " membercount " + ConvToStr(targchan->GetUserCounter()));
+ context.Write("modes", targchan->ChanModes(true));
+ context.Write("membercount", ConvToStr(targchan->GetUserCounter()));
/* now the ugly bit, spool current members of a channel. :| */
- const UserMembList *ulist= targchan->GetUsers();
+ const Channel::MemberMap& ulist = targchan->GetUsers();
/* note that unlike /names, we do NOT check +i vs in the channel */
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
/*
* Unlike Asuka, I define a clone as coming from the same host. --w00t
*/
const UserManager::CloneCounts& clonecount = ServerInstance->Users->GetCloneCounts(i->first);
- user->SendText("%s member %-3u %s%s (%s@%s) %s ",
- checkstr.c_str(), clonecount.global,
- i->second->GetAllPrefixChars(), i->first->nick.c_str(),
- i->first->ident.c_str(), i->first->dhost.c_str(), i->first->fullname.c_str());
+ context.Write("member", InspIRCd::Format("%-3u %s%s (%s@%s) %s ", clonecount.global,
+ i->second->GetAllPrefixChars().c_str(), i->first->nick.c_str(),
+ i->first->ident.c_str(), i->first->dhost.c_str(), i->first->fullname.c_str()));
}
const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
- dumpListMode(user, checkstr, (*i)->GetList(targchan));
+ context.DumpListMode((*i)->GetList(targchan));
- dumpExt(user, checkstr, targchan);
+ context.DumpExt(targchan);
}
else
{
@@ -257,27 +286,26 @@ class CommandCheck : public Command
if (InspIRCd::Match(a->second->host, parameters[0], ascii_case_insensitive_map) || InspIRCd::Match(a->second->dhost, parameters[0], ascii_case_insensitive_map))
{
/* host or vhost matches mask */
- user->SendText(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->fullname);
+ context.Write("match", ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->fullname);
}
/* IP address */
else if (InspIRCd::MatchCIDR(a->second->GetIPString(), parameters[0]))
{
/* same IP. */
- user->SendText(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->fullname);
+ context.Write("match", ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->fullname);
}
}
- user->SendText(checkstr + " matches " + ConvToStr(x));
+ context.Write("matches", ConvToStr(x));
}
- user->SendText(checkstr + " END " + parameters[0]);
-
+ // END is sent by the CheckContext destructor
return CMD_SUCCESS;
}
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- if (parameters.size() > 1)
+ if ((parameters.size() > 1) && (parameters[1].find('.') != std::string::npos))
return ROUTE_OPT_UCAST(parameters[1]);
return ROUTE_LOCALONLY;
}
diff --git a/src/modules/m_chghost.cpp b/src/modules/m_chghost.cpp
index 3a637f9d0..60146c78b 100644
--- a/src/modules/m_chghost.cpp
+++ b/src/modules/m_chghost.cpp
@@ -56,9 +56,10 @@ class CommandChghost : public Command
User* dest = ServerInstance->FindNick(parameters[0]);
- if ((!dest) || (dest->registered != REG_ALL))
+ // Allow services to change the host of unregistered users
+ if ((!dest) || ((dest->registered != REG_ALL) && (!user->server->IsULine())))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
@@ -76,10 +77,7 @@ class CommandChghost : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
diff --git a/src/modules/m_chgident.cpp b/src/modules/m_chgident.cpp
index c855216bf..8ba5b4a5b 100644
--- a/src/modules/m_chgident.cpp
+++ b/src/modules/m_chgident.cpp
@@ -41,7 +41,7 @@ class CommandChgident : public Command
if ((!dest) || (dest->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
@@ -70,10 +70,7 @@ class CommandChgident : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
diff --git a/src/modules/m_chgname.cpp b/src/modules/m_chgname.cpp
index 830d5070b..2582ef652 100644
--- a/src/modules/m_chgname.cpp
+++ b/src/modules/m_chgname.cpp
@@ -39,7 +39,7 @@ class CommandChgname : public Command
if ((!dest) || (dest->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
@@ -66,10 +66,7 @@ class CommandChgname : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
diff --git a/src/modules/m_classban.cpp b/src/modules/m_classban.cpp
new file mode 100644
index 000000000..066834079
--- /dev/null
+++ b/src/modules/m_classban.cpp
@@ -0,0 +1,47 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Johanna Abrahamsson <johanna-a@mjao.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class ModuleClassBan : public Module
+{
+ public:
+ ModResult OnCheckBan(User* user, Channel* c, const std::string& mask) CXX11_OVERRIDE
+ {
+ LocalUser* localUser = IS_LOCAL(user);
+ if ((localUser) && (mask.length() > 2) && (mask[0] == 'n') && (mask[1] == ':'))
+ {
+ if (InspIRCd::Match(localUser->GetClass()->name, mask.substr(2)))
+ return MOD_RES_DENY;
+ }
+ return MOD_RES_PASSTHRU;
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ {
+ tokens["EXTBAN"].push_back('n');
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Class 'n' - Connection class ban", VF_VENDOR | VF_OPTCOMMON);
+ }
+};
+
+MODULE_INIT(ModuleClassBan)
diff --git a/src/modules/m_clearchan.cpp b/src/modules/m_clearchan.cpp
index 27f8ec32f..4142f81d1 100644
--- a/src/modules/m_clearchan.cpp
+++ b/src/modules/m_clearchan.cpp
@@ -93,10 +93,11 @@ class CommandClearChan : public Command
std::string mask;
// Now remove all local non-opers from the channel
- const UserMembList* users = chan->GetUsers();
- for (UserMembCIter i = users->begin(); i != users->end(); )
+ Channel::MemberMap& users = chan->userlist;
+ for (Channel::MemberMap::iterator i = users.begin(); i != users.end(); )
{
User* curr = i->first;
+ const Channel::MemberMap::iterator currit = i;
++i;
if (!IS_LOCAL(curr) || curr->IsOper())
@@ -105,7 +106,7 @@ class CommandClearChan : public Command
// If kicking users, remove them and skip the QuitUser()
if (kick)
{
- chan->KickUser(ServerInstance->FakeClient, curr, reason);
+ chan->KickUser(ServerInstance->FakeClient, currit, reason);
continue;
}
@@ -118,7 +119,7 @@ class CommandClearChan : public Command
mask = ((method[0] == 'Z') ? curr->GetIPString() : "*@" + curr->host);
xline = xlf->Generate(ServerInstance->Time(), 60*60, user->nick, reason, mask);
}
- catch (ModuleException& ex)
+ catch (ModuleException&)
{
// Nothing, move on to the next user
continue;
@@ -169,8 +170,8 @@ class ModuleClearChan : public Module
}
}
- const UserMembList* users = cmd.activechan->GetUsers();
- for (UserMembCIter i = users->begin(); i != users->end(); ++i)
+ const Channel::MemberMap& users = cmd.activechan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
{
LocalUser* curr = IS_LOCAL(i->first);
if (!curr)
@@ -199,8 +200,8 @@ class ModuleClearChan : public Module
{
// Hide the KICK from all non-opers
User* leaving = memb->user;
- const UserMembList* users = memb->chan->GetUsers();
- for (UserMembCIter i = users->begin(); i != users->end(); ++i)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
{
User* curr = i->first;
if ((IS_LOCAL(curr)) && (!curr->IsOper()) && (curr != leaving))
diff --git a/src/modules/m_cloaking.cpp b/src/modules/m_cloaking.cpp
index 5d62c9cf6..5cedb5774 100644
--- a/src/modules/m_cloaking.cpp
+++ b/src/modules/m_cloaking.cpp
@@ -49,7 +49,7 @@ class CloakUser : public ModeHandler
CloakUser(Module* source)
: ModeHandler(source, "cloak", 'x', PARAM_NONE, MODETYPE_USER),
- ext("cloaked_host", source), debounce_ts(0), debounce_count(0)
+ ext("cloaked_host", ExtensionItem::EXT_USER, source), debounce_ts(0), debounce_count(0)
{
}
@@ -89,6 +89,10 @@ class CloakUser : public ModeHandler
if (adding)
{
+ // assume this is more correct
+ if (user->registered != REG_ALL && user->host != user->dhost)
+ return MODEACTION_DENY;
+
std::string* cloak = ext.get(user);
if (!cloak)
@@ -192,7 +196,7 @@ class ModuleCloaking : public Module
input.append(1, '\0'); // null does not terminate a C++ string
input.append(item);
- std::string rv = Hash->sum(input).substr(0,len);
+ std::string rv = Hash->GenerateRaw(input).substr(0,len);
for(int i=0; i < len; i++)
{
// this discards 3 bits per byte. We have an
@@ -253,19 +257,17 @@ class ModuleCloaking : public Module
}
else
{
- char buf[50];
if (ip.sa.sa_family == AF_INET6)
{
- snprintf(buf, 50, ".%02x%02x.%02x%02x%s",
+ rv.append(InspIRCd::Format(".%02x%02x.%02x%02x%s",
ip.in6.sin6_addr.s6_addr[2], ip.in6.sin6_addr.s6_addr[3],
- ip.in6.sin6_addr.s6_addr[0], ip.in6.sin6_addr.s6_addr[1], suffix.c_str());
+ ip.in6.sin6_addr.s6_addr[0], ip.in6.sin6_addr.s6_addr[1], suffix.c_str()));
}
else
{
const unsigned char* ip4 = (const unsigned char*)&ip.in4.sin_addr;
- snprintf(buf, 50, ".%d.%d%s", ip4[1], ip4[0], suffix.c_str());
+ rv.append(InspIRCd::Format(".%d.%d%s", ip4[1], ip4[0], suffix.c_str()));
}
- rv.append(buf);
}
return rv;
}
@@ -345,11 +347,14 @@ class ModuleCloaking : public Module
{
std::string chost;
+ irc::sockets::sockaddrs hostip;
+ bool host_is_ip = irc::sockets::aptosa(host, ip.port(), hostip) && hostip == ip;
+
switch (mode)
{
case MODE_HALF_CLOAK:
{
- if (ipstr != host)
+ if (!host_is_ip)
chost = prefix + SegmentCloak(host, 1, 6) + LastTwoDomainParts(host);
if (chost.empty() || chost.length() > 50)
chost = SegmentIP(ip, false);
diff --git a/src/modules/m_clones.cpp b/src/modules/m_clones.cpp
index c51c8d3b4..b3e695bfd 100644
--- a/src/modules/m_clones.cpp
+++ b/src/modules/m_clones.cpp
@@ -34,7 +34,7 @@ class CommandClones : public Command
CmdResult Handle (const std::vector<std::string> &parameters, User *user)
{
- std::string clonesstr = "304 " + user->nick + " :CLONES";
+ std::string clonesstr = "CLONES ";
unsigned long limit = atoi(parameters[0].c_str());
@@ -45,7 +45,7 @@ class CommandClones : public Command
* :server.name 304 target :CLONES END
*/
- user->WriteServ(clonesstr + " START");
+ user->WriteNumeric(304, clonesstr + "START");
/* hostname or other */
const UserManager::CloneMap& clonemap = ServerInstance->Users->GetCloneMap();
@@ -53,10 +53,10 @@ class CommandClones : public Command
{
const UserManager::CloneCounts& counts = i->second;
if (counts.global >= limit)
- user->WriteServ(clonesstr + " " + ConvToStr(counts.global) + " " + i->first.str());
+ user->WriteNumeric(304, clonesstr + ConvToStr(counts.global) + " " + i->first.str());
}
- user->WriteServ(clonesstr + " END");
+ user->WriteNumeric(304, clonesstr + "END");
return CMD_SUCCESS;
}
diff --git a/src/modules/m_close.cpp b/src/modules/m_close.cpp
index 9c5c9a77b..3f0eedaaf 100644
--- a/src/modules/m_close.cpp
+++ b/src/modules/m_close.cpp
@@ -35,9 +35,12 @@ class CommandClose : public Command
{
std::map<std::string,int> closed;
- for (LocalUserList::const_iterator u = ServerInstance->Users->local_users.begin(); u != ServerInstance->Users->local_users.end(); ++u)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u = list.begin(); u != list.end(); )
{
+ // Quitting the user removes it from the list
LocalUser* user = *u;
+ ++u;
if (user->registered != REG_ALL)
{
ServerInstance->Users->QuitUser(user, "Closing all unknown connections per request");
diff --git a/src/modules/m_commonchans.cpp b/src/modules/m_commonchans.cpp
index eab53b9bc..e04217e71 100644
--- a/src/modules/m_commonchans.cpp
+++ b/src/modules/m_commonchans.cpp
@@ -47,7 +47,7 @@ class ModulePrivacyMode : public Module
User* t = (User*)dest;
if (!user->IsOper() && (t->IsModeSet(pm)) && (!user->server->IsULine()) && !user->SharesChannelWith(t))
{
- user->WriteNumeric(ERR_CANTSENDTOUSER, "%s :You are not permitted to send private messages to this user (+c set)", t->nick.c_str());
+ user->WriteNumeric(ERR_CANTSENDTOUSER, t->nick, "You are not permitted to send private messages to this user (+c set)");
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_conn_join.cpp b/src/modules/m_conn_join.cpp
index 631e5945c..b22dbdf4d 100644
--- a/src/modules/m_conn_join.cpp
+++ b/src/modules/m_conn_join.cpp
@@ -43,7 +43,7 @@ class JoinTimer : public Timer
public:
JoinTimer(LocalUser* u, SimpleExtItem<JoinTimer>& ex, const std::string& chans, unsigned int delay)
- : Timer(delay, ServerInstance->Time(), false)
+ : Timer(delay, false)
, user(u), channels(chans), ext(ex)
{
ServerInstance->Timers.AddTimer(this);
@@ -66,7 +66,8 @@ class ModuleConnJoin : public Module
unsigned int defdelay;
public:
- ModuleConnJoin() : ext("join_timer", this)
+ ModuleConnJoin()
+ : ext("join_timer", ExtensionItem::EXT_USER, this)
{
}
diff --git a/src/modules/m_conn_umodes.cpp b/src/modules/m_conn_umodes.cpp
index 1e3ea1a49..7a8d66ae7 100644
--- a/src/modules/m_conn_umodes.cpp
+++ b/src/modules/m_conn_umodes.cpp
@@ -58,7 +58,7 @@ class ModuleModesOnConnect : public Module
while (ss >> buf)
modes.push_back(buf);
- ServerInstance->Modes->Process(modes, user);
+ ServerInstance->Parser.CallHandler("MODE", modes, user);
}
memcpy(ServerInstance->Config->DisabledUModes, save, 64);
diff --git a/src/modules/m_conn_waitpong.cpp b/src/modules/m_conn_waitpong.cpp
index 496b04c2d..87b6b51f2 100644
--- a/src/modules/m_conn_waitpong.cpp
+++ b/src/modules/m_conn_waitpong.cpp
@@ -32,7 +32,7 @@ class ModuleWaitPong : public Module
public:
ModuleWaitPong()
- : ext("waitpong_pingstr", this)
+ : ext("waitpong_pingstr", ExtensionItem::EXT_USER, this)
{
}
diff --git a/src/modules/m_customprefix.cpp b/src/modules/m_customprefix.cpp
index 65c2cbd31..f6f9a84f6 100644
--- a/src/modules/m_customprefix.cpp
+++ b/src/modules/m_customprefix.cpp
@@ -26,14 +26,13 @@ class CustomPrefixMode : public PrefixMode
bool depriv;
CustomPrefixMode(Module* parent, ConfigTag* Tag)
- : PrefixMode(parent, Tag->getString("name"), 0)
+ : PrefixMode(parent, Tag->getString("name"), 0, Tag->getInt("rank"))
, tag(Tag)
{
std::string v = tag->getString("prefix");
prefix = v.c_str()[0];
v = tag->getString("letter");
mode = v.c_str()[0];
- prefixrank = tag->getInt("rank");
levelrequired = tag->getInt("ranktoset", prefixrank);
depriv = tag->getBool("depriv", true);
}
diff --git a/src/modules/m_customtitle.cpp b/src/modules/m_customtitle.cpp
index 3386e8cd7..30c0aa4f2 100644
--- a/src/modules/m_customtitle.cpp
+++ b/src/modules/m_customtitle.cpp
@@ -28,7 +28,7 @@ class CommandTitle : public Command
public:
StringExtItem ctitle;
CommandTitle(Module* Creator) : Command(Creator,"TITLE", 2),
- ctitle("ctitle", Creator)
+ ctitle("ctitle", ExtensionItem::EXT_USER, Creator)
{
syntax = "<user> <password>";
}
@@ -70,26 +70,28 @@ class CommandTitle : public Command
};
-class ModuleCustomTitle : public Module
+class ModuleCustomTitle : public Module, public Whois::LineEventListener
{
CommandTitle cmd;
public:
- ModuleCustomTitle() : cmd(this)
+ ModuleCustomTitle()
+ : Whois::LineEventListener(this)
+ , cmd(this)
{
}
// :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games.
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text) CXX11_OVERRIDE
+ ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
/* We use this and not OnWhois because this triggers for remote, too */
- if (numeric == 312)
+ if (numeric.GetNumeric() == 312)
{
/* Insert our numeric before 312 */
- const std::string* ctitle = cmd.ctitle.get(dest);
+ const std::string* ctitle = cmd.ctitle.get(whois.GetTarget());
if (ctitle)
{
- ServerInstance->SendWhoisLine(user, dest, 320, "%s :%s", dest->nick.c_str(), ctitle->c_str());
+ whois.SendLine(320, ctitle);
}
}
/* Don't block anything */
diff --git a/src/modules/m_cycle.cpp b/src/modules/m_cycle.cpp
index c8b6bd8b4..202cb123f 100644
--- a/src/modules/m_cycle.cpp
+++ b/src/modules/m_cycle.cpp
@@ -44,7 +44,7 @@ class CommandCycle : public SplitCommand
if (!channel)
{
- user->WriteNumeric(ERR_NOSUCHCHANNEL, "%s :No such channel", parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHCHANNEL, parameters[0], "No such channel");
return CMD_FAILURE;
}
@@ -64,7 +64,7 @@ class CommandCycle : public SplitCommand
}
else
{
- user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel", channel->name.c_str());
+ user->WriteNumeric(ERR_NOTONCHANNEL, channel->name, "You're not on that channel");
}
return CMD_FAILURE;
diff --git a/src/modules/m_dccallow.cpp b/src/modules/m_dccallow.cpp
index 7332402ba..edf9d012f 100644
--- a/src/modules/m_dccallow.cpp
+++ b/src/modules/m_dccallow.cpp
@@ -25,6 +25,29 @@
#include "inspircd.h"
+static const char* const helptext[] =
+{
+ "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]",
+ "You may allow DCCs from specific users by specifying a",
+ "DCC allow for the user you want to receive DCCs from.",
+ "For example, to allow the user Brain to send you inspircd.exe",
+ "you would type:",
+ "/DCCALLOW +Brain",
+ "Brain would then be able to send you files. They would have to",
+ "resend the file again if the server gave them an error message",
+ "before you added them to your DCCALLOW list.",
+ "DCCALLOW entries will be temporary by default, if you want to add",
+ "them to your DCCALLOW list until you leave IRC, type:",
+ "/DCCALLOW +Brain 0",
+ "To remove the user from your DCCALLOW list, type:",
+ "/DCCALLOW -Brain",
+ "To see the users in your DCCALLOW list, type:",
+ "/DCCALLOW LIST",
+ "NOTE: If the user leaves IRC or changes their nickname",
+ " they will be removed from your DCCALLOW list.",
+ " your DCCALLOW list will be deleted when you leave IRC."
+};
+
class BannedFileList
{
public:
@@ -58,11 +81,12 @@ class CommandDccallow : public Command
DCCAllowExt& ext;
public:
+ unsigned int maxentries;
CommandDccallow(Module* parent, DCCAllowExt& Ext)
: Command(parent, "DCCALLOW", 0)
, ext(Ext)
{
- syntax = "{[+|-]<nick> <time>|HELP|LIST}";
+ syntax = "[(+|-)<nick> [<time>]]|[LIST|HELP]";
/* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */
}
@@ -96,15 +120,15 @@ class CommandDccallow : public Command
}
else
{
- user->WriteNumeric(998, ":DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
+ user->WriteNumeric(998, "DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
return CMD_FAILURE;
}
}
- std::string nick = parameters[0].substr(1);
+ std::string nick(parameters[0], 1);
User *target = ServerInstance->FindNickOnly(nick);
- if ((target) && (!IS_SERVER(target)) && (!target->quitting) && (target->registered == REG_ALL))
+ if ((target) && (!target->quitting) && (target->registered == REG_ALL))
{
if (action == '-')
@@ -119,7 +143,7 @@ class CommandDccallow : public Command
if (i->nickname == target->nick)
{
dl->erase(i);
- user->WriteNumeric(995, "%s :Removed %s from your DCCALLOW list", user->nick.c_str(), target->nick.c_str());
+ user->WriteNumeric(995, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
break;
}
}
@@ -129,7 +153,7 @@ class CommandDccallow : public Command
{
if (target == user)
{
- user->WriteNumeric(996, "%s :You cannot add yourself to your own DCCALLOW list!", user->nick.c_str());
+ user->WriteNumeric(996, user->nick, "You cannot add yourself to your own DCCALLOW list!");
return CMD_FAILURE;
}
@@ -142,11 +166,17 @@ class CommandDccallow : public Command
ul.push_back(user);
}
+ if (dl->size() >= maxentries)
+ {
+ user->WriteNumeric(996, user->nick, "Too many nicks on DCCALLOW list");
+ return CMD_FAILURE;
+ }
+
for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
{
if (k->nickname == target->nick)
{
- user->WriteNumeric(996, "%s :%s is already on your DCCALLOW list", user->nick.c_str(), target->nick.c_str());
+ user->WriteNumeric(996, user->nick, InspIRCd::Format("%s is already on your DCCALLOW list", target->nick.c_str()));
return CMD_FAILURE;
}
}
@@ -177,11 +207,11 @@ class CommandDccallow : public Command
if (length > 0)
{
- user->WriteNumeric(993, "%s :Added %s to DCCALLOW list for %ld seconds", user->nick.c_str(), target->nick.c_str(), length);
+ user->WriteNumeric(993, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %ld seconds", target->nick.c_str(), length));
}
else
{
- user->WriteNumeric(994, "%s :Added %s to DCCALLOW list for this session", user->nick.c_str(), target->nick.c_str());
+ user->WriteNumeric(994, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
}
/* route it. */
@@ -191,7 +221,7 @@ class CommandDccallow : public Command
else
{
// nick doesn't exist
- user->WriteNumeric(401, "%s :No such nick/channel", nick.c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(nick));
return CMD_FAILURE;
}
}
@@ -205,26 +235,9 @@ class CommandDccallow : public Command
void DisplayHelp(User* user)
{
- user->WriteNumeric(998, ":DCCALLOW [<+|->nick [time]] [list] [help]");
- user->WriteNumeric(998, ":You may allow DCCs from specific users by specifying a");
- user->WriteNumeric(998, ":DCC allow for the user you want to receive DCCs from.");
- user->WriteNumeric(998, ":For example, to allow the user Brain to send you inspircd.exe");
- user->WriteNumeric(998, ":you would type:");
- user->WriteNumeric(998, ":/DCCALLOW +Brain");
- user->WriteNumeric(998, ":Brain would then be able to send you files. They would have to");
- user->WriteNumeric(998, ":resend the file again if the server gave them an error message");
- user->WriteNumeric(998, ":before you added them to your DCCALLOW list.");
- user->WriteNumeric(998, ":DCCALLOW entries will be temporary by default, if you want to add");
- user->WriteNumeric(998, ":them to your DCCALLOW list until you leave IRC, type:");
- user->WriteNumeric(998, ":/DCCALLOW +Brain 0");
- user->WriteNumeric(998, ":To remove the user from your DCCALLOW list, type:");
- user->WriteNumeric(998, ":/DCCALLOW -Brain");
- user->WriteNumeric(998, ":To see the users in your DCCALLOW list, type:");
- user->WriteNumeric(998, ":/DCCALLOW LIST");
- user->WriteNumeric(998, ":NOTE: If the user leaves IRC or changes their nickname");
- user->WriteNumeric(998, ": they will be removed from your DCCALLOW list.");
- user->WriteNumeric(998, ": your DCCALLOW list will be deleted when you leave IRC.");
- user->WriteNumeric(999, ":End of DCCALLOW HELP");
+ for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
+ user->WriteNumeric(998, helptext[i]);
+ user->WriteNumeric(999, "End of DCCALLOW HELP");
LocalUser* localuser = IS_LOCAL(user);
if (localuser)
@@ -234,18 +247,18 @@ class CommandDccallow : public Command
void DisplayDCCAllowList(User* user)
{
// display current DCCALLOW list
- user->WriteNumeric(990, ":Users on your DCCALLOW list:");
+ user->WriteNumeric(990, "Users on your DCCALLOW list:");
dl = ext.get(user);
if (dl)
{
for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
{
- user->WriteNumeric(991, "%s :%s (%s)", user->nick.c_str(), c->nickname.c_str(), c->hostmask.c_str());
+ user->WriteNumeric(991, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
}
}
- user->WriteNumeric(992, ":End of DCCALLOW list");
+ user->WriteNumeric(992, "End of DCCALLOW list");
}
};
@@ -257,7 +270,7 @@ class ModuleDCCAllow : public Module
public:
ModuleDCCAllow()
- : ext("dccallow", this)
+ : ext("dccallow", ExtensionItem::EXT_USER, this)
, cmd(this, ext)
{
}
@@ -268,11 +281,7 @@ class ModuleDCCAllow : public Module
// remove their DCCALLOW list if they have one
if (udl)
- {
- userlist::iterator it = std::find(ul.begin(), ul.end(), user);
- if (it != ul.end())
- ul.erase(it);
- }
+ stdalgo::erase(ul, user);
// remove them from any DCCALLOW lists
// they are currently on
@@ -314,23 +323,43 @@ class ModuleDCCAllow : public Module
return MOD_RES_PASSTHRU;
}
- // tokenize
- std::stringstream ss(text);
- std::string buf;
- std::vector<std::string> tokens;
+ std::string buf = text.substr(5);
+ size_t s = buf.find(' ');
+ if (s == std::string::npos)
+ return MOD_RES_PASSTHRU;
- while (ss >> buf)
- tokens.push_back(buf);
-
- irc::string type = tokens[1].c_str();
+ const std::string type = buf.substr(0, s);
ConfigTag* conftag = ServerInstance->Config->ConfValue("dccallow");
bool blockchat = conftag->getBool("blockchat");
- if (type == "SEND")
+ if (stdalgo::string::equalsci(type, "SEND"))
{
+ size_t first;
+
+ buf = buf.substr(s + 1);
+
+ if (!buf.empty() && buf[0] == '"')
+ {
+ s = buf.find('"', 1);
+
+ if (s == std::string::npos || s <= 1)
+ return MOD_RES_PASSTHRU;
+
+ --s;
+ first = 1;
+ }
+ else
+ {
+ s = buf.find(' ');
+ first = 0;
+ }
+
+ if (s == std::string::npos)
+ return MOD_RES_PASSTHRU;
+
std::string defaultaction = conftag->getString("action");
- std::string filename = tokens[2];
+ std::string filename = buf.substr(first, s);
bool found = false;
for (unsigned int i = 0; i < bfl.size(); i++)
@@ -357,7 +386,7 @@ class ModuleDCCAllow : public Module
u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
return MOD_RES_DENY;
}
- else if ((type == "CHAT") && (blockchat))
+ else if ((blockchat) && (stdalgo::string::equalsci(type, "CHAT")))
{
user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
u->WriteNotice(user->nick + " (" + user->ident + "@" + user->dhost + ") attempted to initiate a DCC CHAT session, which was blocked.");
@@ -385,7 +414,7 @@ class ModuleDCCAllow : public Module
{
if (iter2->length != 0 && (iter2->set_on + iter2->length) <= ServerInstance->Time())
{
- u->WriteNumeric(997, "%s :DCCALLOW entry for %s has expired", u->nick.c_str(), iter2->nickname.c_str());
+ u->WriteNumeric(997, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
iter2 = dl->erase(iter2);
}
else
@@ -420,7 +449,7 @@ class ModuleDCCAllow : public Module
{
u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
- u->WriteNumeric(995, "%s :Removed %s from your DCCALLOW list", u->nick.c_str(), i->nickname.c_str());
+ u->WriteNumeric(995, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
dl->erase(i);
break;
}
@@ -451,6 +480,9 @@ class ModuleDCCAllow : public Module
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
+ ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
+ cmd.maxentries = tag->getInt("maxentries", 20);
+
bfl.clear();
ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
for (ConfigIter i = tags.first; i != tags.second; ++i)
diff --git a/src/modules/m_deaf.cpp b/src/modules/m_deaf.cpp
index 9800b32a9..88919e91b 100644
--- a/src/modules/m_deaf.cpp
+++ b/src/modules/m_deaf.cpp
@@ -66,7 +66,6 @@ class ModuleDeaf : public Module
return MOD_RES_PASSTHRU;
Channel* chan = static_cast<Channel*>(dest);
- const UserMembList *ulist = chan->GetUsers();
bool is_bypasschar = (deaf_bypasschars.find(text[0]) != std::string::npos);
bool is_bypasschar_uline = (deaf_bypasschars_uline.find(text[0]) != std::string::npos);
@@ -74,10 +73,11 @@ class ModuleDeaf : public Module
* If we have no bypasschars_uline in config, and this is a bypasschar (regular)
* Than it is obviously going to get through +d, no build required
*/
- if (!deaf_bypasschars_uline.empty() && is_bypasschar)
+ if (deaf_bypasschars_uline.empty() && is_bypasschar)
return MOD_RES_PASSTHRU;
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
/* not +d ? */
if (!i->first->IsModeSet(m1))
@@ -94,9 +94,6 @@ class ModuleDeaf : public Module
if (is_bypasschar && !is_a_uline)
continue; /* deliver message */
- if (status && !strchr(i->second->GetAllPrefixChars(), status))
- continue;
-
/* don't deliver message! */
exempt_list.insert(i->first);
}
diff --git a/src/modules/m_delayjoin.cpp b/src/modules/m_delayjoin.cpp
index b3165c7be..e864a8289 100644
--- a/src/modules/m_delayjoin.cpp
+++ b/src/modules/m_delayjoin.cpp
@@ -21,7 +21,6 @@
#include "inspircd.h"
-#include <stdarg.h>
class DelayJoinMode : public ModeHandler
{
@@ -40,7 +39,9 @@ class ModuleDelayJoin : public Module
DelayJoinMode djm;
public:
LocalIntExt unjoined;
- ModuleDelayJoin() : djm(this), unjoined("delayjoin", this)
+ ModuleDelayJoin()
+ : djm(this)
+ , unjoined("delayjoin", ExtensionItem::EXT_MEMBERSHIP, this)
{
}
@@ -67,8 +68,8 @@ ModeAction DelayJoinMode::OnModeChange(User* source, User* dest, Channel* channe
* Make all users visible, as +D is being removed. If we don't do this,
* they remain permanently invisible on this channel!
*/
- const UserMembList* names = channel->GetUsers();
- for (UserMembCIter n = names->begin(); n != names->end(); ++n)
+ const Channel::MemberMap& users = channel->GetUsers();
+ for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
creator->OnText(n->first, channel, TYPE_CHANNEL, "", 0, empty);
}
channel->SetMode(this, adding);
@@ -95,8 +96,8 @@ ModResult ModuleDelayJoin::OnNamesListItem(User* issuer, Membership* memb, std::
static void populate(CUList& except, Membership* memb)
{
- const UserMembList* users = memb->chan->GetUsers();
- for(UserMembCIter i = users->begin(); i != users->end(); i++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
{
if (i->first == memb->user || !IS_LOCAL(i->first))
continue;
@@ -139,10 +140,6 @@ void ModuleDelayJoin::OnBuildNeighborList(User* source, IncludeChanList& include
void ModuleDelayJoin::OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list)
{
- /* Server origin */
- if (!user)
- return;
-
if (target_type != TYPE_CHANNEL)
return;
@@ -166,7 +163,11 @@ void ModuleDelayJoin::OnText(User* user, void* dest, int target_type, const std:
/* make the user visible if he receives any mode change */
ModResult ModuleDelayJoin::OnRawMode(User* user, Channel* channel, ModeHandler* mh, const std::string& param, bool adding)
{
- if (!user || !channel || param.empty())
+ if (!channel || param.empty())
+ return MOD_RES_PASSTHRU;
+
+ // If not a prefix mode then we got nothing to do here
+ if (!mh->IsPrefixMode())
return MOD_RES_PASSTHRU;
User* dest;
diff --git a/src/modules/m_delaymsg.cpp b/src/modules/m_delaymsg.cpp
index 1730663c5..1ad41cc57 100644
--- a/src/modules/m_delaymsg.cpp
+++ b/src/modules/m_delaymsg.cpp
@@ -25,7 +25,7 @@ class DelayMsgMode : public ParamMode<DelayMsgMode, LocalIntExt>
LocalIntExt jointime;
DelayMsgMode(Module* Parent)
: ParamMode<DelayMsgMode, LocalIntExt>(Parent, "delaymsg", 'd')
- , jointime("delaymsg", Parent)
+ , jointime("delaymsg", ExtensionItem::EXT_MEMBERSHIP, Parent)
{
levelrequired = OP_VALUE;
}
@@ -47,6 +47,7 @@ class DelayMsgMode : public ParamMode<DelayMsgMode, LocalIntExt>
class ModuleDelayMsg : public Module
{
DelayMsgMode djm;
+ bool allownotice;
public:
ModuleDelayMsg() : djm(this)
{
@@ -55,6 +56,7 @@ class ModuleDelayMsg : public Module
Version GetVersion() CXX11_OVERRIDE;
void OnUserJoin(Membership* memb, bool sync, bool created, CUList&) CXX11_OVERRIDE;
ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE;
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
};
ModeAction DelayMsgMode::OnSet(User* source, Channel* chan, std::string& parameter)
@@ -73,8 +75,8 @@ void DelayMsgMode::OnUnset(User* source, Channel* chan)
/*
* Clean up metadata
*/
- const UserMembList* names = chan->GetUsers();
- for (UserMembCIter n = names->begin(); n != names->end(); ++n)
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
jointime.set(n->second, 0);
}
@@ -93,11 +95,10 @@ void ModuleDelayMsg::OnUserJoin(Membership* memb, bool sync, bool created, CULis
ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype)
{
- /* Server origin */
- if ((!user) || (!IS_LOCAL(user)))
+ if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
- if ((target_type != TYPE_CHANNEL) || (msgtype != MSG_PRIVMSG))
+ if ((target_type != TYPE_CHANNEL) || ((!allownotice) && (msgtype == MSG_NOTICE)))
return MOD_RES_PASSTHRU;
Channel* channel = (Channel*) dest;
@@ -117,8 +118,7 @@ ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_ty
{
if (channel->GetPrefixValue(user) < VOICE_VALUE)
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :You must wait %d seconds after joining to send to channel (+d)",
- channel->name.c_str(), len);
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, channel->name, InspIRCd::Format("You must wait %d seconds after joining to send to channel (+d)", len));
return MOD_RES_DENY;
}
}
@@ -130,4 +130,10 @@ ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_ty
return MOD_RES_PASSTHRU;
}
+void ModuleDelayMsg::ReadConfig(ConfigStatus& status)
+{
+ ConfigTag* tag = ServerInstance->Config->ConfValue("delaymsg");
+ allownotice = tag->getBool("allownotice", true);
+}
+
MODULE_INIT(ModuleDelayMsg)
diff --git a/src/modules/m_denychans.cpp b/src/modules/m_denychans.cpp
index 184134025..467c8a03b 100644
--- a/src/modules/m_denychans.cpp
+++ b/src/modules/m_denychans.cpp
@@ -56,7 +56,7 @@ class ModuleDenyChannels : public Module
if (InspIRCd::Match(redirect, j->second->getString("name")))
{
bool goodchan = false;
- ConfigTagList goodchans = ServerInstance->Config->ConfTags("badchan");
+ ConfigTagList goodchans = ServerInstance->Config->ConfTags("goodchan");
for (ConfigIter k = goodchans.first; k != goodchans.second; ++k)
{
if (InspIRCd::Match(redirect, k->second->getString("name")))
@@ -113,13 +113,13 @@ class ModuleDenyChannels : public Module
Channel *newchan = ServerInstance->FindChan(redirect);
if ((!newchan) || (!newchan->IsModeSet(redirectmode)))
{
- user->WriteNumeric(926, "%s :Channel %s is forbidden, redirecting to %s: %s", cname.c_str(),cname.c_str(),redirect.c_str(), reason.c_str());
+ user->WriteNumeric(926, cname, InspIRCd::Format("Channel %s is forbidden, redirecting to %s: %s", cname.c_str(), redirect.c_str(), reason.c_str()));
Channel::JoinUser(user, redirect);
return MOD_RES_DENY;
}
}
- user->WriteNumeric(926, "%s :Channel %s is forbidden: %s", cname.c_str(),cname.c_str(),reason.c_str());
+ user->WriteNumeric(926, cname, InspIRCd::Format("Channel %s is forbidden: %s", cname.c_str(), reason.c_str()));
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_devoice.cpp b/src/modules/m_devoice.cpp
index 901f77b1a..4e4b3a354 100644
--- a/src/modules/m_devoice.cpp
+++ b/src/modules/m_devoice.cpp
@@ -43,7 +43,7 @@ class CommandDevoice : public Command
modes.push_back("-v");
modes.push_back(user->nick);
- ServerInstance->Modes->Process(modes, ServerInstance->FakeClient);
+ ServerInstance->Parser.CallHandler("MODE", modes, ServerInstance->FakeClient);
return CMD_SUCCESS;
}
};
diff --git a/src/modules/m_dnsbl.cpp b/src/modules/m_dnsbl.cpp
index 6fe54fa19..752a0d7a5 100644
--- a/src/modules/m_dnsbl.cpp
+++ b/src/modules/m_dnsbl.cpp
@@ -66,7 +66,17 @@ class DNSBLResolver : public DNS::Request
if (!them)
return;
- const DNS::ResourceRecord &ans_record = r->answers[0];
+ const DNS::ResourceRecord* const ans_record = r->FindAnswerOfType(DNS::QUERY_A);
+ if (!ans_record)
+ return;
+
+ // All replies should be in 127.0.0.0/8
+ if (ans_record->rdata.compare(0, 4, "127.") != 0)
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "DNSBL: %s returned address outside of acceptable subnet 127.0.0.0/8: %s", ConfEntry->domain.c_str(), ans_record->rdata.c_str());
+ ConfEntry->stats_misses++;
+ return;
+ }
int i = countExt.get(them);
if (i)
@@ -78,7 +88,7 @@ class DNSBLResolver : public DNS::Request
bool match = false;
in_addr resultip;
- inet_aton(ans_record.rdata.c_str(), &resultip);
+ inet_aton(ans_record->rdata.c_str(), &resultip);
switch (ConfEntry->type)
{
@@ -117,13 +127,13 @@ class DNSBLResolver : public DNS::Request
{
if (!ConfEntry->ident.empty())
{
- them->WriteNumeric(304, ":Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
+ them->WriteNumeric(304, "Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
them->ChangeIdent(ConfEntry->ident);
}
if (!ConfEntry->host.empty())
{
- them->WriteNumeric(304, ":Your host has been set to " + ConfEntry->host + " because you matched " + reason);
+ them->WriteNumeric(304, "Your host has been set to " + ConfEntry->host + " because you matched " + reason);
them->ChangeDisplayedHost(ConfEntry->host);
}
@@ -236,7 +246,12 @@ class ModuleDNSBL : public Module
return DNSBLConfEntry::I_UNKNOWN;
}
public:
- ModuleDNSBL() : DNS(this, "DNS"), nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
+ ModuleDNSBL()
+ : DNS(this, "DNS")
+ , nameExt("dnsbl_match", ExtensionItem::EXT_USER, this)
+ , countExt("dnsbl_pending", ExtensionItem::EXT_USER, this)
+ {
+ }
Version GetVersion() CXX11_OVERRIDE
{
@@ -319,7 +334,7 @@ class ModuleDNSBL : public Module
void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
{
- if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET) || !DNS)
+ if ((user->exempt) || !DNS)
return;
if (user->MyClass)
@@ -330,13 +345,32 @@ class ModuleDNSBL : public Module
else
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User has no connect class in OnSetUserIP");
- unsigned int a, b, c, d;
- d = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
- c = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
- b = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
- a = (unsigned int) user->client_sa.in4.sin_addr.s_addr & 0xFF;
+ std::string reversedip;
+ if (user->client_sa.sa.sa_family == AF_INET)
+ {
+ unsigned int a, b, c, d;
+ d = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
+ c = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
+ b = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
+ a = (unsigned int) user->client_sa.in4.sin_addr.s_addr & 0xFF;
+
+ reversedip = ConvToStr(d) + "." + ConvToStr(c) + "." + ConvToStr(b) + "." + ConvToStr(a);
+ }
+ else if (user->client_sa.sa.sa_family == AF_INET6)
+ {
+ const unsigned char* ip = user->client_sa.in6.sin6_addr.s6_addr;
+
+ std::string buf = BinToHex(ip, 16);
+ for (std::string::const_reverse_iterator it = buf.rbegin(); it != buf.rend(); ++it)
+ {
+ reversedip.push_back(*it);
+ reversedip.push_back('.');
+ }
+ }
+ else
+ return;
- const std::string reversedip = ConvToStr(d) + "." + ConvToStr(c) + "." + ConvToStr(b) + "." + ConvToStr(a);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Reversed IP %s -> %s", user->GetIPString().c_str(), reversedip.c_str());
countExt.set(user, DNSBLConfEntries.size());
@@ -382,9 +416,9 @@ class ModuleDNSBL : public Module
return MOD_RES_PASSTHRU;
}
- ModResult OnStats(char symbol, User* user, string_list &results) CXX11_OVERRIDE
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'd')
+ if (stats.GetSymbol() != 'd')
return MOD_RES_PASSTHRU;
unsigned long total_hits = 0, total_misses = 0;
@@ -394,12 +428,12 @@ class ModuleDNSBL : public Module
total_hits += (*i)->stats_hits;
total_misses += (*i)->stats_misses;
- results.push_back("304 " + user->nick + " :DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " +
+ stats.AddRow(304, "DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " +
ConvToStr((*i)->stats_hits) + " hits and " + ConvToStr((*i)->stats_misses) + " misses");
}
- results.push_back("304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits));
- results.push_back("304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses));
+ stats.AddRow(304, "DNSBLSTATS Total hits: " + ConvToStr(total_hits));
+ stats.AddRow(304, "DNSBLSTATS Total misses: " + ConvToStr(total_misses));
return MOD_RES_PASSTHRU;
}
diff --git a/src/modules/m_exemptchanops.cpp b/src/modules/m_exemptchanops.cpp
index 43ae21a1c..2884385fb 100644
--- a/src/modules/m_exemptchanops.cpp
+++ b/src/modules/m_exemptchanops.cpp
@@ -29,9 +29,23 @@ class ExemptChanOps : public ListModeBase
bool ValidateParam(User* user, Channel* chan, std::string &word)
{
- if (!ServerInstance->Modes->FindMode(word, MODETYPE_CHANNEL))
+ std::string::size_type p = word.find(':');
+ if (p == std::string::npos)
{
- user->WriteNumeric(955, "%s %s :Mode doesn't exist", chan->name.c_str(), word.c_str());
+ user->WriteNumeric(955, chan->name, word, "Invalid exemptchanops entry, format is <restriction>:<prefix>");
+ return false;
+ }
+
+ std::string restriction(word, 0, p);
+ // If there is a '-' in the restriction string ignore it and everything after it
+ // to support "auditorium-vis" and "auditorium-see" in m_auditorium
+ p = restriction.find('-');
+ if (p != std::string::npos)
+ restriction.erase(p);
+
+ if (!ServerInstance->Modes->FindMode(restriction, MODETYPE_CHANNEL))
+ {
+ user->WriteNumeric(955, chan->name, restriction, "Unknown restriction");
return false;
}
@@ -40,17 +54,17 @@ class ExemptChanOps : public ListModeBase
void TellListTooLong(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(959, "%s %s :Channel exemptchanops list is full", chan->name.c_str(), word.c_str());
+ user->WriteNumeric(959, chan->name, word, "Channel exemptchanops list is full");
}
void TellAlreadyOnList(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(957, "%s :The word %s is already on the exemptchanops list", chan->name.c_str(), word.c_str());
+ user->WriteNumeric(957, chan->name, InspIRCd::Format("The word %s is already on the exemptchanops list", word.c_str()));
}
void TellNotSet(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(958, "%s :No such exemptchanops word is set", chan->name.c_str());
+ user->WriteNumeric(958, chan->name, "No such exemptchanops word is set");
}
};
@@ -84,7 +98,7 @@ class ExemptHandler : public HandlerBase3<ModResult, User*, Channel*, const std:
if (pos == std::string::npos)
continue;
if (!i->mask.compare(0, pos, restriction))
- minmode = (*i).mask.substr(pos + 1);
+ minmode.assign(i->mask, pos + 1, std::string::npos);
}
}
diff --git a/src/modules/m_filter.cpp b/src/modules/m_filter.cpp
index 9acce033a..bd19a60ba 100644
--- a/src/modules/m_filter.cpp
+++ b/src/modules/m_filter.cpp
@@ -156,7 +156,7 @@ class CommandFilter : public Command
class ModuleFilter : public Module
{
- typedef std::set<std::string, irc::insensitive_swo> ExemptTargetSet;
+ typedef insp::flat_set<std::string, irc::insensitive_swo> ExemptTargetSet;
bool initing;
RegexFactory* factory;
@@ -187,7 +187,7 @@ class ModuleFilter : public Module
FilterResult DecodeFilter(const std::string &data);
void OnSyncNetwork(ProtocolInterface::Server& server) CXX11_OVERRIDE;
void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata) CXX11_OVERRIDE;
- ModResult OnStats(char symbol, User* user, string_list &results) CXX11_OVERRIDE;
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE;
ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE;
void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
bool AppliesToMe(User* user, FilterResult* filter, int flags);
@@ -343,14 +343,14 @@ ModResult ModuleFilter::OnUserPreMessage(User* user, void* dest, int target_type
{
ServerInstance->SNO->WriteGlobalSno('a', "FILTER: "+user->nick+" had their message filtered, target was "+target+": "+f->reason);
if (target_type == TYPE_CHANNEL)
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Message to channel blocked and opers notified (%s)", target.c_str(), f->reason.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, target, InspIRCd::Format("Message to channel blocked and opers notified (%s)", f->reason.c_str()));
else
user->WriteNotice("Your message to "+target+" was blocked and opers notified: "+f->reason);
}
else if (f->action == FA_SILENT)
{
if (target_type == TYPE_CHANNEL)
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Message to channel blocked (%s)", target.c_str(), f->reason.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, target, InspIRCd::Format("Message to channel blocked (%s)", f->reason.c_str()));
else
user->WriteNotice("Your message to "+target+" was blocked: "+f->reason);
}
@@ -632,17 +632,15 @@ std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &freeform
bool ModuleFilter::StringToFilterAction(const std::string& str, FilterAction& fa)
{
- irc::string s(str.c_str());
-
- if (s == "gline")
+ if (stdalgo::string::equalsci(str, "gline"))
fa = FA_GLINE;
- else if (s == "block")
+ else if (stdalgo::string::equalsci(str, "block"))
fa = FA_BLOCK;
- else if (s == "silent")
+ else if (stdalgo::string::equalsci(str, "silent"))
fa = FA_SILENT;
- else if (s == "kill")
+ else if (stdalgo::string::equalsci(str, "kill"))
fa = FA_KILL;
- else if (s == "none")
+ else if (stdalgo::string::equalsci(str, "none"))
fa = FA_NONE;
else
return false;
@@ -693,21 +691,21 @@ void ModuleFilter::ReadFilters()
}
}
-ModResult ModuleFilter::OnStats(char symbol, User* user, string_list &results)
+ModResult ModuleFilter::OnStats(Stats::Context& stats)
{
- if (symbol == 's')
+ if (stats.GetSymbol() == 's')
{
for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); i++)
{
- results.push_back("223 "+user->nick+" :"+RegexEngine.GetProvider()+":"+i->freeform+" "+i->GetFlags()+" "+FilterActionToString(i->action)+" "+ConvToStr(i->gline_time)+" :"+i->reason);
+ stats.AddRow(223, RegexEngine.GetProvider()+":"+i->freeform+" "+i->GetFlags()+" "+FilterActionToString(i->action)+" "+ConvToStr(i->gline_time)+" :"+i->reason);
}
for (ExemptTargetSet::const_iterator i = exemptedchans.begin(); i != exemptedchans.end(); ++i)
{
- results.push_back("223 "+user->nick+" :EXEMPT "+(*i));
+ stats.AddRow(223, "EXEMPT "+(*i));
}
for (ExemptTargetSet::const_iterator i = exemptednicks.begin(); i != exemptednicks.end(); ++i)
{
- results.push_back("223 "+user->nick+" :EXEMPT "+(*i));
+ stats.AddRow(223, "EXEMPT "+(*i));
}
}
return MOD_RES_PASSTHRU;
diff --git a/src/modules/m_flashpolicyd.cpp b/src/modules/m_flashpolicyd.cpp
index 95b82848f..8f847e111 100644
--- a/src/modules/m_flashpolicyd.cpp
+++ b/src/modules/m_flashpolicyd.cpp
@@ -23,20 +23,30 @@ class FlashPDSocket;
namespace
{
- std::set<FlashPDSocket*> sockets;
+ insp::intrusive_list<FlashPDSocket> sockets;
std::string policy_reply;
const std::string expected_request("<policy-file-request/>\0", 23);
}
-class FlashPDSocket : public BufferedSocket
+class FlashPDSocket : public BufferedSocket, public Timer, public insp::intrusive_list_node<FlashPDSocket>
{
- public:
- time_t created;
+ /** True if this object is in the cull list
+ */
+ bool waitingcull;
+
+ bool Tick(time_t currtime) CXX11_OVERRIDE
+ {
+ AddToCull();
+ return false;
+ }
- FlashPDSocket(int newfd)
+ public:
+ FlashPDSocket(int newfd, unsigned int timeoutsec)
: BufferedSocket(newfd)
- , created(ServerInstance->Time())
+ , Timer(timeoutsec)
+ , waitingcull(false)
{
+ ServerInstance->Timers.AddTimer(this);
}
~FlashPDSocket()
@@ -58,10 +68,10 @@ class FlashPDSocket : public BufferedSocket
void AddToCull()
{
- if (created == 0)
+ if (waitingcull)
return;
- created = 0;
+ waitingcull = true;
Close();
ServerInstance->GlobalCulls.AddItem(this);
}
@@ -69,19 +79,9 @@ class FlashPDSocket : public BufferedSocket
class ModuleFlashPD : public Module
{
- time_t timeout;
+ unsigned int timeout;
public:
- void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
- {
- for (std::set<FlashPDSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
- {
- FlashPDSocket* sock = *i;
- if ((sock->created + timeout <= curtime) && (sock->created != 0))
- sock->AddToCull();
- }
- }
-
ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
{
if (from->bind_tag->getString("type") != "flashpolicyd")
@@ -90,7 +90,7 @@ class ModuleFlashPD : public Module
if (policy_reply.empty())
return MOD_RES_DENY;
- sockets.insert(new FlashPDSocket(nfd));
+ sockets.push_front(new FlashPDSocket(nfd, timeout));
return MOD_RES_ALLOW;
}
@@ -128,6 +128,13 @@ class ModuleFlashPD : public Module
to_ports.append(ConvToStr(ls->bind_port)).push_back(',');
}
+
+ if (to_ports.empty())
+ {
+ policy_reply.clear();
+ return;
+ }
+
to_ports.erase(to_ports.size() - 1);
policy_reply =
@@ -141,7 +148,7 @@ class ModuleFlashPD : public Module
CullResult cull()
{
- for (std::set<FlashPDSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
+ for (insp::intrusive_list<FlashPDSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
{
FlashPDSocket* sock = *i;
sock->AddToCull();
diff --git a/src/modules/m_globalload.cpp b/src/modules/m_globalload.cpp
index 8ee8472e6..b71f29fcc 100644
--- a/src/modules/m_globalload.cpp
+++ b/src/modules/m_globalload.cpp
@@ -44,11 +44,11 @@ class CommandGloadmodule : public Command
if (ServerInstance->Modules->Load(parameters[0].c_str()))
{
ServerInstance->SNO->WriteToSnoMask('a', "NEW MODULE '%s' GLOBALLY LOADED BY '%s'",parameters[0].c_str(), user->nick.c_str());
- user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module successfully loaded.", parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Module successfully loaded.");
}
else
{
- user->WriteNumeric(ERR_CANTLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTLOADMODULE, parameters[0], ServerInstance->Modules->LastError());
}
}
else
@@ -79,7 +79,7 @@ class CommandGunloadmodule : public Command
if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") &&
InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map))
{
- user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :You cannot unload core commands!", parameters[0].c_str());
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "You cannot unload core commands!");
return CMD_FAILURE;
}
@@ -93,16 +93,15 @@ class CommandGunloadmodule : public Command
if (ServerInstance->Modules->Unload(m))
{
ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY UNLOADED BY '%s'",parameters[0].c_str(), user->nick.c_str());
- user->SendText(":%s 973 %s %s :Module successfully unloaded.",
- ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), parameters[0].c_str());
+ user->WriteRemoteNumeric(973, parameters[0], "Module successfully unloaded.");
}
else
{
- user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], ServerInstance->Modules->LastError());
}
}
else
- user->SendText(":%s %03d %s %s :No such module", ServerInstance->Config->ServerName.c_str(), ERR_CANTUNLOADMODULE, user->nick.c_str(), parameters[0].c_str());
+ user->WriteRemoteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "No such module");
}
else
ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBAL UNLOAD BY '%s' (not unloaded here)",parameters[0].c_str(), user->nick.c_str());
@@ -116,25 +115,6 @@ class CommandGunloadmodule : public Command
}
};
-class GReloadModuleWorker : public HandlerBase1<void, bool>
-{
- public:
- const std::string nick;
- const std::string name;
- const std::string uid;
- GReloadModuleWorker(const std::string& usernick, const std::string& uuid, const std::string& modn)
- : nick(usernick), name(modn), uid(uuid) {}
- void Call(bool result)
- {
- ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY RELOADED BY '%s'%s", name.c_str(), nick.c_str(), result ? "" : " (failed here)");
- User* user = ServerInstance->FindNick(uid);
- if (user)
- user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %ssuccessfully reloaded.",
- name.c_str(), result ? "" : "un");
- ServerInstance->GlobalCulls.AddItem(this);
- }
-};
-
/** Handle /GRELOADMODULE
*/
class CommandGreloadmodule : public Command
@@ -154,14 +134,12 @@ class CommandGreloadmodule : public Command
Module* m = ServerInstance->Modules->Find(parameters[0]);
if (m)
{
- GReloadModuleWorker* worker = NULL;
- if (m != creator)
- worker = new GReloadModuleWorker(user->nick, user->uuid, parameters[0]);
- ServerInstance->Modules->Reload(m, worker);
+ ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY RELOADED BY '%s'", parameters[0].c_str(), user->nick.c_str());
+ ServerInstance->Parser.CallHandler("RELOADMODULE", parameters, user);
}
else
{
- user->WriteNumeric(RPL_LOADEDMODULE, "%s :Could not find module by that name", parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Could not find module by that name");
return CMD_FAILURE;
}
}
diff --git a/src/modules/m_helpop.cpp b/src/modules/m_helpop.cpp
index 2fe958a71..95f69774b 100644
--- a/src/modules/m_helpop.cpp
+++ b/src/modules/m_helpop.cpp
@@ -57,15 +57,15 @@ class CommandHelpop : public Command
if (parameter == "index")
{
/* iterate over all helpop items */
- user->WriteNumeric(290, ":HELPOP topic index");
+ user->WriteNumeric(290, "HELPOP topic index");
for (HelpopMap::const_iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++)
- user->WriteNumeric(292, ": %s", iter->first.c_str());
- user->WriteNumeric(292, ":*** End of HELPOP topic index");
+ user->WriteNumeric(292, InspIRCd::Format(" %s", iter->first.c_str()));
+ user->WriteNumeric(292, "*** End of HELPOP topic index");
}
else
{
- user->WriteNumeric(290, ":*** HELPOP for %s", parameter.c_str());
- user->WriteNumeric(292, ": -");
+ user->WriteNumeric(290, InspIRCd::Format("*** HELPOP for %s", parameter.c_str()));
+ user->WriteNumeric(292, " -");
HelpopMap::const_iterator iter = helpop_map.find(parameter);
@@ -82,26 +82,28 @@ class CommandHelpop : public Command
{
// Writing a blank line will not work with some clients
if (token.empty())
- user->WriteNumeric(292, ": ");
+ user->WriteNumeric(292, ' ');
else
- user->WriteNumeric(292, ":%s", token.c_str());
+ user->WriteNumeric(292, token);
}
- user->WriteNumeric(292, ": -");
- user->WriteNumeric(292, ":*** End of HELPOP");
+ user->WriteNumeric(292, " -");
+ user->WriteNumeric(292, "*** End of HELPOP");
}
return CMD_SUCCESS;
}
};
-class ModuleHelpop : public Module
+class ModuleHelpop : public Module, public Whois::EventListener
{
CommandHelpop cmd;
Helpop ho;
public:
ModuleHelpop()
- : cmd(this), ho(this)
+ : Whois::EventListener(this)
+ , cmd(this)
+ , ho(this)
{
}
@@ -139,11 +141,11 @@ class ModuleHelpop : public Module
helpop_map.swap(help);
}
- void OnWhois(User* src, User* dst) CXX11_OVERRIDE
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- if (dst->IsModeSet(ho))
+ if (whois.GetTarget()->IsModeSet(ho))
{
- ServerInstance->SendWhoisLine(src, dst, 310, dst->nick+" :is available for help.");
+ whois.SendLine(310, "is available for help.");
}
}
diff --git a/src/modules/m_hidechans.cpp b/src/modules/m_hidechans.cpp
index cd3ac2c26..08caae6b2 100644
--- a/src/modules/m_hidechans.cpp
+++ b/src/modules/m_hidechans.cpp
@@ -28,12 +28,14 @@ class HideChans : public SimpleUserModeHandler
HideChans(Module* Creator) : SimpleUserModeHandler(Creator, "hidechans", 'I') { }
};
-class ModuleHideChans : public Module
+class ModuleHideChans : public Module, public Whois::LineEventListener
{
bool AffectsOpers;
HideChans hm;
public:
- ModuleHideChans() : hm(this)
+ ModuleHideChans()
+ : Whois::LineEventListener(this)
+ , hm(this)
{
}
@@ -47,18 +49,18 @@ class ModuleHideChans : public Module
AffectsOpers = ServerInstance->Config->ConfValue("hidechans")->getBool("affectsopers");
}
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text) CXX11_OVERRIDE
+ ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
/* always show to self */
- if (user == dest)
+ if (whois.IsSelfWhois())
return MOD_RES_PASSTHRU;
/* don't touch anything except 319 */
- if (numeric != 319)
+ if (numeric.GetNumeric() != 319)
return MOD_RES_PASSTHRU;
/* don't touch if -I */
- if (!dest->IsModeSet(hm))
+ if (!whois.GetTarget()->IsModeSet(hm))
return MOD_RES_PASSTHRU;
/* if it affects opers, we don't care if they are opered */
@@ -66,7 +68,7 @@ class ModuleHideChans : public Module
return MOD_RES_DENY;
/* doesn't affect opers, sender is opered */
- if (user->HasPrivPermission("users/auspex"))
+ if (whois.GetSource()->HasPrivPermission("users/auspex"))
return MOD_RES_PASSTHRU;
/* user must be opered, boned. */
diff --git a/src/modules/m_hidelist.cpp b/src/modules/m_hidelist.cpp
new file mode 100644
index 000000000..97173c14b
--- /dev/null
+++ b/src/modules/m_hidelist.cpp
@@ -0,0 +1,87 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class ListWatcher : public ModeWatcher
+{
+ // Minimum rank required to view the list
+ const unsigned int minrank;
+
+ public:
+ ListWatcher(Module* mod, const std::string& modename, unsigned int rank)
+ : ModeWatcher(mod, modename, MODETYPE_CHANNEL)
+ , minrank(rank)
+ {
+ }
+
+ bool BeforeMode(User* user, User* destuser, Channel* chan, std::string& param, bool adding)
+ {
+ // Only handle listmode list requests
+ if (!param.empty())
+ return true;
+
+ // If the user requesting the list is a member of the channel see if they have the
+ // rank required to view the list
+ Membership* memb = chan->GetUser(user);
+ if ((memb) && (memb->getRank() >= minrank))
+ return true;
+
+ if (user->HasPrivPermission("channels/auspex"))
+ return true;
+
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, InspIRCd::Format("You do not have access to view the %s list", GetModeName().c_str()));
+ return false;
+ }
+};
+
+class ModuleHideList : public Module
+{
+ std::vector<ListWatcher*> watchers;
+
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ stdalgo::delete_all(watchers);
+ watchers.clear();
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("hidelist");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ std::string modename = tag->getString("mode");
+ // If rank is set to 0 everyone inside the channel can view the list,
+ // but non-members may not
+ unsigned int rank = tag->getInt("rank", HALFOP_VALUE, 0);
+ watchers.push_back(new ListWatcher(this, modename, rank));
+ }
+ }
+
+ ~ModuleHideList()
+ {
+ stdalgo::delete_all(watchers);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for hiding the list of listmodes", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleHideList)
diff --git a/src/modules/m_hideoper.cpp b/src/modules/m_hideoper.cpp
index d3c2bf444..3d8f8910c 100644
--- a/src/modules/m_hideoper.cpp
+++ b/src/modules/m_hideoper.cpp
@@ -26,18 +26,37 @@
class HideOper : public SimpleUserModeHandler
{
public:
+ size_t opercount;
+
HideOper(Module* Creator) : SimpleUserModeHandler(Creator, "hideoper", 'H')
+ , opercount(0)
{
oper = true;
}
+
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+ {
+ if (SimpleUserModeHandler::OnModeChange(source, dest, channel, parameter, adding) == MODEACTION_DENY)
+ return MODEACTION_DENY;
+
+ if (adding)
+ opercount++;
+ else
+ opercount--;
+
+ return MODEACTION_ALLOW;
+ }
};
-class ModuleHideOper : public Module
+class ModuleHideOper : public Module, public Whois::LineEventListener
{
HideOper hm;
+ bool active;
public:
ModuleHideOper()
- : hm(this)
+ : Whois::LineEventListener(this)
+ , hm(this)
+ , active(false)
{
}
@@ -46,35 +65,87 @@ class ModuleHideOper : public Module
return Version("Provides support for hiding oper status with user mode +H", VF_VENDOR);
}
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text) CXX11_OVERRIDE
+ void OnUserQuit(User* user, const std::string&, const std::string&) CXX11_OVERRIDE
+ {
+ if (user->IsModeSet(hm))
+ hm.opercount--;
+ }
+
+ ModResult OnNumeric(User* user, const Numeric::Numeric& numeric) CXX11_OVERRIDE
+ {
+ if (numeric.GetNumeric() != 252 || active || user->HasPrivPermission("users/auspex"))
+ return MOD_RES_PASSTHRU;
+
+ // If there are no visible operators then we shouldn't send the numeric.
+ size_t opercount = ServerInstance->Users->all_opers.size() - hm.opercount;
+ if (opercount)
+ {
+ active = true;
+ user->WriteNumeric(252, opercount, "operator(s) online");
+ active = false;
+ }
+ return MOD_RES_DENY;
+ }
+
+ ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
/* Dont display numeric 313 (RPL_WHOISOPER) if they have +H set and the
* person doing the WHOIS is not an oper
*/
- if (numeric != 313)
+ if (numeric.GetNumeric() != 313)
return MOD_RES_PASSTHRU;
- if (!dest->IsModeSet(hm))
+ if (!whois.GetTarget()->IsModeSet(hm))
return MOD_RES_PASSTHRU;
- if (!user->HasPrivPermission("users/auspex"))
+ if (!whois.GetSource()->HasPrivPermission("users/auspex"))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
- void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
+ ModResult OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
if (user->IsModeSet(hm) && !source->HasPrivPermission("users/auspex"))
{
+ // Hide the line completely if doing a "/who * o" query
+ if ((params.size() > 1) && (params[1].find('o') != std::string::npos))
+ return MOD_RES_DENY;
+
// hide the "*" that marks the user as an oper from the /WHO line
- std::string::size_type pos = line.find("*");
+ // #chan ident localhost insp22.test nick H@ :0 Attila
+ if (numeric.GetParams().size() < 6)
+ return MOD_RES_PASSTHRU;
+
+ std::string& param = numeric.GetParams()[5];
+ const std::string::size_type pos = param.find('*');
if (pos != std::string::npos)
- line.erase(pos, 1);
- // hide the line completely if doing a "/who * o" query
- if (params.size() > 1 && params[1].find('o') != std::string::npos)
- line.clear();
+ param.erase(pos, 1);
}
+ return MOD_RES_PASSTHRU;
+ }
+
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
+ {
+ if (stats.GetSymbol() != 'P')
+ return MOD_RES_PASSTHRU;
+
+ unsigned int count = 0;
+ const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+ for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
+ {
+ User* oper = *i;
+ if (!oper->server->IsULine() && (stats.GetSource()->IsOper() || !oper->IsModeSet(hm)))
+ {
+ LocalUser* lu = IS_LOCAL(oper);
+ stats.AddRow(249, oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
+ (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
+ count++;
+ }
+ }
+ stats.AddRow(249, ConvToStr(count)+" OPER(s)");
+
+ return MOD_RES_DENY;
}
};
diff --git a/src/modules/m_hostcycle.cpp b/src/modules/m_hostcycle.cpp
index d3646e899..621f06a27 100644
--- a/src/modules/m_hostcycle.cpp
+++ b/src/modules/m_hostcycle.cpp
@@ -19,28 +19,33 @@
#include "inspircd.h"
+#include "modules/cap.h"
class ModuleHostCycle : public Module
{
+ Cap::Reference chghostcap;
+
/** Send fake quit/join/mode messages for host or ident cycle.
*/
- static void DoHostCycle(User* user, const std::string& newident, const std::string& newhost, const char* quitmsg)
+ void DoHostCycle(User* user, const std::string& newident, const std::string& newhost, const char* quitmsg)
{
// GetFullHost() returns the original data at the time this function is called
const std::string quitline = ":" + user->GetFullHost() + " QUIT :" + quitmsg;
- already_sent_t silent_id = ++LocalUser::already_sent_id;
- already_sent_t seen_id = ++LocalUser::already_sent_id;
+ already_sent_t silent_id = ServerInstance->Users.NextAlreadySentId();
+ already_sent_t seen_id = ServerInstance->Users.NextAlreadySentId();
IncludeChanList include_chans(user->chans.begin(), user->chans.end());
std::map<User*,bool> exceptions;
FOREACH_MOD(OnBuildNeighborList, (user, include_chans, exceptions));
+ // Users shouldn't see themselves quitting when host cycling
+ exceptions.erase(user);
for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
{
LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting)
+ if ((u) && (!u->quitting) && (!chghostcap.get(u)))
{
if (i->second)
{
@@ -72,14 +77,16 @@ class ModuleHostCycle : public Module
modeline.append(" ").append(user->nick);
}
- const UserMembList* ulist = c->GetUsers();
- for (UserMembList::const_iterator j = ulist->begin(); j != ulist->end(); ++j)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator j = ulist.begin(); j != ulist.end(); ++j)
{
LocalUser* u = IS_LOCAL(j->first);
if (u == NULL || u == user)
continue;
if (u->already_sent == silent_id)
continue;
+ if (chghostcap.get(u))
+ continue;
if (u->already_sent != seen_id)
{
@@ -95,6 +102,11 @@ class ModuleHostCycle : public Module
}
public:
+ ModuleHostCycle()
+ : chghostcap(this, "chghost")
+ {
+ }
+
void OnChangeIdent(User* user, const std::string& newident) CXX11_OVERRIDE
{
DoHostCycle(user, newident, user->dhost, "Changing ident");
diff --git a/src/modules/m_httpd.cpp b/src/modules/m_httpd.cpp
index dda39afec..6055d1f77 100644
--- a/src/modules/m_httpd.cpp
+++ b/src/modules/m_httpd.cpp
@@ -29,8 +29,9 @@
class ModuleHttpServer;
static ModuleHttpServer* HttpModule;
-static bool claimed;
-static std::set<HttpServerSocket*> sockets;
+static insp::intrusive_list<HttpServerSocket> sockets;
+static Events::ModuleEventProvider* aclevprov;
+static Events::ModuleEventProvider* reqevprov;
/** HTTP socket states
*/
@@ -43,7 +44,7 @@ enum HttpState
/** A socket used for HTTP transport
*/
-class HttpServerSocket : public BufferedSocket
+class HttpServerSocket : public BufferedSocket, public Timer, public insp::intrusive_list_node<HttpServerSocket>
{
HttpState InternalState;
std::string ip;
@@ -56,17 +57,37 @@ class HttpServerSocket : public BufferedSocket
std::string uri;
std::string http_version;
- public:
- const time_t createtime;
+ /** True if this object is in the cull list
+ */
+ bool waitingcull;
- HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
- : BufferedSocket(newfd), ip(IP), postsize(0)
- , createtime(ServerInstance->Time())
+ bool Tick(time_t currtime) CXX11_OVERRIDE
+ {
+ AddToCull();
+ return false;
+ }
+
+ public:
+ HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server, unsigned int timeoutsec)
+ : BufferedSocket(newfd)
+ , Timer(timeoutsec)
+ , InternalState(HTTP_SERVE_WAIT_REQUEST)
+ , ip(IP)
+ , postsize(0)
+ , waitingcull(false)
{
- InternalState = HTTP_SERVE_WAIT_REQUEST;
+ if ((!via->iohookprovs.empty()) && (via->iohookprovs.back()))
+ {
+ via->iohookprovs.back()->OnAccept(this, client, server);
+ // IOHook may have errored
+ if (!getError().empty())
+ {
+ AddToCull();
+ return;
+ }
+ }
- if (via->iohookprov)
- via->iohookprov->OnAccept(this, client, server);
+ ServerInstance->Timers.AddTimer(this);
}
~HttpServerSocket()
@@ -76,7 +97,7 @@ class HttpServerSocket : public BufferedSocket
void OnError(BufferedSocketError) CXX11_OVERRIDE
{
- ServerInstance->GlobalCulls.AddItem(this);
+ AddToCull();
}
std::string Response(int response)
@@ -185,12 +206,7 @@ class HttpServerSocket : public BufferedSocket
WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
- time_t local = ServerInstance->Time();
- struct tm *timeinfo = gmtime(&local);
- char *date = asctime(timeinfo);
- date[strlen(date) - 1] = '\0';
- rheaders.CreateHeader("Date", date);
-
+ rheaders.CreateHeader("Date", InspIRCd::TimeString(ServerInstance->Time(), "%a, %d %b %Y %H:%M:%S GMT", true));
rheaders.CreateHeader("Server", INSPIRCD_BRANCH);
rheaders.SetHeader("Content-Length", ConvToStr(size));
@@ -262,7 +278,7 @@ class HttpServerSocket : public BufferedSocket
continue;
}
- std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
+ std::string cheader(reqbuffer, hbegin, hend - hbegin);
std::string::size_type fieldsep = cheader.find(':');
if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
@@ -293,7 +309,7 @@ class HttpServerSocket : public BufferedSocket
if (reqbuffer.length() >= postsize)
{
- postdata = reqbuffer.substr(0, postsize);
+ postdata.assign(reqbuffer, 0, postsize);
reqbuffer.erase(0, postsize);
}
else if (!reqbuffer.empty())
@@ -315,14 +331,14 @@ class HttpServerSocket : public BufferedSocket
{
InternalState = HTTP_SERVE_SEND_DATA;
- claimed = false;
- HTTPRequest acl((Module*)HttpModule, "httpd_acl", request_type, uri, &headers, this, ip, postdata);
- acl.Send();
- if (!claimed)
+ ModResult MOD_RESULT;
+ HTTPRequest acl(request_type, uri, &headers, this, ip, postdata);
+ FIRST_MOD_RESULT_CUSTOM(*aclevprov, HTTPACLEventListener, OnHTTPACLCheck, MOD_RESULT, (acl));
+ if (MOD_RESULT != MOD_RES_DENY)
{
- HTTPRequest url((Module*)HttpModule, "httpd_url", request_type, uri, &headers, this, ip, postdata);
- url.Send();
- if (!claimed)
+ HTTPRequest url(request_type, uri, &headers, this, ip, postdata);
+ FIRST_MOD_RESULT_CUSTOM(*reqevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (url));
+ if (MOD_RESULT == MOD_RES_PASSTHRU)
{
SendHTTPError(404);
}
@@ -334,6 +350,16 @@ class HttpServerSocket : public BufferedSocket
SendHeaders(n->str().length(), response, *hheaders);
WriteData(n->str());
}
+
+ void AddToCull()
+ {
+ if (waitingcull)
+ return;
+
+ waitingcull = true;
+ Close();
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
};
class HTTPdAPIImpl : public HTTPdAPIBase
@@ -346,21 +372,25 @@ class HTTPdAPIImpl : public HTTPdAPIBase
void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
{
- claimed = true;
resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
}
};
class ModuleHttpServer : public Module
{
- std::vector<HttpServerSocket *> httpsocks;
HTTPdAPIImpl APIImpl;
unsigned int timeoutsec;
+ Events::ModuleEventProvider acleventprov;
+ Events::ModuleEventProvider reqeventprov;
public:
ModuleHttpServer()
: APIImpl(this)
+ , acleventprov(this, "event/http-acl")
+ , reqeventprov(this, "event/http-request")
{
+ aclevprov = &acleventprov;
+ reqevprov = &reqeventprov;
}
void init() CXX11_OVERRIDE
@@ -371,7 +401,7 @@ class ModuleHttpServer : public Module
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
- timeoutsec = tag->getInt("timeout");
+ timeoutsec = tag->getInt("timeout", 10, 1);
}
ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
@@ -381,21 +411,17 @@ class ModuleHttpServer : public Module
int port;
std::string incomingip;
irc::sockets::satoap(*client, incomingip, port);
- sockets.insert(new HttpServerSocket(nfd, incomingip, from, client, server));
+ sockets.push_front(new HttpServerSocket(nfd, incomingip, from, client, server, timeoutsec));
return MOD_RES_ALLOW;
}
- void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
+ void OnUnloadModule(Module* mod)
{
- if (!timeoutsec)
- return;
-
- time_t oldest_allowed = curtime - timeoutsec;
- for (std::set<HttpServerSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); )
+ for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); )
{
HttpServerSocket* sock = *i;
++i;
- if (sock->createtime < oldest_allowed)
+ if (sock->GetModHook(mod))
{
sock->cull();
delete sock;
@@ -405,13 +431,10 @@ class ModuleHttpServer : public Module
CullResult cull() CXX11_OVERRIDE
{
- std::set<HttpServerSocket*> local;
- local.swap(sockets);
- for (std::set<HttpServerSocket*>::const_iterator i = local.begin(); i != local.end(); ++i)
+ for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
{
HttpServerSocket* sock = *i;
- sock->cull();
- delete sock;
+ sock->AddToCull();
}
return Module::cull();
}
diff --git a/src/modules/m_httpd_acl.cpp b/src/modules/m_httpd_acl.cpp
index 74d25deba..866fa0e86 100644
--- a/src/modules/m_httpd_acl.cpp
+++ b/src/modules/m_httpd_acl.cpp
@@ -20,7 +20,6 @@
#include "inspircd.h"
#include "modules/httpd.h"
-#include "protocol.h"
class HTTPACL
{
@@ -37,7 +36,7 @@ class HTTPACL
blacklist(set_blacklist) { }
};
-class ModuleHTTPAccessList : public Module
+class ModuleHTTPAccessList : public Module, public HTTPACLEventListener
{
std::string stylesheet;
std::vector<HTTPACL> acl_list;
@@ -45,7 +44,8 @@ class ModuleHTTPAccessList : public Module
public:
ModuleHTTPAccessList()
- : API(this)
+ : HTTPACLEventListener(this)
+ , API(this)
{
}
@@ -105,12 +105,10 @@ class ModuleHTTPAccessList : public Module
API->SendResponse(response);
}
- void OnEvent(Event& event) CXX11_OVERRIDE
+ bool IsAccessAllowed(HTTPRequest* http)
{
- if (event.id == "httpd_acl")
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd acl event");
- HTTPRequest* http = (HTTPRequest*)&event;
for (std::vector<HTTPACL>::const_iterator this_acl = acl_list.begin(); this_acl != acl_list.end(); ++this_acl)
{
@@ -129,7 +127,7 @@ class ModuleHTTPAccessList : public Module
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to blacklisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)",
http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), entry.c_str());
BlockAccess(http, 403);
- return;
+ return false;
}
}
}
@@ -151,7 +149,7 @@ class ModuleHTTPAccessList : public Module
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to whitelisted resource %s (matched by pattern %s) from ip %s (Not in whitelist)",
http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str());
BlockAccess(http, 403);
- return;
+ return false;
}
}
if (!this_acl->password.empty() && !this_acl->username.empty())
@@ -187,7 +185,7 @@ class ModuleHTTPAccessList : public Module
if (user == this_acl->username && pass == this_acl->password)
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "HTTP authorization: password and username match");
- return;
+ return true;
}
else
/* Invalid password */
@@ -206,13 +204,22 @@ class ModuleHTTPAccessList : public Module
/* No password given at all, access denied */
BlockAccess(http, 401, "WWW-Authenticate", "Basic realm=\"Restricted Object\"");
}
+ return false;
}
/* A path may only match one ACL (the first it finds in the config file) */
- return;
+ break;
}
}
}
+ return true;
+ }
+
+ ModResult OnHTTPACLCheck(HTTPRequest& req) CXX11_OVERRIDE
+ {
+ if (IsAccessAllowed(&req))
+ return MOD_RES_PASSTHRU;
+ return MOD_RES_DENY;
}
Version GetVersion() CXX11_OVERRIDE
diff --git a/src/modules/m_httpd_config.cpp b/src/modules/m_httpd_config.cpp
index 8333d9f9c..6fd7f4050 100644
--- a/src/modules/m_httpd_config.cpp
+++ b/src/modules/m_httpd_config.cpp
@@ -20,15 +20,15 @@
#include "inspircd.h"
#include "modules/httpd.h"
-#include "protocol.h"
-class ModuleHttpConfig : public Module
+class ModuleHttpConfig : public Module, public HTTPRequestEventListener
{
HTTPdAPI API;
public:
ModuleHttpConfig()
- : API(this)
+ : HTTPRequestEventListener(this)
+ , API(this)
{
}
@@ -66,14 +66,12 @@ class ModuleHttpConfig : public Module
return ret;
}
- void OnEvent(Event& event) CXX11_OVERRIDE
+ ModResult HandleRequest(HTTPRequest* http)
{
std::stringstream data("");
- if (event.id == "httpd_url")
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd event");
- HTTPRequest* http = (HTTPRequest*)&event;
if ((http->GetURI() == "/config") || (http->GetURI() == "/config/"))
{
@@ -97,8 +95,15 @@ class ModuleHttpConfig : public Module
response.headers.SetHeader("X-Powered-By", MODNAME);
response.headers.SetHeader("Content-Type", "text/html");
API->SendResponse(response);
+ return MOD_RES_DENY; // Handled
}
}
+ return MOD_RES_PASSTHRU;
+ }
+
+ ModResult OnHTTPRequest(HTTPRequest& req) CXX11_OVERRIDE
+ {
+ return HandleRequest(&req);
}
Version GetVersion() CXX11_OVERRIDE
diff --git a/src/modules/m_httpd_stats.cpp b/src/modules/m_httpd_stats.cpp
index 2dcf1e1cf..62ae0c204 100644
--- a/src/modules/m_httpd_stats.cpp
+++ b/src/modules/m_httpd_stats.cpp
@@ -24,16 +24,16 @@
#include "inspircd.h"
#include "modules/httpd.h"
#include "xline.h"
-#include "protocol.h"
-class ModuleHttpStats : public Module
+class ModuleHttpStats : public Module, public HTTPRequestEventListener
{
- static std::map<char, char const*> const &entities;
+ static const insp::flat_map<char, char const*>& entities;
HTTPdAPI API;
public:
ModuleHttpStats()
- : API(this)
+ : HTTPRequestEventListener(this)
+ , API(this)
{
}
@@ -44,7 +44,7 @@ class ModuleHttpStats : public Module
for (std::string::const_iterator x = str.begin(); x != str.end(); ++x)
{
- std::map<char, char const*>::const_iterator it = entities.find(*x);
+ insp::flat_map<char, char const*>::const_iterator it = entities.find(*x);
if (it != entities.end())
{
@@ -88,14 +88,12 @@ class ModuleHttpStats : public Module
data << "</metadata>";
}
- void OnEvent(Event& event) CXX11_OVERRIDE
+ ModResult HandleRequest(HTTPRequest* http)
{
std::stringstream data("");
- if (event.id == "httpd_url")
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd event");
- HTTPRequest* http = (HTTPRequest*)&event;
if ((http->GetURI() == "/stats") || (http->GetURI() == "/stats/"))
{
@@ -108,19 +106,15 @@ class ModuleHttpStats : public Module
data << "<channelcount>" << ServerInstance->GetChans().size() << "</channelcount>";
data << "<opercount>" << ServerInstance->Users->all_opers.size() << "</opercount>";
data << "<socketcount>" << (SocketEngine::GetUsedFds()) << "</socketcount><socketmax>" << SocketEngine::GetMaxFds() << "</socketmax><socketengine>" INSPIRCD_SOCKETENGINE_NAME "</socketengine>";
-
- time_t current_time = 0;
- current_time = ServerInstance->Time();
- time_t server_uptime = current_time - ServerInstance->startup_time;
- struct tm* stime;
- stime = gmtime(&server_uptime);
- data << "<uptime><days>" << stime->tm_yday << "</days><hours>" << stime->tm_hour << "</hours><mins>" << stime->tm_min << "</mins><secs>" << stime->tm_sec << "</secs><boot_time_t>" << ServerInstance->startup_time << "</boot_time_t></uptime>";
+ data << "<uptime><boot_time_t>" << ServerInstance->startup_time << "</boot_time_t></uptime>";
data << "<isupport>";
- const std::vector<std::string>& isupport = ServerInstance->ISupport.GetLines();
- for (std::vector<std::string>::const_iterator it = isupport.begin(); it != isupport.end(); it++)
+ const std::vector<Numeric::Numeric>& isupport = ServerInstance->ISupport.GetLines();
+ for (std::vector<Numeric::Numeric>::const_iterator i = isupport.begin(); i != isupport.end(); ++i)
{
- data << Sanitize(*it) << std::endl;
+ const Numeric::Numeric& num = *i;
+ for (std::vector<std::string>::const_iterator j = num.GetParams().begin(); j != num.GetParams().end()-1; ++j)
+ data << "<token>" << Sanitize(*j) << "</token>" << std::endl;
}
data << "</isupport></general><xlines>";
std::vector<std::string> xltypes = ServerInstance->XLines->GetAllTypes();
@@ -156,16 +150,16 @@ class ModuleHttpStats : public Module
Channel* c = i->second;
data << "<channel>";
- data << "<usercount>" << c->GetUsers()->size() << "</usercount><channelname>" << Sanitize(c->name) << "</channelname>";
+ data << "<usercount>" << c->GetUsers().size() << "</usercount><channelname>" << Sanitize(c->name) << "</channelname>";
data << "<channeltopic>";
data << "<topictext>" << Sanitize(c->topic) << "</topictext>";
data << "<setby>" << Sanitize(c->setby) << "</setby>";
data << "<settime>" << c->topicset << "</settime>";
data << "</channeltopic>";
data << "<channelmodes>" << Sanitize(c->ChanModes(true)) << "</channelmodes>";
- const UserMembList* ulist = c->GetUsers();
- for (UserMembCIter x = ulist->begin(); x != ulist->end(); ++x)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator x = ulist.begin(); x != ulist.end(); ++x)
{
Membership* memb = x->second;
data << "<channelmember><uid>" << memb->user->uuid << "</uid><privs>"
@@ -190,12 +184,12 @@ class ModuleHttpStats : public Module
data << "<user>";
data << "<nickname>" << u->nick << "</nickname><uuid>" << u->uuid << "</uuid><realhost>"
<< u->host << "</realhost><displayhost>" << u->dhost << "</displayhost><gecos>"
- << Sanitize(u->fullname) << "</gecos><server>" << u->server << "</server>";
+ << Sanitize(u->fullname) << "</gecos><server>" << u->server->GetName() << "</server>";
if (u->IsAway())
data << "<away>" << Sanitize(u->awaymsg) << "</away><awaytime>" << u->awaytime << "</awaytime>";
if (u->IsOper())
data << "<opertype>" << Sanitize(u->oper->name) << "</opertype>";
- data << "<modes>" << u->FormatModes() << "</modes><ident>" << Sanitize(u->ident) << "</ident>";
+ data << "<modes>" << u->GetModeLetters().substr(1) << "</modes><ident>" << Sanitize(u->ident) << "</ident>";
LocalUser* lu = IS_LOCAL(u);
if (lu)
data << "<port>" << lu->GetServerPort() << "</port><servaddr>"
@@ -217,7 +211,7 @@ class ModuleHttpStats : public Module
data << "<server>";
data << "<servername>" << b->servername << "</servername>";
data << "<parentname>" << b->parentname << "</parentname>";
- data << "<gecos>" << b->gecos << "</gecos>";
+ data << "<gecos>" << Sanitize(b->gecos) << "</gecos>";
data << "<usercount>" << b->usercount << "</usercount>";
// This is currently not implemented, so, commented out.
// data << "<opercount>" << b->opercount << "</opercount>";
@@ -225,15 +219,30 @@ class ModuleHttpStats : public Module
data << "</server>";
}
- data << "</serverlist></inspircdstats>";
+ data << "</serverlist><commandlist>";
+
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
+ {
+ data << "<command><name>" << i->second->name << "</name><usecount>" << i->second->use_count << "</usecount></command>";
+ }
+
+ data << "</commandlist></inspircdstats>";
/* Send the document back to m_httpd */
HTTPDocumentResponse response(this, *http, &data, 200);
response.headers.SetHeader("X-Powered-By", MODNAME);
response.headers.SetHeader("Content-Type", "text/xml");
API->SendResponse(response);
+ return MOD_RES_DENY; // Handled
}
}
+ return MOD_RES_PASSTHRU;
+ }
+
+ ModResult OnHTTPRequest(HTTPRequest& req) CXX11_OVERRIDE
+ {
+ return HandleRequest(&req);
}
Version GetVersion() CXX11_OVERRIDE
@@ -242,9 +251,9 @@ class ModuleHttpStats : public Module
}
};
-static std::map<char, char const*> const &init_entities()
+static const insp::flat_map<char, char const*>& init_entities()
{
- static std::map<char, char const*> entities;
+ static insp::flat_map<char, char const*> entities;
entities['<'] = "lt";
entities['>'] = "gt";
entities['&'] = "amp";
@@ -252,6 +261,6 @@ static std::map<char, char const*> const &init_entities()
return entities;
}
-std::map<char, char const*> const &ModuleHttpStats::entities = init_entities ();
+const insp::flat_map<char, char const*>& ModuleHttpStats::entities = init_entities();
MODULE_INIT(ModuleHttpStats)
diff --git a/src/modules/m_ident.cpp b/src/modules/m_ident.cpp
index 3e87b8c5a..0e5aa43ae 100644
--- a/src/modules/m_ident.cpp
+++ b/src/modules/m_ident.cpp
@@ -140,9 +140,8 @@ class IdentRequestSocket : public EventHandler
}
}
- void OnConnected()
+ void OnEventHandlerWrite() CXX11_OVERRIDE
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnConnected()");
SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
char req[32];
@@ -163,30 +162,6 @@ class IdentRequestSocket : public EventHandler
done = true;
}
- void HandleEvent(EventType et, int errornum = 0)
- {
- switch (et)
- {
- case EVENT_READ:
- /* fd readable event, received ident response */
- ReadResponse();
- break;
- case EVENT_WRITE:
- /* fd writeable event, successfully connected! */
- OnConnected();
- break;
- case EVENT_ERROR:
- /* fd error event, ohshi- */
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "EVENT_ERROR");
- /* We *must* Close() here immediately or we get a
- * huge storm of EVENT_ERROR events!
- */
- Close();
- done = true;
- break;
- }
- }
-
void Close()
{
/* Remove ident socket from engine, and close it, but dont detatch it
@@ -204,7 +179,7 @@ class IdentRequestSocket : public EventHandler
return done;
}
- void ReadResponse()
+ void OnEventHandlerRead() CXX11_OVERRIDE
{
/* We don't really need to buffer for incomplete replies here, since IDENT replies are
* extremely short - there is *no* sane reason it'd be in more than one packet
@@ -264,6 +239,12 @@ class IdentRequestSocket : public EventHandler
}
}
+ void OnEventHandlerError(int errornum) CXX11_OVERRIDE
+ {
+ Close();
+ done = true;
+ }
+
CullResult cull() CXX11_OVERRIDE
{
Close();
@@ -277,7 +258,8 @@ class ModuleIdent : public Module
bool NoLookupPrefix;
SimpleExtItem<IdentRequestSocket, stdalgo::culldeleter> ext;
public:
- ModuleIdent() : ext("ident_socket", this)
+ ModuleIdent()
+ : ext("ident_socket", ExtensionItem::EXT_USER, this)
{
}
diff --git a/src/modules/m_ircv3.cpp b/src/modules/m_ircv3.cpp
index 5cb2ab6b1..9e94e556d 100644
--- a/src/modules/m_ircv3.cpp
+++ b/src/modules/m_ircv3.cpp
@@ -19,58 +19,20 @@
#include "inspircd.h"
#include "modules/account.h"
#include "modules/cap.h"
+#include "modules/ircv3.h"
-class ModuleIRCv3 : public Module
+class ModuleIRCv3 : public Module, public AccountEventListener
{
- GenericCap cap_accountnotify;
- GenericCap cap_awaynotify;
- GenericCap cap_extendedjoin;
- bool accountnotify;
- bool awaynotify;
- bool extendedjoin;
+ Cap::Capability cap_accountnotify;
+ Cap::Capability cap_awaynotify;
+ Cap::Capability cap_extendedjoin;
CUList last_excepts;
- void WriteNeighboursWithExt(User* user, const std::string& line, const LocalIntExt& ext)
- {
- IncludeChanList chans(user->chans.begin(), user->chans.end());
-
- std::map<User*, bool> exceptions;
- FOREACH_MOD(OnBuildNeighborList, (user, chans, exceptions));
-
- // Send it to all local users who were explicitly marked as neighbours by modules and have the required ext
- for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if ((u) && (i->second) && (ext.get(u)))
- u->Write(line);
- }
-
- // Now consider sending it to all other users who has at least a common channel with the user
- std::set<User*> already_sent;
- for (IncludeChanList::const_iterator i = chans.begin(); i != chans.end(); ++i)
- {
- const UserMembList* userlist = (*i)->chan->GetUsers();
- for (UserMembList::const_iterator m = userlist->begin(); m != userlist->end(); ++m)
- {
- /*
- * Send the line if the channel member in question meets all of the following criteria:
- * - local
- * - not the user who is doing the action (i.e. whose channels we're iterating)
- * - has the given extension
- * - not on the except list built by modules
- * - we haven't sent the line to the member yet
- *
- */
- LocalUser* member = IS_LOCAL(m->first);
- if ((member) && (member != user) && (ext.get(member)) && (exceptions.find(member) == exceptions.end()) && (already_sent.insert(member).second))
- member->Write(line);
- }
- }
- }
-
public:
- ModuleIRCv3() : cap_accountnotify(this, "account-notify"),
+ ModuleIRCv3()
+ : AccountEventListener(this)
+ , cap_accountnotify(this, "account-notify"),
cap_awaynotify(this, "away-notify"),
cap_extendedjoin(this, "extended-join")
{
@@ -79,47 +41,32 @@ class ModuleIRCv3 : public Module
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
- accountnotify = conf->getBool("accountnotify", true);
- awaynotify = conf->getBool("awaynotify", true);
- extendedjoin = conf->getBool("extendedjoin", true);
+ cap_accountnotify.SetActive(conf->getBool("accountnotify", true));
+ cap_awaynotify.SetActive(conf->getBool("awaynotify", true));
+ cap_extendedjoin.SetActive(conf->getBool("extendedjoin", true));
}
- void OnEvent(Event& ev) CXX11_OVERRIDE
+ void OnAccountChange(User* user, const std::string& newaccount) CXX11_OVERRIDE
{
- if (awaynotify)
- cap_awaynotify.HandleEvent(ev);
- if (extendedjoin)
- cap_extendedjoin.HandleEvent(ev);
-
- if (accountnotify)
- {
- cap_accountnotify.HandleEvent(ev);
-
- if (ev.id == "account_login")
- {
- AccountEvent* ae = static_cast<AccountEvent*>(&ev);
-
- // :nick!user@host ACCOUNT account
- // or
- // :nick!user@host ACCOUNT *
- std::string line = ":" + ae->user->GetFullHost() + " ACCOUNT ";
- if (ae->account.empty())
- line += "*";
- else
- line += std::string(ae->account);
-
- WriteNeighboursWithExt(ae->user, line, cap_accountnotify.ext);
- }
- }
+ // :nick!user@host ACCOUNT account
+ // or
+ // :nick!user@host ACCOUNT *
+ std::string line = ":" + user->GetFullHost() + " ACCOUNT ";
+ if (newaccount.empty())
+ line += "*";
+ else
+ line += newaccount;
+
+ IRCv3::WriteNeighborsWithCap(user, line, cap_accountnotify);
}
void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
{
// Remember who is not going to see the JOIN because of other modules
- if ((awaynotify) && (memb->user->IsAway()))
+ if ((cap_awaynotify.IsActive()) && (memb->user->IsAway()))
last_excepts = excepts;
- if (!extendedjoin)
+ if (!cap_extendedjoin.IsActive())
return;
/*
@@ -134,12 +81,12 @@ class ModuleIRCv3 : public Module
std::string line;
std::string mode;
- const UserMembList* userlist = memb->chan->GetUsers();
- for (UserMembCIter it = userlist->begin(); it != userlist->end(); ++it)
+ const Channel::MemberMap& userlist = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator it = userlist.begin(); it != userlist.end(); ++it)
{
// Send the extended join line if the current member is local, has the extended-join cap and isn't excepted
User* member = IS_LOCAL(it->first);
- if ((member) && (cap_extendedjoin.ext.get(member)) && (excepts.find(member) == excepts.end()))
+ if ((member) && (cap_extendedjoin.get(member)) && (excepts.find(member) == excepts.end()))
{
// Construct the lines we're going to send if we haven't constructed them already
if (line.empty())
@@ -188,7 +135,7 @@ class ModuleIRCv3 : public Module
ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE
{
- if (awaynotify)
+ if (cap_awaynotify.IsActive())
{
// Going away: n!u@h AWAY :reason
// Back from away: n!u@h AWAY
@@ -196,24 +143,24 @@ class ModuleIRCv3 : public Module
if (!awaymsg.empty())
line += " :" + awaymsg;
- WriteNeighboursWithExt(user, line, cap_awaynotify.ext);
+ IRCv3::WriteNeighborsWithCap(user, line, cap_awaynotify);
}
return MOD_RES_PASSTHRU;
}
void OnPostJoin(Membership *memb) CXX11_OVERRIDE
{
- if ((!awaynotify) || (!memb->user->IsAway()))
+ if ((!cap_awaynotify.IsActive()) || (!memb->user->IsAway()))
return;
std::string line = ":" + memb->user->GetFullHost() + " AWAY :" + memb->user->awaymsg;
- const UserMembList* userlist = memb->chan->GetUsers();
- for (UserMembCIter it = userlist->begin(); it != userlist->end(); ++it)
+ const Channel::MemberMap& userlist = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator it = userlist.begin(); it != userlist.end(); ++it)
{
// Send the away notify line if the current member is local, has the away-notify cap and isn't excepted
User* member = IS_LOCAL(it->first);
- if ((member) && (cap_awaynotify.ext.get(member)) && (last_excepts.find(member) == last_excepts.end()))
+ if ((member) && (cap_awaynotify.get(member)) && (last_excepts.find(member) == last_excepts.end()) && (it->second != memb))
{
member->Write(line);
}
diff --git a/src/modules/m_ircv3_capnotify.cpp b/src/modules/m_ircv3_capnotify.cpp
new file mode 100644
index 000000000..93c30df12
--- /dev/null
+++ b/src/modules/m_ircv3_capnotify.cpp
@@ -0,0 +1,149 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+#include "modules/reload.h"
+
+class CapNotify : public Cap::Capability
+{
+ bool OnRequest(LocalUser* user, bool add) CXX11_OVERRIDE
+ {
+ // Users using the negotiation protocol v3.2 or newer may not turn off cap-notify
+ if ((!add) && (GetProtocol(user) != Cap::CAP_LEGACY))
+ return false;
+ return true;
+ }
+
+ bool OnList(LocalUser* user) CXX11_OVERRIDE
+ {
+ // If the client supports 3.2 enable cap-notify for them
+ if (GetProtocol(user) != Cap::CAP_LEGACY)
+ set(user, true);
+ return true;
+ }
+
+ public:
+ CapNotify(Module* mod)
+ : Cap::Capability(mod, "cap-notify")
+ {
+ }
+};
+
+class ModuleIRCv3CapNotify : public Module, public Cap::EventListener, public ReloadModule::EventListener
+{
+ CapNotify capnotify;
+ std::string reloadedmod;
+ std::vector<std::string> reloadedcaps;
+
+ void Send(const std::string& capname, Cap::Capability* cap, bool add)
+ {
+ std::string msg = (add ? "NEW :" : "DEL :");
+ msg.append(capname);
+ std::string msgwithval = msg;
+ msgwithval.push_back('=');
+ std::string::size_type msgpos = msgwithval.size();
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* user = *i;
+ if (!capnotify.get(user))
+ continue;
+
+ // If the cap is being added and the client supports cap values then show the value, if any
+ if ((add) && (capnotify.GetProtocol(user) != Cap::CAP_LEGACY))
+ {
+ const std::string* capvalue = cap->GetValue(user);
+ if ((capvalue) && (!capvalue->empty()))
+ {
+ msgwithval.append(*capvalue);
+ user->WriteCommand("CAP", msgwithval);
+ msgwithval.erase(msgpos);
+ continue;
+ }
+ }
+ user->WriteCommand("CAP", msg);
+ }
+ }
+
+ public:
+ ModuleIRCv3CapNotify()
+ : Cap::EventListener(this)
+ , ReloadModule::EventListener(this)
+ , capnotify(this)
+ {
+ }
+
+ void OnCapAddDel(Cap::Capability* cap, bool add) CXX11_OVERRIDE
+ {
+ if (cap->creator == this)
+ return;
+
+ if (cap->creator->ModuleSourceFile == reloadedmod)
+ {
+ if (!add)
+ reloadedcaps.push_back(cap->GetName());
+ return;
+ }
+ Send(cap->GetName(), cap, add);
+ }
+
+ void OnCapValueChange(Cap::Capability* cap) CXX11_OVERRIDE
+ {
+ // The value of a cap has changed, send CAP DEL and CAP NEW with the new value
+ Send(cap->GetName(), cap, false);
+ Send(cap->GetName(), cap, true);
+ }
+
+ void OnReloadModuleSave(Module* mod, ReloadModule::CustomData& cd) CXX11_OVERRIDE
+ {
+ if (mod == this)
+ return;
+ reloadedmod = mod->ModuleSourceFile;
+ // Request callback when reload is complete
+ cd.add(this, NULL);
+ }
+
+ void OnReloadModuleRestore(Module* mod, void* data) CXX11_OVERRIDE
+ {
+ // Reloading can change the set of caps provided by a module so assuming that if the reload succeded all
+ // caps that the module previously provided are available or all were lost if the reload failed is wrong.
+ // Instead, we verify the availability of each cap individually.
+ dynamic_reference_nocheck<Cap::Manager> capmanager(this, "capmanager");
+ if (capmanager)
+ {
+ for (std::vector<std::string>::const_iterator i = reloadedcaps.begin(); i != reloadedcaps.end(); ++i)
+ {
+ const std::string& capname = *i;
+ if (!capmanager->Find(capname))
+ Send(capname, NULL, false);
+ }
+ }
+ reloadedmod.clear();
+ reloadedcaps.clear();
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the cap-notify IRCv3.2 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3CapNotify)
diff --git a/src/modules/m_ircv3_chghost.cpp b/src/modules/m_ircv3_chghost.cpp
new file mode 100644
index 000000000..af3503108
--- /dev/null
+++ b/src/modules/m_ircv3_chghost.cpp
@@ -0,0 +1,57 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+#include "modules/ircv3.h"
+
+class ModuleIRCv3ChgHost : public Module
+{
+ Cap::Capability cap;
+
+ void DoChgHost(User* user, const std::string& ident, const std::string& host)
+ {
+ std::string line(1, ':');
+ line.append(user->GetFullHost()).append(" CHGHOST ").append(ident).append(1, ' ').append(host);
+ IRCv3::WriteNeighborsWithCap(user, line, cap);
+ }
+
+ public:
+ ModuleIRCv3ChgHost()
+ : cap(this, "chghost")
+ {
+ }
+
+ void OnChangeIdent(User* user, const std::string& newident) CXX11_OVERRIDE
+ {
+ DoChgHost(user, newident, user->dhost);
+ }
+
+ void OnChangeHost(User* user, const std::string& newhost) CXX11_OVERRIDE
+ {
+ DoChgHost(user, user->ident, newhost);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the chghost IRCv3.2 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3ChgHost)
diff --git a/src/modules/m_ircv3_echomessage.cpp b/src/modules/m_ircv3_echomessage.cpp
new file mode 100644
index 000000000..8773d7187
--- /dev/null
+++ b/src/modules/m_ircv3_echomessage.cpp
@@ -0,0 +1,70 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2013-2015 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+
+static const char* MessageTypeStringSp[] = { "PRIVMSG ", "NOTICE " };
+
+class ModuleIRCv3EchoMessage : public Module
+{
+ Cap::Capability cap;
+
+ public:
+ ModuleIRCv3EchoMessage()
+ : cap(this, "echo-message")
+ {
+ }
+
+ void OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
+ {
+ if (!cap.get(user))
+ return;
+
+ std::string msg = MessageTypeStringSp[msgtype];
+ if (target_type == TYPE_USER)
+ {
+ User* destuser = static_cast<User*>(dest);
+ msg.append(destuser->nick);
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ if (status)
+ msg.push_back(status);
+
+ Channel* chan = static_cast<Channel*>(dest);
+ msg.append(chan->name);
+ }
+ else
+ {
+ const char* servername = static_cast<const char*>(dest);
+ msg.append(servername);
+ }
+ msg.append(" :").append(text);
+ user->WriteFrom(user, msg);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the echo-message IRCv3.2 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3EchoMessage)
diff --git a/src/modules/m_ircv3_invitenotify.cpp b/src/modules/m_ircv3_invitenotify.cpp
new file mode 100644
index 000000000..3783ff33c
--- /dev/null
+++ b/src/modules/m_ircv3_invitenotify.cpp
@@ -0,0 +1,68 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+
+class ModuleIRCv3InviteNotify : public Module
+{
+ Cap::Capability cap;
+
+ public:
+ ModuleIRCv3InviteNotify()
+ : cap(this, "invite-notify")
+ {
+ }
+
+ void OnUserInvite(User* source, User* dest, Channel* chan, time_t expiry, unsigned int notifyrank, CUList& notifyexcepts) CXX11_OVERRIDE
+ {
+ std::string msg = "INVITE ";
+ msg.append(dest->nick).append(1, ' ').append(chan->name);
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ User* user = i->first;
+ // Skip members who don't use this extension or were excluded by other modules
+ if ((!cap.get(user)) || (notifyexcepts.count(user)))
+ continue;
+
+ Membership* memb = i->second;
+ // Check whether the member has a high enough rank to see the notification
+ if (memb->getRank() < notifyrank)
+ continue;
+
+ // Send and add the user to the exceptions so they won't get the NOTICE invite announcement message
+ user->WriteFrom(source, msg);
+ notifyexcepts.insert(user);
+ }
+ }
+
+ void Prioritize() CXX11_OVERRIDE
+ {
+ // Prioritize after all modules to see all excepted users
+ ServerInstance->Modules.SetPriority(this, I_OnUserInvite, PRIORITY_LAST);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the invite-notify IRCv3.2 extension", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleIRCv3InviteNotify)
diff --git a/src/modules/m_joinflood.cpp b/src/modules/m_joinflood.cpp
index 52802f168..077ceff52 100644
--- a/src/modules/m_joinflood.cpp
+++ b/src/modules/m_joinflood.cpp
@@ -23,6 +23,9 @@
#include "inspircd.h"
+// The number of seconds the channel will be closed for.
+static unsigned int duration;
+
/** Holds settings and state associated with channel mode +j
*/
class joinfloodsettings
@@ -71,7 +74,7 @@ class joinfloodsettings
void lock()
{
- unlocktime = ServerInstance->Time() + 60;
+ unlocktime = ServerInstance->Time() + duration;
}
bool operator==(const joinfloodsettings& other) const
@@ -95,7 +98,7 @@ class JoinFlood : public ParamMode<JoinFlood, SimpleExtItem<joinfloodsettings> >
std::string::size_type colon = parameter.find(':');
if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
{
- source->WriteNumeric(608, "%s :Invalid flood parameter",channel->name.c_str());
+ source->WriteNumeric(608, channel->name, "Invalid flood parameter");
return MODEACTION_DENY;
}
@@ -104,7 +107,7 @@ class JoinFlood : public ParamMode<JoinFlood, SimpleExtItem<joinfloodsettings> >
unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
if ((njoins<1) || (nsecs<1))
{
- source->WriteNumeric(608, "%s :Invalid flood parameter",channel->name.c_str());
+ source->WriteNumeric(608, channel->name, "Invalid flood parameter");
return MODEACTION_DENY;
}
@@ -129,6 +132,12 @@ class ModuleJoinFlood : public Module
{
}
+ void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("joinflood");
+ duration = tag->getDuration("duration", 60, 10, 600);
+ }
+
ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
@@ -136,7 +145,7 @@ class ModuleJoinFlood : public Module
joinfloodsettings *f = jf.ext.get(chan);
if (f && f->islocked())
{
- user->WriteNumeric(609, "%s :This channel is temporarily unavailable (+j). Please try again later.",chan->name.c_str());
+ user->WriteNumeric(609, chan->name, "This channel is temporarily unavailable (+j). Please try again later.");
return MOD_RES_DENY;
}
}
@@ -159,7 +168,7 @@ class ModuleJoinFlood : public Module
{
f->clear();
f->lock();
- memb->chan->WriteChannelWithServ((char*)ServerInstance->Config->ServerName.c_str(), "NOTICE %s :This channel has been closed to new users for 60 seconds because there have been more than %d joins in %d seconds.", memb->chan->name.c_str(), f->joins, f->secs);
+ memb->chan->WriteNotice(InspIRCd::Format("This channel has been closed to new users for %u seconds because there have been more than %d joins in %d seconds.", duration, f->joins, f->secs));
}
}
}
diff --git a/src/modules/m_jumpserver.cpp b/src/modules/m_jumpserver.cpp
index 523500e50..89391c8a4 100644
--- a/src/modules/m_jumpserver.cpp
+++ b/src/modules/m_jumpserver.cpp
@@ -108,12 +108,15 @@ class CommandJumpserver : public Command
if (redirect_all_immediately)
{
/* Redirect everyone but the oper sending the command */
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); )
{
+ // Quitting the user removes it from the list
LocalUser* t = *i;
+ ++i;
if (!t->IsOper())
{
- t->WriteNumeric(RPL_REDIR, "%s %d :Please use this Server/Port instead", parameters[0].c_str(), GetPort(t));
+ t->WriteNumeric(RPL_REDIR, parameters[0], GetPort(t), "Please use this Server/Port instead");
ServerInstance->Users->QuitUser(t, reason);
n_done++;
}
@@ -137,7 +140,7 @@ class CommandJumpserver : public Command
int GetPort(LocalUser* user)
{
- int p = (SSLClientCert::GetCertificate(&user->eh) ? sslport : port);
+ int p = (SSLIOHook::IsSSL(&user->eh) ? sslport : port);
if (p == 0)
p = user->GetServerPort();
return p;
@@ -157,10 +160,9 @@ class ModuleJumpServer : public Module
if (js.redirect_new_users)
{
int port = js.GetPort(user);
- user->WriteNumeric(RPL_REDIR, "%s %d :Please use this Server/Port instead",
- js.redirect_to.c_str(), port);
+ user->WriteNumeric(RPL_REDIR, js.redirect_to, port, "Please use this Server/Port instead");
ServerInstance->Users->QuitUser(user, js.reason);
- return MOD_RES_PASSTHRU;
+ return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
diff --git a/src/modules/m_kicknorejoin.cpp b/src/modules/m_kicknorejoin.cpp
index fdb7b8f24..ad8bfdcb6 100644
--- a/src/modules/m_kicknorejoin.cpp
+++ b/src/modules/m_kicknorejoin.cpp
@@ -25,24 +25,69 @@
#include "inspircd.h"
-typedef std::map<std::string, time_t> delaylist;
-
-struct KickRejoinData
+class KickRejoinData
{
- delaylist kicked;
- unsigned int delay;
+ struct KickedUser
+ {
+ std::string uuid;
+ time_t expire;
+
+ KickedUser(User* user, unsigned int Delay)
+ : uuid(user->uuid)
+ , expire(ServerInstance->Time() + Delay)
+ {
+ }
+ };
+
+ typedef std::vector<KickedUser> KickedList;
+
+ mutable KickedList kicked;
+
+ public:
+ const unsigned int delay;
KickRejoinData(unsigned int Delay) : delay(Delay) { }
+
+ bool canjoin(LocalUser* user) const
+ {
+ for (KickedList::iterator i = kicked.begin(); i != kicked.end(); )
+ {
+ KickedUser& rec = *i;
+ if (rec.expire > ServerInstance->Time())
+ {
+ if (rec.uuid == user->uuid)
+ return false;
+ ++i;
+ }
+ else
+ {
+ // Expired record, remove.
+ stdalgo::vector::swaperase(kicked, i);
+ if (kicked.empty())
+ break;
+ }
+ }
+ return true;
+ }
+
+ void add(User* user)
+ {
+ // One user can be in the list multiple times if the user gets kicked, force joins
+ // (skipping OnUserPreJoin) and gets kicked again, but that's okay because canjoin()
+ // works correctly in this case as well
+ kicked.push_back(KickedUser(user, delay));
+ }
};
/** Handles channel mode +J
*/
class KickRejoin : public ParamMode<KickRejoin, SimpleExtItem<KickRejoinData> >
{
- static const unsigned int max = 60;
+ const unsigned int max;
public:
KickRejoin(Module* Creator)
: ParamMode<KickRejoin, SimpleExtItem<KickRejoinData> >(Creator, "kicknorejoin", 'J')
+ , max(60)
{
}
@@ -63,6 +108,11 @@ class KickRejoin : public ParamMode<KickRejoin, SimpleExtItem<KickRejoinData> >
{
out.append(ConvToStr(krd->delay));
}
+
+ std::string GetModuleSettings() const
+ {
+ return ConvToStr(max);
+ }
};
class ModuleKickNoRejoin : public Module
@@ -79,28 +129,11 @@ public:
{
if (chan)
{
- KickRejoinData* data = kr.ext.get(chan);
- if (data)
+ const KickRejoinData* data = kr.ext.get(chan);
+ if ((data) && (!data->canjoin(user)))
{
- delaylist& kicked = data->kicked;
- for (delaylist::iterator iter = kicked.begin(); iter != kicked.end(); )
- {
- if (iter->second > ServerInstance->Time())
- {
- if (iter->first == user->uuid)
- {
- user->WriteNumeric(ERR_DELAYREJOIN, "%s :You must wait %u seconds after being kicked to rejoin (+J)",
- chan->name.c_str(), data->delay);
- return MOD_RES_DENY;
- }
- ++iter;
- }
- else
- {
- // Expired record, remove.
- kicked.erase(iter++);
- }
- }
+ user->WriteNumeric(ERR_DELAYREJOIN, chan, InspIRCd::Format("You must wait %u seconds after being kicked to rejoin (+J)", data->delay));
+ return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
@@ -114,13 +147,13 @@ public:
KickRejoinData* data = kr.ext.get(memb->chan);
if (data)
{
- data->kicked[memb->user->uuid] = ServerInstance->Time() + data->delay;
+ data->add(memb->user);
}
}
Version GetVersion() CXX11_OVERRIDE
{
- return Version("Channel mode to delay rejoin after kick", VF_VENDOR);
+ return Version("Channel mode to delay rejoin after kick", VF_VENDOR | VF_COMMON, kr.GetModuleSettings());
}
};
diff --git a/src/modules/m_knock.cpp b/src/modules/m_knock.cpp
index 26397bc9c..cf623c4ab 100644
--- a/src/modules/m_knock.cpp
+++ b/src/modules/m_knock.cpp
@@ -45,30 +45,30 @@ class CommandKnock : public Command
Channel* c = ServerInstance->FindChan(parameters[0]);
if (!c)
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
if (c->HasUser(user))
{
- user->WriteNumeric(ERR_KNOCKONCHAN, "%s :Can't KNOCK on %s, you are already on that channel.", c->name.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_KNOCKONCHAN, c->name, InspIRCd::Format("Can't KNOCK on %s, you are already on that channel.", c->name.c_str()));
return CMD_FAILURE;
}
if (c->IsModeSet(noknockmode))
{
- user->WriteNumeric(480, ":Can't KNOCK on %s, +K is set.", c->name.c_str());
+ user->WriteNumeric(480, InspIRCd::Format("Can't KNOCK on %s, +K is set.", c->name.c_str()));
return CMD_FAILURE;
}
if (!c->IsModeSet(inviteonlymode))
{
- user->WriteNumeric(ERR_CHANOPEN, "%s :Can't KNOCK on %s, channel is not invite only so knocking is pointless!", c->name.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_CHANOPEN, c->name, InspIRCd::Format("Can't KNOCK on %s, channel is not invite only so knocking is pointless!", c->name.c_str()));
return CMD_FAILURE;
}
if (sendnotice)
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :User %s is KNOCKing on %s (%s)", c->name.c_str(), user->nick.c_str(), c->name.c_str(), parameters[1].c_str());
+ c->WriteNotice(InspIRCd::Format("User %s is KNOCKing on %s (%s)", user->nick.c_str(), c->name.c_str(), parameters[1].c_str()));
if (sendnumeric)
c->WriteChannelWithServ(ServerInstance->Config->ServerName, "710 %s %s %s :is KNOCKing: %s", c->name.c_str(), c->name.c_str(), user->GetFullHost().c_str(), parameters[1].c_str());
@@ -98,14 +98,12 @@ class ModuleKnock : public Module
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
std::string knocknotify = ServerInstance->Config->ConfValue("knock")->getString("notify");
- irc::string notify(knocknotify.c_str());
-
- if (notify == "numeric")
+ if (stdalgo::string::equalsci(knocknotify, "numeric"))
{
cmd.sendnotice = false;
cmd.sendnumeric = true;
}
- else if (notify == "both")
+ else if (stdalgo::string::equalsci(knocknotify, "both"))
{
cmd.sendnotice = true;
cmd.sendnumeric = true;
diff --git a/src/modules/m_ldapauth.cpp b/src/modules/m_ldapauth.cpp
index e89ce4949..fedf02b4d 100644
--- a/src/modules/m_ldapauth.cpp
+++ b/src/modules/m_ldapauth.cpp
@@ -64,7 +64,7 @@ class BindInterface : public LDAPInterface
while (i < text.length() - 1 && isalpha(text[i + 1]))
++i;
- std::string key = text.substr(start, (i - start) + 1);
+ std::string key(text, start, (i - start) + 1);
result.append(replacements[key]);
}
else
@@ -90,8 +90,8 @@ class BindInterface : public LDAPInterface
if (pos == std::string::npos) // malformed
continue;
- std::string key = dnPart.substr(0, pos);
- std::string value = dnPart.substr(pos + 1, dnPart.length() - pos + 1); // +1s to skip the = itself
+ std::string key(dnPart, 0, pos);
+ std::string value(dnPart, pos + 1, dnPart.length() - pos + 1); // +1s to skip the = itself
dnParts[key] = value;
}
@@ -307,8 +307,8 @@ class ModuleLDAPAuth : public Module
public:
ModuleLDAPAuth()
: LDAP(this, "LDAP")
- , ldapAuthed("ldapauth", this)
- , ldapVhost("ldapauth_vhost", this)
+ , ldapAuthed("ldapauth", ExtensionItem::EXT_USER, this)
+ , ldapVhost("ldapauth_vhost", ExtensionItem::EXT_USER, this)
{
me = this;
authed = &ldapAuthed;
@@ -406,9 +406,22 @@ public:
return MOD_RES_DENY;
}
+ std::string what;
+ std::string::size_type pos = user->password.find(':');
+ if (pos != std::string::npos)
+ {
+ what = attribute + "=" + user->password.substr(0, pos);
+
+ // Trim the user: prefix, leaving just 'pass' for later password check
+ user->password = user->password.substr(pos + 1);
+ }
+ else
+ {
+ what = attribute + "=" + (useusername ? user->ident : user->nick);
+ }
+
try
{
- std::string what = attribute + "=" + (useusername ? user->ident : user->nick);
LDAP->BindAsManager(new AdminBindInterface(this, LDAP.GetProvider(), user->uuid, base, what));
}
catch (LDAPException &ex)
diff --git a/src/modules/m_ldapoper.cpp b/src/modules/m_ldapoper.cpp
index 9bfa3971f..9deb9a203 100644
--- a/src/modules/m_ldapoper.cpp
+++ b/src/modules/m_ldapoper.cpp
@@ -41,7 +41,7 @@ class LDAPOperBase : public LDAPInterface
if (!user)
return;
- Command* oper_command = ServerInstance->Parser->GetHandler("OPER");
+ Command* oper_command = ServerInstance->Parser.GetHandler("OPER");
if (!oper_command)
return;
@@ -83,7 +83,7 @@ class BindInterface : public LDAPOperBase
void OnResult(const LDAPResult& r) CXX11_OVERRIDE
{
User* user = ServerInstance->FindUUID(uid);
- OperIndex::iterator iter = ServerInstance->Config->oper_blocks.find(opername);
+ ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->oper_blocks.find(opername);
if (!user || iter == ServerInstance->Config->oper_blocks.end())
{
@@ -208,7 +208,7 @@ class ModuleLDAPAuth : public Module
const std::string& opername = parameters[0];
const std::string& password = parameters[1];
- OperIndex::iterator it = ServerInstance->Config->oper_blocks.find(opername);
+ ServerConfig::OperIndex::const_iterator it = ServerInstance->Config->oper_blocks.find(opername);
if (it == ServerInstance->Config->oper_blocks.end())
return MOD_RES_PASSTHRU;
diff --git a/src/modules/m_lockserv.cpp b/src/modules/m_lockserv.cpp
index 65b9aa036..7c1bb5bd3 100644
--- a/src/modules/m_lockserv.cpp
+++ b/src/modules/m_lockserv.cpp
@@ -27,24 +27,25 @@
class CommandLockserv : public Command
{
- bool& locked;
+ std::string& locked;
public:
- CommandLockserv(Module* Creator, bool& lock) : Command(Creator, "LOCKSERV", 0), locked(lock)
+ CommandLockserv(Module* Creator, std::string& lock) : Command(Creator, "LOCKSERV", 0, 1), locked(lock)
{
+ allow_empty_last_param = false;
flags_needed = 'o';
}
CmdResult Handle (const std::vector<std::string> &parameters, User *user)
{
- if (locked)
+ if (!locked.empty())
{
user->WriteNotice("The server is already locked.");
return CMD_FAILURE;
}
- locked = true;
- user->WriteNumeric(988, "%s :Closed for new connections", user->server->GetName().c_str());
+ locked = parameters.empty() ? "Server is temporarily closed. Please try again later." : parameters[0];
+ user->WriteNumeric(988, user->server->GetName(), "Closed for new connections");
ServerInstance->SNO->WriteGlobalSno('a', "Oper %s used LOCKSERV to temporarily disallow new connections", user->nick.c_str());
return CMD_SUCCESS;
}
@@ -52,24 +53,24 @@ class CommandLockserv : public Command
class CommandUnlockserv : public Command
{
- bool& locked;
+ std::string& locked;
public:
- CommandUnlockserv(Module* Creator, bool &lock) : Command(Creator, "UNLOCKSERV", 0), locked(lock)
+ CommandUnlockserv(Module* Creator, std::string& lock) : Command(Creator, "UNLOCKSERV", 0), locked(lock)
{
flags_needed = 'o';
}
CmdResult Handle (const std::vector<std::string> &parameters, User *user)
{
- if (!locked)
+ if (locked.empty())
{
user->WriteNotice("The server isn't locked.");
return CMD_FAILURE;
}
- locked = false;
- user->WriteNumeric(989, "%s :Open for new connections", user->server->GetName().c_str());
+ locked.clear();
+ user->WriteNumeric(989, user->server->GetName(), "Open for new connections");
ServerInstance->SNO->WriteGlobalSno('a', "Oper %s used UNLOCKSERV to allow new connections", user->nick.c_str());
return CMD_SUCCESS;
}
@@ -77,7 +78,7 @@ class CommandUnlockserv : public Command
class ModuleLockserv : public Module
{
- bool locked;
+ std::string locked;
CommandLockserv lockcommand;
CommandUnlockserv unlockcommand;
@@ -86,23 +87,18 @@ class ModuleLockserv : public Module
{
}
- void init() CXX11_OVERRIDE
- {
- locked = false;
- }
-
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
// Emergency way to unlock
if (!status.srcuser)
- locked = false;
+ locked.clear();
}
ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
- if (locked)
+ if (!locked.empty())
{
- ServerInstance->Users->QuitUser(user, "Server is temporarily closed. Please try again later.");
+ ServerInstance->Users->QuitUser(user, locked);
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
@@ -110,7 +106,7 @@ class ModuleLockserv : public Module
ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
- return locked ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+ return !locked.empty() ? MOD_RES_DENY : MOD_RES_PASSTHRU;
}
Version GetVersion() CXX11_OVERRIDE
diff --git a/src/modules/m_md5.cpp b/src/modules/m_md5.cpp
index ecf76d07c..26ff4cffc 100644
--- a/src/modules/m_md5.cpp
+++ b/src/modules/m_md5.cpp
@@ -61,23 +61,13 @@ class MD5Provider : public HashProvider
} while (--words);
}
- void MD5Init(MD5Context *ctx, unsigned int* ikey = NULL)
+ void MD5Init(MD5Context *ctx)
{
/* These are the defaults for md5 */
- if (!ikey)
- {
- ctx->buf[0] = 0x67452301;
- ctx->buf[1] = 0xefcdab89;
- ctx->buf[2] = 0x98badcfe;
- ctx->buf[3] = 0x10325476;
- }
- else
- {
- ctx->buf[0] = ikey[0];
- ctx->buf[1] = ikey[1];
- ctx->buf[2] = ikey[2];
- ctx->buf[3] = ikey[3];
- }
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
ctx->bytes[0] = 0;
ctx->bytes[1] = 0;
@@ -154,7 +144,7 @@ class MD5Provider : public HashProvider
void MD5Transform(word32 buf[4], word32 const in[16])
{
- register word32 a, b, c, d;
+ word32 a, b, c, d;
a = buf[0];
b = buf[1];
@@ -236,37 +226,23 @@ class MD5Provider : public HashProvider
}
- void MyMD5(void *dest, void *orig, int len, unsigned int* ikey)
+ void MyMD5(void *dest, void *orig, int len)
{
MD5Context context;
- MD5Init(&context, ikey);
+ MD5Init(&context);
MD5Update(&context, (const unsigned char*)orig, len);
MD5Final((unsigned char*)dest, &context);
}
-
- void GenHash(const char* src, char* dest, const char* xtab, unsigned int* ikey, size_t srclen)
- {
- unsigned char bytes[16];
-
- MyMD5((char*)bytes, (void*)src, srclen, ikey);
-
- for (int i = 0; i < 16; i++)
- {
- *dest++ = xtab[bytes[i] / 16];
- *dest++ = xtab[bytes[i] % 16];
- }
- *dest++ = 0;
- }
public:
- std::string sum(const std::string& data)
+ std::string GenerateRaw(const std::string& data)
{
char res[16];
- MyMD5(res, (void*)data.data(), data.length(), NULL);
+ MyMD5(res, (void*)data.data(), data.length());
return std::string(res, 16);
}
- MD5Provider(Module* parent) : HashProvider(parent, "hash/md5", 16, 64) {}
+ MD5Provider(Module* parent) : HashProvider(parent, "md5", 16, 64) {}
};
class ModuleMD5 : public Module
diff --git a/src/modules/m_messageflood.cpp b/src/modules/m_messageflood.cpp
index 92d67b9ab..7323605cb 100644
--- a/src/modules/m_messageflood.cpp
+++ b/src/modules/m_messageflood.cpp
@@ -34,7 +34,7 @@ class floodsettings
unsigned int secs;
unsigned int lines;
time_t reset;
- std::map<User*, unsigned int> counters;
+ insp::flat_map<User*, unsigned int> counters;
floodsettings(bool a, int b, int c) : ban(a), secs(b), lines(c)
{
@@ -54,11 +54,7 @@ class floodsettings
void clear(User* who)
{
- std::map<User*, unsigned int>::iterator iter = counters.find(who);
- if (iter != counters.end())
- {
- counters.erase(iter);
- }
+ counters.erase(who);
}
};
@@ -77,7 +73,7 @@ class MsgFlood : public ParamMode<MsgFlood, SimpleExtItem<floodsettings> >
std::string::size_type colon = parameter.find(':');
if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
{
- source->WriteNumeric(608, "%s :Invalid flood parameter", channel->name.c_str());
+ source->WriteNumeric(608, channel->name, "Invalid flood parameter");
return MODEACTION_DENY;
}
@@ -88,7 +84,7 @@ class MsgFlood : public ParamMode<MsgFlood, SimpleExtItem<floodsettings> >
if ((nlines<2) || (nsecs<1))
{
- source->WriteNumeric(608, "%s :Invalid flood parameter", channel->name.c_str());
+ source->WriteNumeric(608, channel->name, "Invalid flood parameter");
return MODEACTION_DENY;
}
@@ -137,15 +133,13 @@ class ModuleMsgFlood : public Module
f->clear(user);
if (f->ban)
{
- std::vector<std::string> parameters;
- parameters.push_back(dest->name);
- parameters.push_back("+b");
- parameters.push_back("*!*@" + user->dhost);
- ServerInstance->Modes->Process(parameters, ServerInstance->FakeClient);
+ Modes::ChangeList changelist;
+ changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->dhost);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, dest, NULL, changelist);
}
- const std::string kickMessage = "Channel flood triggered (limit is " + ConvToStr(f->lines) +
- " in " + ConvToStr(f->secs) + " secs)";
+ const std::string kickMessage = "Channel flood triggered (trigger is " + ConvToStr(f->lines) +
+ " lines in " + ConvToStr(f->secs) + " secs)";
dest->KickUser(ServerInstance->FakeClient, user, kickMessage);
diff --git a/src/modules/m_mlock.cpp b/src/modules/m_mlock.cpp
index d9c43ec10..d3ab5b9fd 100644
--- a/src/modules/m_mlock.cpp
+++ b/src/modules/m_mlock.cpp
@@ -25,7 +25,7 @@ class ModuleMLock : public Module
public:
ModuleMLock()
- : mlock("mlock", this)
+ : mlock("mlock", ExtensionItem::EXT_CHANNEL, this)
{
}
@@ -50,8 +50,7 @@ class ModuleMLock : public Module
std::string::size_type p = mlock_str->find(mode);
if (p != std::string::npos)
{
- source->WriteNumeric(742, "%s %c %s :MODE cannot be set due to channel having an active MLOCK restriction policy",
- channel->name.c_str(), mode, mlock_str->c_str());
+ source->WriteNumeric(742, channel->name, mode, *mlock_str, "MODE cannot be set due to channel having an active MLOCK restriction policy");
return MOD_RES_DENY;
}
diff --git a/src/modules/m_modenotice.cpp b/src/modules/m_modenotice.cpp
index e02c9147f..056eb4a62 100644
--- a/src/modules/m_modenotice.cpp
+++ b/src/modules/m_modenotice.cpp
@@ -32,7 +32,8 @@ class CommandModeNotice : public Command
{
std::string msg = "*** From " + src->nick + ": " + parameters[1];
int mlen = parameters[0].length();
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
User* user = *i;
for (int n = 0; n < mlen; n++)
diff --git a/src/modules/m_monitor.cpp b/src/modules/m_monitor.cpp
new file mode 100644
index 000000000..c69732a73
--- /dev/null
+++ b/src/modules/m_monitor.cpp
@@ -0,0 +1,444 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+namespace IRCv3
+{
+ namespace Monitor
+ {
+ class ExtItem;
+ struct Entry;
+ class Manager;
+ class ManagerInternal;
+
+ typedef std::vector<Entry*> WatchedList;
+ typedef std::vector<LocalUser*> WatcherList;
+ }
+}
+
+struct IRCv3::Monitor::Entry
+{
+ WatcherList watchers;
+ std::string nick;
+
+ void SetNick(const std::string& Nick)
+ {
+ nick.clear();
+ // We may show this string to other users so do not leak the casing
+ std::transform(Nick.begin(), Nick.end(), std::back_inserter(nick), ::tolower);
+ }
+
+ const std::string& GetNick() const { return nick; }
+};
+
+class IRCv3::Monitor::Manager
+{
+ struct ExtData
+ {
+ WatchedList list;
+ };
+
+ class ExtItem : public ExtensionItem
+ {
+ Manager& manager;
+
+ public:
+ ExtItem(Module* mod, const std::string& extname, Manager& managerref)
+ : ExtensionItem(extname, ExtensionItem::EXT_USER, mod)
+ , manager(managerref)
+ {
+ }
+
+ ExtData* get(Extensible* container, bool create = false)
+ {
+ ExtData* extdata = static_cast<ExtData*>(get_raw(container));
+ if ((!extdata) && (create))
+ {
+ extdata = new ExtData;
+ set_raw(container, extdata);
+ }
+ return extdata;
+ }
+
+ void unset(Extensible* container)
+ {
+ free(unset_raw(container));
+ }
+
+ std::string serialize(SerializeFormat format, const Extensible* container, void* item) const
+ {
+ std::string ret;
+ if (format == FORMAT_NETWORK)
+ return ret;
+
+ const ExtData* extdata = static_cast<ExtData*>(item);
+ for (WatchedList::const_iterator i = extdata->list.begin(); i != extdata->list.end(); ++i)
+ {
+ const Entry* entry = *i;
+ ret.append(entry->GetNick()).push_back(' ');
+ }
+ if (!ret.empty())
+ ret.erase(ret.size()-1);
+ return ret;
+ }
+
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
+
+ void free(void* item)
+ {
+ delete static_cast<ExtData*>(item);
+ }
+ };
+
+ public:
+ Manager(Module* mod, const std::string& extname)
+ : ext(mod, extname, *this)
+ {
+ }
+
+ enum WatchResult
+ {
+ WR_OK,
+ WR_TOOMANY,
+ WR_ALREADYWATCHING,
+ WR_INVALIDNICK
+ };
+
+ WatchResult Watch(LocalUser* user, const std::string& nick, unsigned int maxwatch)
+ {
+ if (!ServerInstance->IsNick(nick))
+ return WR_INVALIDNICK;
+
+ WatchedList* watched = GetWatchedPriv(user, true);
+ if (watched->size() >= maxwatch)
+ return WR_TOOMANY;
+
+ Entry* entry = AddWatcher(nick, user);
+ if (stdalgo::isin(*watched, entry))
+ return WR_ALREADYWATCHING;
+
+ entry->watchers.push_back(user);
+ watched->push_back(entry);
+ return WR_OK;
+ }
+
+ bool Unwatch(LocalUser* user, const std::string& nick)
+ {
+ WatchedList* list = GetWatchedPriv(user);
+ if (!list)
+ return false;
+
+ bool ret = RemoveWatcher(nick, user, *list);
+ // If no longer watching any nick unset ext
+ if (list->empty())
+ ext.unset(user);
+ return ret;
+ }
+
+ const WatchedList& GetWatched(LocalUser* user)
+ {
+ WatchedList* list = GetWatchedPriv(user);
+ if (list)
+ return *list;
+ return emptywatchedlist;
+ }
+
+ void UnwatchAll(LocalUser* user)
+ {
+ WatchedList* list = GetWatchedPriv(user);
+ if (!list)
+ return;
+
+ while (!list->empty())
+ {
+ Entry* entry = list->front();
+ RemoveWatcher(entry->GetNick(), user, *list);
+ }
+ ext.unset(user);
+ }
+
+ WatcherList* GetWatcherList(const std::string& nick)
+ {
+ Entry* entry = Find(nick);
+ if (entry)
+ return &entry->watchers;
+ return NULL;
+ }
+
+ static User* FindNick(const std::string& nick)
+ {
+ User* user = ServerInstance->FindNickOnly(nick);
+ if ((user) && (user->registered == REG_ALL))
+ return user;
+ return NULL;
+ }
+
+ private:
+ typedef TR1NS::unordered_map<std::string, Entry, irc::insensitive, irc::StrHashComp> NickHash;
+
+ Entry* Find(const std::string& nick)
+ {
+ NickHash::iterator it = nicks.find(nick);
+ if (it != nicks.end())
+ return &it->second;
+ return NULL;
+ }
+
+ Entry* AddWatcher(const std::string& nick, LocalUser* user)
+ {
+ std::pair<NickHash::iterator, bool> ret = nicks.insert(std::make_pair(nick, Entry()));
+ Entry& entry = ret.first->second;
+ if (ret.second)
+ entry.SetNick(nick);
+ return &entry;
+ }
+
+ bool RemoveWatcher(const std::string& nick, LocalUser* user, WatchedList& watchedlist)
+ {
+ NickHash::iterator it = nicks.find(nick);
+ // If nobody is watching this nick the user trying to remove it isn't watching it for sure
+ if (it == nicks.end())
+ return false;
+
+ Entry& entry = it->second;
+ // Erase from the user's list of watched nicks
+ if (!stdalgo::vector::swaperase(watchedlist, &entry))
+ return false; // User is not watching this nick
+
+ // Erase from the nick's list of watching users
+ stdalgo::vector::swaperase(entry.watchers, user);
+
+ // If nobody else is watching the nick remove map entry
+ if (entry.watchers.empty())
+ nicks.erase(it);
+
+ return true;
+ }
+
+ WatchedList* GetWatchedPriv(LocalUser* user, bool create = false)
+ {
+ ExtData* extdata = ext.get(user, create);
+ if (!extdata)
+ return NULL;
+ return &extdata->list;
+ }
+
+ NickHash nicks;
+ ExtItem ext;
+ WatchedList emptywatchedlist;
+};
+
+// inline is needed in static builds to support m_watch including the Manager code from this file
+inline void IRCv3::Monitor::Manager::ExtItem::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+ if (format == FORMAT_NETWORK)
+ return;
+
+ irc::spacesepstream ss(value);
+ for (std::string nick; ss.GetToken(nick); )
+ manager.Watch(static_cast<LocalUser*>(container), nick, UINT_MAX);
+}
+
+#ifndef INSPIRCD_MONITOR_MANAGER_ONLY
+
+enum
+{
+ RPL_MONONLINE = 730,
+ RPL_MONOFFLINE = 731,
+ RPL_MONLIST = 732,
+ RPL_ENDOFMONLIST = 733,
+ ERR_MONLISTFULL = 734
+};
+
+class CommandMonitor : public SplitCommand
+{
+ typedef Numeric::Builder<> ReplyBuilder;
+ // Additional penalty for the /MONITOR L and /MONITOR S commands that request a list from the server
+ static const unsigned int ListPenalty = 3000;
+
+ IRCv3::Monitor::Manager& manager;
+
+ void HandlePlus(LocalUser* user, const std::string& input)
+ {
+ ReplyBuilder online(user, RPL_MONONLINE);
+ ReplyBuilder offline(user, RPL_MONOFFLINE);
+ irc::commasepstream ss(input);
+ for (std::string nick; ss.GetToken(nick); )
+ {
+ IRCv3::Monitor::Manager::WatchResult result = manager.Watch(user, nick, maxmonitor);
+ if (result == IRCv3::Monitor::Manager::WR_TOOMANY)
+ {
+ // List is full, send error which includes the remaining nicks that were not processed
+ user->WriteNumeric(ERR_MONLISTFULL, maxmonitor, InspIRCd::Format("%s%s%s", nick.c_str(), (ss.StreamEnd() ? "" : ","), ss.GetRemaining().c_str()), "Monitor list is full");
+ break;
+ }
+ else if (result != IRCv3::Monitor::Manager::WR_OK)
+ continue; // Already added or invalid nick
+
+ ReplyBuilder& out = (IRCv3::Monitor::Manager::FindNick(nick) ? online : offline);
+ out.Add(nick);
+ }
+
+ online.Flush();
+ offline.Flush();
+ }
+
+ void HandleMinus(LocalUser* user, const std::string& input)
+ {
+ irc::commasepstream ss(input);
+ for (std::string nick; ss.GetToken(nick); )
+ manager.Unwatch(user, nick);
+ }
+
+ public:
+ unsigned int maxmonitor;
+
+ CommandMonitor(Module* mod, IRCv3::Monitor::Manager& managerref)
+ : SplitCommand(mod, "MONITOR", 1)
+ , manager(managerref)
+ {
+ Penalty = 2;
+ allow_empty_last_param = false;
+ syntax = "[C|L|S|+ <nick1>[,<nick2>]|- <nick1>[,<nick2>]";
+ }
+
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+ {
+ char subcmd = toupper(parameters[0][0]);
+ if (subcmd == '+')
+ {
+ if (parameters.size() > 1)
+ HandlePlus(user, parameters[1]);
+ }
+ else if (subcmd == '-')
+ {
+ if (parameters.size() > 1)
+ HandleMinus(user, parameters[1]);
+ }
+ else if (subcmd == 'C')
+ {
+ manager.UnwatchAll(user);
+ }
+ else if (subcmd == 'L')
+ {
+ user->CommandFloodPenalty += ListPenalty;
+ const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+ ReplyBuilder out(user, RPL_MONLIST);
+ for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ IRCv3::Monitor::Entry* entry = *i;
+ out.Add(entry->GetNick());
+ }
+ out.Flush();
+ user->WriteNumeric(RPL_ENDOFMONLIST, "End of MONITOR list");
+ }
+ else if (subcmd == 'S')
+ {
+ user->CommandFloodPenalty += ListPenalty;
+
+ ReplyBuilder online(user, RPL_MONONLINE);
+ ReplyBuilder offline(user, RPL_MONOFFLINE);
+
+ const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+ for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ IRCv3::Monitor::Entry* entry = *i;
+ ReplyBuilder& out = (IRCv3::Monitor::Manager::FindNick(entry->GetNick()) ? online : offline);
+ out.Add(entry->GetNick());
+ }
+
+ online.Flush();
+ offline.Flush();
+ }
+ else
+ return CMD_FAILURE;
+
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleMonitor : public Module
+{
+ IRCv3::Monitor::Manager manager;
+ CommandMonitor cmd;
+
+ void SendAlert(unsigned int numeric, const std::string& nick)
+ {
+ const IRCv3::Monitor::WatcherList* list = manager.GetWatcherList(nick);
+ if (!list)
+ return;
+
+ for (IRCv3::Monitor::WatcherList::const_iterator i = list->begin(); i != list->end(); ++i)
+ {
+ LocalUser* curr = *i;
+ curr->WriteNumeric(numeric, nick);
+ }
+ }
+
+ public:
+ ModuleMonitor()
+ : manager(this, "monitor")
+ , cmd(this, manager)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("monitor");
+ cmd.maxmonitor = tag->getInt("maxentries", 30, 1);
+ }
+
+ void OnPostConnect(User* user) CXX11_OVERRIDE
+ {
+ SendAlert(RPL_MONONLINE, user->nick);
+ }
+
+ void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE
+ {
+ // Detect and ignore nickname case change
+ if (ServerInstance->FindNickOnly(oldnick) == user)
+ return;
+
+ SendAlert(RPL_MONOFFLINE, oldnick);
+ SendAlert(RPL_MONONLINE, user->nick);
+ }
+
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
+ {
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ manager.UnwatchAll(localuser);
+ SendAlert(RPL_MONOFFLINE, user->nick);
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ {
+ tokens["MONITOR"] = ConvToStr(cmd.maxmonitor);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides MONITOR support", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleMonitor)
+
+#endif
diff --git a/src/modules/m_muteban.cpp b/src/modules/m_muteban.cpp
index 72c4acd47..c9caf6a6a 100644
--- a/src/modules/m_muteban.cpp
+++ b/src/modules/m_muteban.cpp
@@ -36,7 +36,7 @@ class ModuleQuietBan : public Module
Channel* chan = static_cast<Channel*>(dest);
if (chan->GetExtBanStatus(user, 'm') == MOD_RES_DENY && chan->GetPrefixValue(user) < VOICE_VALUE)
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (you're muted)", chan->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're muted)");
return MOD_RES_DENY;
}
diff --git a/src/modules/m_namedmodes.cpp b/src/modules/m_namedmodes.cpp
index 5c0ffeea5..7a86c9e3c 100644
--- a/src/modules/m_namedmodes.cpp
+++ b/src/modules/m_namedmodes.cpp
@@ -19,48 +19,60 @@
#include "inspircd.h"
-static void DisplayList(User* user, Channel* channel)
+static void DisplayList(LocalUser* user, Channel* channel)
{
- std::stringstream items;
+ Numeric::ParamBuilder<1> numeric(user, 961);
+ numeric.AddStatic(channel->name);
+
const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
{
ModeHandler* mh = i->second;
if (!channel->IsModeSet(mh))
continue;
- items << " +" << mh->name;
- if (mh->GetNumParams(true))
- items << " " << channel->GetModeParameter(mh);
+ numeric.Add("+" + mh->name);
+ if (mh->NeedsParam(true))
+ {
+ if ((mh->name == "key") && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
+ numeric.Add("<key>");
+ else
+ numeric.Add(channel->GetModeParameter(mh));
+ }
}
- const std::string line = ":" + ServerInstance->Config->ServerName + " 961 " + user->nick + " " + channel->name;
- user->SendText(line, items);
- user->WriteNumeric(960, "%s :End of mode list", channel->name.c_str());
+ numeric.Flush();
+ user->WriteNumeric(960, channel->name, "End of mode list");
}
-class CommandProp : public Command
+class CommandProp : public SplitCommand
{
public:
- CommandProp(Module* parent) : Command(parent, "PROP", 1)
+ CommandProp(Module* parent)
+ : SplitCommand(parent, "PROP", 1)
{
syntax = "<user|channel> {[+-]<mode> [<value>]}*";
}
- CmdResult Handle(const std::vector<std::string> &parameters, User *src)
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* src)
{
+ Channel* const chan = ServerInstance->FindChan(parameters[0]);
+ if (!chan)
+ {
+ src->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+ return CMD_FAILURE;
+ }
+
if (parameters.size() == 1)
{
- Channel* chan = ServerInstance->FindChan(parameters[0]);
- if (chan)
- DisplayList(src, chan);
+ DisplayList(src, chan);
return CMD_SUCCESS;
}
unsigned int i = 1;
- std::vector<std::string> modes;
- modes.push_back(parameters[0]);
- modes.push_back("");
+ Modes::ChangeList modes;
while (i < parameters.size())
{
std::string prop = parameters[i++];
+ if (prop.empty())
+ continue;
bool plus = prop[0] != '-';
if (prop[0] == '+' || prop[0] == '-')
prop.erase(prop.begin());
@@ -68,16 +80,16 @@ class CommandProp : public Command
ModeHandler* mh = ServerInstance->Modes->FindMode(prop, MODETYPE_CHANNEL);
if (mh)
{
- modes[1].push_back(plus ? '+' : '-');
- modes[1].push_back(mh->GetModeChar());
- if (mh->GetNumParams(plus))
+ if (mh->NeedsParam(plus))
{
if (i != parameters.size())
- modes.push_back(parameters[i++]);
+ modes.push(mh, plus, parameters[i++]);
}
+ else
+ modes.push(mh, plus);
}
}
- ServerInstance->Modes->Process(modes, src);
+ ServerInstance->Modes->ProcessSingle(src, chan, NULL, modes, ModeParser::MODE_CHECKACCESS);
return CMD_SUCCESS;
}
};
@@ -89,6 +101,13 @@ class DummyZ : public ModeHandler
{
list = true;
}
+
+ // Handle /MODE #chan Z
+ void DisplayList(User* user, Channel* chan)
+ {
+ if (IS_LOCAL(user))
+ ::DisplayList(static_cast<LocalUser*>(user), chan);
+ }
};
class ModuleNamedModes : public Module
@@ -110,76 +129,59 @@ class ModuleNamedModes : public Module
ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_FIRST);
}
- ModResult OnPreMode(User* source, User* dest, Channel* channel, const std::vector<std::string>& parameters) CXX11_OVERRIDE
+ ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
{
if (!channel)
return MOD_RES_PASSTHRU;
- if (parameters[1].find('Z') == std::string::npos)
- return MOD_RES_PASSTHRU;
- if (parameters.size() <= 2)
- {
- DisplayList(source, channel);
- return MOD_RES_DENY;
- }
-
- std::vector<std::string> newparms;
- newparms.push_back(parameters[0]);
- newparms.push_back(parameters[1]);
- std::string modelist = newparms[1];
- bool adding = true;
- unsigned int param_at = 2;
- for(unsigned int i = 0; i < modelist.length(); i++)
+ Modes::ChangeList::List& list = modes.getlist();
+ for (Modes::ChangeList::List::iterator i = list.begin(); i != list.end(); )
{
- unsigned char modechar = modelist[i];
- if (modechar == '+' || modechar == '-')
- {
- adding = (modechar == '+');
- continue;
- }
- ModeHandler *mh = ServerInstance->Modes->FindMode(modechar, MODETYPE_CHANNEL);
- if (modechar == 'Z')
+ Modes::Change& curr = *i;
+ // Replace all namebase (dummyZ) modes being changed with the actual
+ // mode handler and parameter. The parameter format of the namebase mode is
+ // <modename>[=<parameter>].
+ if (curr.mh == &dummyZ)
{
- std::string name, value;
- if (param_at < parameters.size())
- name = parameters[param_at++];
+ std::string name = curr.param;
+ std::string value;
std::string::size_type eq = name.find('=');
if (eq != std::string::npos)
{
- value = name.substr(eq + 1);
- name = name.substr(0, eq);
+ value.assign(name, eq + 1, std::string::npos);
+ name.erase(eq);
}
- mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL);
+ ModeHandler* mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL);
if (!mh)
{
// Mode handler not found
- modelist.erase(i--, 1);
+ i = list.erase(i);
continue;
}
- if (mh->GetNumParams(adding))
+ curr.param.clear();
+ if (mh->NeedsParam(curr.adding))
{
if (value.empty())
{
// Mode needs a parameter but there wasn't one
- modelist.erase(i--, 1);
+ i = list.erase(i);
continue;
}
- newparms.push_back(value);
+ // Change parameter to the text after the '='
+ curr.param = value;
}
- modelist[i] = mh->GetModeChar();
- }
- else if (mh && mh->GetNumParams(adding) && param_at < parameters.size())
- {
- newparms.push_back(parameters[param_at++]);
+ // Put the actual ModeHandler in place of the namebase handler
+ curr.mh = mh;
}
+
+ ++i;
}
- newparms[1] = modelist;
- ServerInstance->Modes->Process(newparms, source);
- return MOD_RES_DENY;
+
+ return MOD_RES_PASSTHRU;
}
};
diff --git a/src/modules/m_namesx.cpp b/src/modules/m_namesx.cpp
index f211b01d8..beac968ef 100644
--- a/src/modules/m_namesx.cpp
+++ b/src/modules/m_namesx.cpp
@@ -25,7 +25,7 @@
class ModuleNamesX : public Module
{
- GenericCap cap;
+ Cap::Capability cap;
public:
ModuleNamesX() : cap(this, "multi-prefix")
{
@@ -52,7 +52,7 @@ class ModuleNamesX : public Module
{
if ((parameters.size()) && (!strcasecmp(parameters[0].c_str(),"NAMESX")))
{
- cap.ext.set(user, 1);
+ cap.set(user, true);
return MOD_RES_DENY;
}
}
@@ -61,43 +61,28 @@ class ModuleNamesX : public Module
ModResult OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
{
- if (cap.ext.get(issuer))
+ if (cap.get(issuer))
prefixes = memb->GetAllPrefixChars();
return MOD_RES_PASSTHRU;
}
- void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
+ ModResult OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
- if ((!memb) || (!cap.ext.get(source)))
- return;
-
- // Channel names can contain ":", and ":" as a 'start-of-token' delimiter is
- // only ever valid after whitespace, so... find the actual delimiter first!
- // Thanks to FxChiP for pointing this out.
- std::string::size_type pos = line.find(" :");
- if (pos == std::string::npos || pos == 0)
- return;
- pos--;
- // Don't do anything if the user has no prefixes
- if ((line[pos] == 'H') || (line[pos] == 'G') || (line[pos] == '*'))
- return;
-
- // 352 21DAAAAAB #chan ident localhost insp21.test 21DAAAAAB H@ :0 a
- // pos
+ if ((!memb) || (!cap.get(source)))
+ return MOD_RES_PASSTHRU;
// Don't do anything if the user has only one prefix
std::string prefixes = memb->GetAllPrefixChars();
if (prefixes.length() <= 1)
- return;
+ return MOD_RES_PASSTHRU;
- line.erase(pos, 1);
- line.insert(pos, prefixes);
- }
+ // #chan ident localhost insp22.test nick H@ :0 Attila
+ if (numeric.GetParams().size() < 6)
+ return MOD_RES_PASSTHRU;
- void OnEvent(Event& ev) CXX11_OVERRIDE
- {
- cap.HandleEvent(ev);
+ numeric.GetParams()[5].append(prefixes, 1, std::string::npos);
+ return MOD_RES_PASSTHRU;
}
};
diff --git a/src/modules/m_nationalchars.cpp b/src/modules/m_nationalchars.cpp
index eb2d080c8..8e836c407 100644
--- a/src/modules/m_nationalchars.cpp
+++ b/src/modules/m_nationalchars.cpp
@@ -26,7 +26,6 @@
by Chernov-Phoenix Alexey (Phoenix@RusNet) mailto:phoenix /email address separator/ pravmail.ru */
#include "inspircd.h"
-#include "caller.h"
#include <fstream>
class lwbNickHandler : public HandlerBase1<bool, const std::string&>
@@ -224,11 +223,35 @@ class ModuleNationalChars : public Module
caller1<bool, const std::string&> rememberer;
bool forcequit;
const unsigned char * lowermap_rememberer;
+ unsigned char prev_map[256];
+
+ template <typename T>
+ void RehashHashmap(T& hashmap)
+ {
+ T newhash(hashmap.bucket_count());
+ for (typename T::const_iterator i = hashmap.begin(); i != hashmap.end(); ++i)
+ newhash.insert(std::make_pair(i->first, i->second));
+ hashmap.swap(newhash);
+ }
+
+ void CheckRehash()
+ {
+ // See if anything changed
+ if (!memcmp(prev_map, national_case_insensitive_map, sizeof(prev_map)))
+ return;
+
+ memcpy(prev_map, national_case_insensitive_map, sizeof(prev_map));
+
+ RehashHashmap(ServerInstance->Users.clientlist);
+ RehashHashmap(ServerInstance->Users.uuidlist);
+ RehashHashmap(ServerInstance->chanlist);
+ }
public:
ModuleNationalChars()
: rememberer(ServerInstance->IsNick), lowermap_rememberer(national_case_insensitive_map)
{
+ memcpy(prev_map, national_case_insensitive_map, sizeof(prev_map));
}
void init() CXX11_OVERRIDE
@@ -248,13 +271,22 @@ class ModuleNationalChars : public Module
{
ConfigTag* tag = ServerInstance->Config->ConfValue("nationalchars");
charset = tag->getString("file");
- casemapping = tag->getString("casemapping", charset);
+ casemapping = tag->getString("casemapping", FileSystem::GetFileName(charset));
+ if (casemapping.find(' ') != std::string::npos)
+ throw ModuleException("<nationalchars:casemapping> must not contain any spaces!");
+#if defined _WIN32
+ if (!FileSystem::StartsWithWindowsDriveLetter(charset))
+ charset.insert(0, "./locales/");
+#else
if(charset[0] != '/')
charset.insert(0, "../locales/");
+#endif
unsigned char * tables[8] = { m_additional, m_additionalMB, m_additionalUp, m_lower, m_upper, m_additionalUtf8, m_additionalUtf8range, m_additionalUtf8interval };
- loadtables(charset, tables, 8, 5);
+ if (!loadtables(charset, tables, 8, 5))
+ throw ModuleException("The locale file failed to load. Check your log file for more information.");
forcequit = tag->getBool("forcequit");
CheckForceQuit("National character set changed");
+ CheckRehash();
}
void CheckForceQuit(const char * message)
@@ -262,10 +294,13 @@ class ModuleNationalChars : public Module
if (!forcequit)
return;
- for (LocalUserList::const_iterator iter = ServerInstance->Users->local_users.begin(); iter != ServerInstance->Users->local_users.end(); ++iter)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator iter = list.begin(); iter != list.end(); )
{
/* Fix by Brain: Dont quit UID users */
+ // Quitting the user removes it from the list
User* n = *iter;
+ ++iter;
if (!isdigit(n->nick[0]) && !ServerInstance->IsNick(n->nick))
ServerInstance->Users->QuitUser(n, message);
}
@@ -276,6 +311,7 @@ class ModuleNationalChars : public Module
ServerInstance->IsNick = rememberer;
national_case_insensitive_map = lowermap_rememberer;
CheckForceQuit("National characters module unloaded");
+ CheckRehash();
}
Version GetVersion() CXX11_OVERRIDE
@@ -292,13 +328,13 @@ class ModuleNationalChars : public Module
}
/*so Bynets Unreal distribution stuff*/
- void loadtables(std::string filename, unsigned char ** tables, unsigned char cnt, char faillimit)
+ bool loadtables(std::string filename, unsigned char ** tables, unsigned char cnt, char faillimit)
{
std::ifstream ifs(ServerInstance->Config->Paths.PrependConfig(filename).c_str());
if (ifs.fail())
{
ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "loadtables() called for missing file: %s", filename.c_str());
- return;
+ return false;
}
for (unsigned char n=0; n< cnt; n++)
@@ -313,11 +349,12 @@ class ModuleNationalChars : public Module
if (loadtable(ifs, tables[n], 255) && (n < faillimit))
{
ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "loadtables() called for illegal file: %s (line %d)", filename.c_str(), n+1);
- return;
+ return false;
}
}
makereverse(m_additional, m_reverse_additional, sizeof(m_additional));
+ return true;
}
unsigned char symtoi(const char *t,unsigned char base)
diff --git a/src/modules/m_nickflood.cpp b/src/modules/m_nickflood.cpp
index f74a18422..abb3cdfaf 100644
--- a/src/modules/m_nickflood.cpp
+++ b/src/modules/m_nickflood.cpp
@@ -20,6 +20,9 @@
#include "inspircd.h"
+// The number of seconds nickname changing will be blocked for.
+static unsigned int duration;
+
/** Holds settings and state associated with channel mode +F
*/
class nickfloodsettings
@@ -72,7 +75,7 @@ class nickfloodsettings
void lock()
{
- unlocktime = ServerInstance->Time() + 60;
+ unlocktime = ServerInstance->Time() + duration;
}
};
@@ -91,7 +94,7 @@ class NickFlood : public ParamMode<NickFlood, SimpleExtItem<nickfloodsettings> >
std::string::size_type colon = parameter.find(':');
if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
{
- source->WriteNumeric(608, "%s :Invalid flood parameter",channel->name.c_str());
+ source->WriteNumeric(608, channel->name, "Invalid flood parameter");
return MODEACTION_DENY;
}
@@ -101,7 +104,7 @@ class NickFlood : public ParamMode<NickFlood, SimpleExtItem<nickfloodsettings> >
if ((nnicks<1) || (nsecs<1))
{
- source->WriteNumeric(608, "%s :Invalid flood parameter",channel->name.c_str());
+ source->WriteNumeric(608, channel->name, "Invalid flood parameter");
return MODEACTION_DENY;
}
@@ -126,9 +129,15 @@ class ModuleNickFlood : public Module
{
}
- ModResult OnUserPreNick(User* user, const std::string &newnick) CXX11_OVERRIDE
+ void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("nickflood");
+ duration = tag->getDuration("duration", 60, 10, 600);
+ }
+
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); i++)
{
Channel* channel = (*i)->chan;
ModResult res;
@@ -142,7 +151,7 @@ class ModuleNickFlood : public Module
if (f->islocked())
{
- user->WriteNumeric(ERR_CANTCHANGENICK, ":%s has been locked for nickchanges for 60 seconds because there have been more than %u nick changes in %u seconds", channel->name.c_str(), f->nicks, f->secs);
+ user->WriteNumeric(ERR_CANTCHANGENICK, InspIRCd::Format("%s has been locked for nickchanges for %u seconds because there have been more than %u nick changes in %u seconds", channel->name.c_str(), duration, f->nicks, f->secs));
return MOD_RES_DENY;
}
@@ -150,7 +159,7 @@ class ModuleNickFlood : public Module
{
f->clear();
f->lock();
- channel->WriteChannelWithServ((char*)ServerInstance->Config->ServerName.c_str(), "NOTICE %s :No nick changes are allowed for 60 seconds because there have been more than %u nick changes in %u seconds.", channel->name.c_str(), f->nicks, f->secs);
+ channel->WriteNotice(InspIRCd::Format("No nick changes are allowed for %u seconds because there have been more than %u nick changes in %u seconds.", duration, f->nicks, f->secs));
return MOD_RES_DENY;
}
}
@@ -167,7 +176,7 @@ class ModuleNickFlood : public Module
if (isdigit(user->nick[0])) /* allow switches to UID */
return;
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); ++i)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); ++i)
{
Channel* channel = (*i)->chan;
ModResult res;
diff --git a/src/modules/m_nicklock.cpp b/src/modules/m_nicklock.cpp
index b8d4ac4df..35845c8d8 100644
--- a/src/modules/m_nicklock.cpp
+++ b/src/modules/m_nicklock.cpp
@@ -55,7 +55,7 @@ class CommandNicklock : public Command
return CMD_FAILURE;
}
- user->WriteNumeric(947, "%s :Nickname now locked.", parameters[1].c_str());
+ user->WriteNumeric(947, parameters[1], "Nickname now locked.");
}
/* If we made it this far, extend the user */
@@ -64,7 +64,7 @@ class CommandNicklock : public Command
locked.set(target, 1);
std::string oldnick = target->nick;
- if (target->ForceNickChange(parameters[1]))
+ if (target->ChangeNick(parameters[1]))
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used NICKLOCK to change and hold "+oldnick+" to "+parameters[1]);
else
{
@@ -78,10 +78,7 @@ class CommandNicklock : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -114,13 +111,11 @@ class CommandNickunlock : public Command
if (locked.set(target, 0))
{
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used NICKUNLOCK on "+target->nick);
- user->SendText(":%s 945 %s %s :Nickname now unlocked.",
- ServerInstance->Config->ServerName.c_str(),user->nick.c_str(),target->nick.c_str());
+ user->WriteRemoteNumeric(945, target->nick, "Nickname now unlocked.");
}
else
{
- user->SendText(":%s 946 %s %s :This user's nickname is not locked.",
- ServerInstance->Config->ServerName.c_str(),user->nick.c_str(),target->nick.c_str());
+ user->WriteRemoteNumeric(946, target->nick, "This user's nickname is not locked.");
return CMD_FAILURE;
}
}
@@ -130,10 +125,7 @@ class CommandNickunlock : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -144,7 +136,9 @@ class ModuleNickLock : public Module
CommandNickunlock cmd2;
public:
ModuleNickLock()
- : locked("nick_locked", this), cmd1(this, locked), cmd2(this, locked)
+ : locked("nick_locked", ExtensionItem::EXT_USER, this)
+ , cmd1(this, locked)
+ , cmd2(this, locked)
{
}
@@ -153,14 +147,11 @@ class ModuleNickLock : public Module
return Version("Provides the NICKLOCK command, allows an oper to change a users nick and lock them to it until they quit", VF_OPTCOMMON | VF_VENDOR);
}
- ModResult OnUserPreNick(User* user, const std::string &newnick) CXX11_OVERRIDE
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
- if (!IS_LOCAL(user))
- return MOD_RES_PASSTHRU;
-
if (locked.get(user))
{
- user->WriteNumeric(ERR_CANTCHANGENICK, ":You cannot change your nickname (your nick is locked)");
+ user->WriteNumeric(ERR_CANTCHANGENICK, "You cannot change your nickname (your nick is locked)");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
@@ -169,7 +160,7 @@ class ModuleNickLock : public Module
void Prioritize()
{
Module *nflood = ServerInstance->Modules->Find("m_nickflood.so");
- ServerInstance->Modules->SetPriority(this, I_OnUserPreNick, PRIORITY_BEFORE, &nflood);
+ ServerInstance->Modules->SetPriority(this, I_OnUserPreNick, PRIORITY_BEFORE, nflood);
}
};
diff --git a/src/modules/m_noctcp.cpp b/src/modules/m_noctcp.cpp
index 953557d90..49b53ee95 100644
--- a/src/modules/m_noctcp.cpp
+++ b/src/modules/m_noctcp.cpp
@@ -56,7 +56,7 @@ class ModuleNoCTCP : public Module
if (!c->GetExtBanStatus(user, 'C').check(!c->IsModeSet(nc)))
{
- user->WriteNumeric(ERR_NOCTCPALLOWED, "%s :Can't send CTCP to channel (+C set)", c->name.c_str());
+ user->WriteNumeric(ERR_NOCTCPALLOWED, c->name, "Can't send CTCP to channel (+C set)");
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_nokicks.cpp b/src/modules/m_nokicks.cpp
index 0acf84118..fb3455567 100644
--- a/src/modules/m_nokicks.cpp
+++ b/src/modules/m_nokicks.cpp
@@ -48,7 +48,7 @@ class ModuleNoKicks : public Module
if (!memb->chan->GetExtBanStatus(source, 'Q').check(!memb->chan->IsModeSet(nk)))
{
// Can't kick with Q in place, not even opers with override, and founders
- source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :Can't kick user %s from channel (+Q set)", memb->chan->name.c_str(), memb->user->nick.c_str());
+ source->WriteNumeric(ERR_CHANOPRIVSNEEDED, memb->chan->name, InspIRCd::Format("Can't kick user %s from channel (+Q set)", memb->user->nick.c_str()));
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
diff --git a/src/modules/m_nonicks.cpp b/src/modules/m_nonicks.cpp
index 15ee4e7f8..d4da3e951 100644
--- a/src/modules/m_nonicks.cpp
+++ b/src/modules/m_nonicks.cpp
@@ -46,12 +46,9 @@ class ModuleNoNickChange : public Module
tokens["EXTBAN"].push_back('N');
}
- ModResult OnUserPreNick(User* user, const std::string &newnick) CXX11_OVERRIDE
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
- if (!IS_LOCAL(user))
- return MOD_RES_PASSTHRU;
-
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); i++)
{
Channel* curr = (*i)->chan;
@@ -65,8 +62,8 @@ class ModuleNoNickChange : public Module
if (!curr->GetExtBanStatus(user, 'N').check(!curr->IsModeSet(nn)))
{
- user->WriteNumeric(ERR_CANTCHANGENICK, ":Can't change nickname while on %s (+N is set)",
- curr->name.c_str());
+ user->WriteNumeric(ERR_CANTCHANGENICK, InspIRCd::Format("Can't change nickname while on %s (+N is set)",
+ curr->name.c_str()));
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_nonotice.cpp b/src/modules/m_nonotice.cpp
index cab367ad9..3d6d0bb09 100644
--- a/src/modules/m_nonotice.cpp
+++ b/src/modules/m_nonotice.cpp
@@ -55,7 +55,7 @@ class ModuleNoNotice : public Module
return MOD_RES_PASSTHRU;
else
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Can't send NOTICE to channel (+T set)", c->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send NOTICE to channel (+T set)");
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_ojoin.cpp b/src/modules/m_ojoin.cpp
index e4314873b..9465a51e5 100644
--- a/src/modules/m_ojoin.cpp
+++ b/src/modules/m_ojoin.cpp
@@ -30,10 +30,11 @@ class CommandOjoin : public SplitCommand
bool notice;
bool op;
ModeHandler* npmh;
- CommandOjoin(Module* parent) :
- SplitCommand(parent, "OJOIN", 1)
+ CommandOjoin(Module* parent, ModeHandler& mode)
+ : SplitCommand(parent, "OJOIN", 1)
+ , npmh(&mode)
{
- flags_needed = 'o'; Penalty = 0; syntax = "<channel>";
+ flags_needed = 'o'; syntax = "<channel>";
active = false;
}
@@ -57,26 +58,24 @@ class CommandOjoin : public SplitCommand
if (notice)
{
- channel = ServerInstance->FindChan(parameters[0]);
- channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s joined on official network business.",
- parameters[0].c_str(), user->nick.c_str());
- ServerInstance->PI->SendChannelNotice(channel, 0, user->nick + " joined on official network business.");
+ const std::string msg = user->nick + " joined on official network business.";
+ channel->WriteNotice(msg);
+ ServerInstance->PI->SendChannelNotice(channel, 0, msg);
}
}
else
{
+ channel = ServerInstance->FindChan(parameters[0]);
+ if (!channel)
+ return CMD_FAILURE;
+
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used OJOIN in "+parameters[0]);
// they're already in the channel
- std::vector<std::string> modes;
- modes.push_back(parameters[0]);
- modes.push_back(std::string("+") + npmh->GetModeChar());
+ Modes::ChangeList changelist;
+ changelist.push_add(npmh, user->nick);
if (op)
- {
- modes[1].push_back('o');
- modes.push_back(user->nick);
- }
- modes.push_back(user->nick);
- ServerInstance->Modes->Process(modes, ServerInstance->FakeClient);
+ changelist.push_add(ServerInstance->Modes->FindMode('o', MODETYPE_CHANNEL), user->nick);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, channel, NULL, changelist);
}
return CMD_SUCCESS;
}
@@ -88,11 +87,9 @@ class NetworkPrefix : public PrefixMode
{
public:
NetworkPrefix(Module* parent, char NPrefix)
- : PrefixMode(parent, "official-join", 'Y')
+ : PrefixMode(parent, "official-join", 'Y', NETWORK_VALUE, NPrefix)
{
- prefix = NPrefix;
levelrequired = INT_MAX;
- prefixrank = NETWORK_VALUE;
}
ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding)
@@ -108,35 +105,22 @@ class NetworkPrefix : public PrefixMode
class ModuleOjoin : public Module
{
- NetworkPrefix* np;
+ NetworkPrefix np;
CommandOjoin mycommand;
public:
ModuleOjoin()
- : np(NULL), mycommand(this)
+ : np(this, ServerInstance->Config->ConfValue("ojoin")->getString("prefix").c_str()[0])
+ , mycommand(this, np)
{
}
- void init() CXX11_OVERRIDE
- {
- std::string npre = ServerInstance->Config->ConfValue("ojoin")->getString("prefix");
- char NPrefix = npre.empty() ? 0 : npre[0];
- if (NPrefix && ServerInstance->Modes->FindPrefix(NPrefix))
- throw ModuleException("Looks like the prefix you picked for m_ojoin is already in use. Pick another.");
-
- /* Initialise module variables */
- np = new NetworkPrefix(this, NPrefix);
- mycommand.npmh = np;
-
- ServerInstance->Modules->AddService(*np);
- }
-
ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (mycommand.active)
{
- privs += np->GetModeChar();
+ privs += np.GetModeChar();
if (mycommand.op)
privs += 'o';
return MOD_RES_ALLOW;
@@ -155,22 +139,17 @@ class ModuleOjoin : public Module
ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) CXX11_OVERRIDE
{
// Don't do anything if they're not +Y
- if (!memb->hasMode(np->GetModeChar()))
+ if (!memb->HasMode(&np))
return MOD_RES_PASSTHRU;
// Let them do whatever they want to themselves.
if (source == memb->user)
return MOD_RES_PASSTHRU;
- source->WriteNumeric(ERR_RESTRICTED, memb->chan->name+" :Can't kick "+memb->user->nick+" as they're on official network business.");
+ source->WriteNumeric(ERR_RESTRICTED, memb->chan->name, "Can't kick "+memb->user->nick+" as they're on official network business.");
return MOD_RES_DENY;
}
- ~ModuleOjoin()
- {
- delete np;
- }
-
void Prioritize()
{
ServerInstance->Modules->SetPriority(this, I_OnUserPreJoin, PRIORITY_FIRST);
diff --git a/src/modules/m_operchans.cpp b/src/modules/m_operchans.cpp
index 3c6b4cd59..0b074ebab 100644
--- a/src/modules/m_operchans.cpp
+++ b/src/modules/m_operchans.cpp
@@ -44,8 +44,7 @@ class ModuleOperChans : public Module
{
if (chan && chan->IsModeSet(oc) && !user->IsOper())
{
- user->WriteNumeric(ERR_CANTJOINOPERSONLY, "%s :Only IRC operators may join %s (+O is set)",
- chan->name.c_str(), chan->name.c_str());
+ user->WriteNumeric(ERR_CANTJOINOPERSONLY, chan->name, InspIRCd::Format("Only IRC operators may join %s (+O is set)", chan->name.c_str()));
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
diff --git a/src/modules/m_operlevels.cpp b/src/modules/m_operlevels.cpp
index ac7178a93..bf758b1f7 100644
--- a/src/modules/m_operlevels.cpp
+++ b/src/modules/m_operlevels.cpp
@@ -44,7 +44,7 @@ class ModuleOperLevels : public Module
{
if (IS_LOCAL(source)) ServerInstance->SNO->WriteGlobalSno('a', "Oper %s (level %ld) attempted to /kill a higher oper: %s (level %ld): Reason: %s",source->nick.c_str(),source_level,dest->nick.c_str(),dest_level,reason.c_str());
dest->WriteNotice("*** Oper " + source->nick + " attempted to /kill you!");
- source->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper %s is a higher level than you", dest->nick.c_str());
+ source->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper %s is a higher level than you", dest->nick.c_str()));
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_operlog.cpp b/src/modules/m_operlog.cpp
index d015d5ead..68f50bf6d 100644
--- a/src/modules/m_operlog.cpp
+++ b/src/modules/m_operlog.cpp
@@ -49,7 +49,7 @@ class ModuleOperLog : public Module
if ((user->IsOper()) && (user->HasPermission(command)))
{
- Command* thiscommand = ServerInstance->Parser->GetHandler(command);
+ Command* thiscommand = ServerInstance->Parser.GetHandler(command);
if ((thiscommand) && (thiscommand->flags_needed == 'o'))
{
std::string msg = "[" + user->GetFullRealHost() + "] " + command + " " + irc::stringjoiner(parameters);
diff --git a/src/modules/m_opermodes.cpp b/src/modules/m_opermodes.cpp
index 7ab54cedf..33ebb57a0 100644
--- a/src/modules/m_opermodes.cpp
+++ b/src/modules/m_opermodes.cpp
@@ -60,7 +60,7 @@ class ModuleModesOnOper : public Module
while (ss >> buf)
modes.push_back(buf);
- ServerInstance->Modes->Process(modes, u);
+ ServerInstance->Parser.CallHandler("MODE", modes, u);
}
};
diff --git a/src/modules/m_opermotd.cpp b/src/modules/m_opermotd.cpp
index bd1853d43..f6cb5853c 100644
--- a/src/modules/m_opermotd.cpp
+++ b/src/modules/m_opermotd.cpp
@@ -43,28 +43,27 @@ class CommandOpermotd : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- if (!parameters.empty())
+ if ((!parameters.empty()) && (parameters[0].find('.') != std::string::npos))
return ROUTE_OPT_UCAST(parameters[0]);
return ROUTE_LOCALONLY;
}
void ShowOperMOTD(User* user)
{
- const std::string& servername = ServerInstance->Config->ServerName;
if (opermotd.empty())
{
- user->SendText(":%s 455 %s :OPERMOTD file is missing", servername.c_str(), user->nick.c_str());
+ user->WriteRemoteNumeric(455, "OPERMOTD file is missing");
return;
}
- user->SendText(":%s 375 %s :- IRC Operators Message of the Day", servername.c_str(), user->nick.c_str());
+ user->WriteRemoteNumeric(375, "- IRC Operators Message of the Day");
for (file_cache::const_iterator i = opermotd.begin(); i != opermotd.end(); ++i)
{
- user->SendText(":%s 372 %s :- %s", servername.c_str(), user->nick.c_str(), i->c_str());
+ user->WriteRemoteNumeric(372, InspIRCd::Format("- %s", i->c_str()));
}
- user->SendText(":%s 376 %s :- End of OPERMOTD", servername.c_str(), user->nick.c_str());
+ user->WriteRemoteNumeric(376, "- End of OPERMOTD");
}
};
diff --git a/src/modules/m_operprefix.cpp b/src/modules/m_operprefix.cpp
index 3bf4c8434..d66f99450 100644
--- a/src/modules/m_operprefix.cpp
+++ b/src/modules/m_operprefix.cpp
@@ -29,12 +29,12 @@
class OperPrefixMode : public PrefixMode
{
public:
- OperPrefixMode(Module* Creator) : PrefixMode(Creator, "operprefix", 'y')
+ OperPrefixMode(Module* Creator)
+ : PrefixMode(Creator, "operprefix", 'y', OPERPREFIX_VALUE)
{
std::string pfx = ServerInstance->Config->ConfValue("operprefix")->getString("prefix", "!");
prefix = pfx.empty() ? '!' : pfx[0];
levelrequired = INT_MAX;
- prefixrank = OPERPREFIX_VALUE;
}
};
@@ -72,18 +72,26 @@ class ModuleOperPrefixMode : public Module
return MOD_RES_PASSTHRU;
}
+ void OnPostJoin(Membership* memb)
+ {
+ if ((!IS_LOCAL(memb->user)) || (!memb->user->IsOper()) || (memb->user->IsModeSet(hideopermode)))
+ return;
+
+ if (memb->HasMode(&opm))
+ return;
+
+ // The user was force joined and OnUserPreJoin() did not run. Set the operprefix now.
+ Modes::ChangeList changelist;
+ changelist.push_add(&opm, memb->user->nick);
+ ServerInstance->Modes.Process(ServerInstance->FakeClient, memb->chan, NULL, changelist);
+ }
+
void SetOperPrefix(User* user, bool add)
{
- std::vector<std::string> modechange;
- modechange.push_back("");
- modechange.push_back(add ? "+" : "-");
- modechange[1].push_back(opm.GetModeChar());
- modechange.push_back(user->nick);
- for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++)
- {
- modechange[0] = (*v)->chan->name;
- ServerInstance->Modes->Process(modechange, ServerInstance->FakeClient);
- }
+ Modes::ChangeList changelist;
+ changelist.push(&opm, add, user->nick);
+ for (User::ChanList::iterator v = user->chans.begin(); v != user->chans.end(); v++)
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, (*v)->chan, NULL, changelist);
}
void OnPostOper(User* user, const std::string& opername, const std::string& opertype) CXX11_OVERRIDE
diff --git a/src/modules/m_override.cpp b/src/modules/m_override.cpp
index 756ef8edc..fd09dd6ec 100644
--- a/src/modules/m_override.cpp
+++ b/src/modules/m_override.cpp
@@ -25,6 +25,7 @@
#include "inspircd.h"
+#include "modules/invite.h"
class ModuleOverride : public Module
{
@@ -34,15 +35,13 @@ class ModuleOverride : public Module
ChanModeReference inviteonly;
ChanModeReference key;
ChanModeReference limit;
+ Invite::API invapi;
- static bool IsOverride(unsigned int userlevel, const std::string& modeline)
+ static bool IsOverride(unsigned int userlevel, const Modes::ChangeList::List& list)
{
- for (std::string::const_iterator i = modeline.begin(); i != modeline.end(); ++i)
+ for (Modes::ChangeList::List::const_iterator i = list.begin(); i != list.end(); ++i)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
- if (!mh)
- continue;
-
+ ModeHandler* mh = i->mh;
if (mh->GetLevelRequired() > userlevel)
return true;
}
@@ -59,7 +58,7 @@ class ModuleOverride : public Module
}
if (NoisyOverride)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass %s", chan->name.c_str(), user->nick.c_str(), bypasswhat);
+ chan->WriteNotice(InspIRCd::Format("%s used oper override to bypass %s", user->nick.c_str(), bypasswhat));
ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass " + mode + " on " + chan->name);
return MOD_RES_ALLOW;
}
@@ -70,6 +69,7 @@ class ModuleOverride : public Module
, inviteonly(this, "inviteonly")
, key(this, "key")
, limit(this, "limit")
+ , invapi(this)
{
}
@@ -121,7 +121,8 @@ class ModuleOverride : public Module
if (source->IsOper() && CanOverride(source,"KICK"))
{
// If the kicker's status is less than the target's, or the kicker's status is less than or equal to voice
- if ((memb->chan->GetPrefixValue(source) < memb->getRank()) || (memb->chan->GetPrefixValue(source) <= VOICE_VALUE))
+ if ((memb->chan->GetPrefixValue(source) < memb->getRank()) || (memb->chan->GetPrefixValue(source) <= VOICE_VALUE) ||
+ (memb->chan->GetPrefixValue(source) == HALFOP_VALUE && memb->getRank() == HALFOP_VALUE))
{
ServerInstance->SNO->WriteGlobalSno('v',source->nick+" used oper override to kick "+memb->user->nick+" on "+memb->chan->name+" ("+reason+")");
return MOD_RES_ALLOW;
@@ -130,23 +131,42 @@ class ModuleOverride : public Module
return MOD_RES_PASSTHRU;
}
- ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters) CXX11_OVERRIDE
+ ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
{
if (!channel)
return MOD_RES_PASSTHRU;
if (!source->IsOper() || !IS_LOCAL(source))
return MOD_RES_PASSTHRU;
+ const Modes::ChangeList::List& list = modes.getlist();
unsigned int mode = channel->GetPrefixValue(source);
- if (!IsOverride(mode, parameters[1]))
+ if (!IsOverride(mode, list))
return MOD_RES_PASSTHRU;
if (CanOverride(source, "MODE"))
{
- std::string msg = source->nick+" overriding modes:";
- for(unsigned int i=0; i < parameters.size(); i++)
- msg += " " + parameters[i];
+ std::string msg = source->nick + " overriding modes: ";
+
+ // Construct a MODE string in the old format for sending it as a snotice
+ std::string params;
+ char pm = 0;
+ for (Modes::ChangeList::List::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ const Modes::Change& item = *i;
+ if (!item.param.empty())
+ params.append(1, ' ').append(item.param);
+
+ char wanted_pm = (item.adding ? '+' : '-');
+ if (wanted_pm != pm)
+ {
+ pm = wanted_pm;
+ msg += pm;
+ }
+
+ msg += item.mh->GetModeChar();
+ }
+ msg += params;
ServerInstance->SNO->WriteGlobalSno('v',msg);
return MOD_RES_ALLOW;
}
@@ -161,7 +181,7 @@ class ModuleOverride : public Module
{
if (chan->IsModeSet(inviteonly) && (CanOverride(user,"INVITE")))
{
- if (!user->IsInvited(chan))
+ if (!invapi->IsInvited(user, chan))
return HandleJoinOverride(user, chan, keygiven, "invite-only", "+i");
return MOD_RES_ALLOW;
}
diff --git a/src/modules/m_passforward.cpp b/src/modules/m_passforward.cpp
index 8cdd343b1..3050dba0b 100644
--- a/src/modules/m_passforward.cpp
+++ b/src/modules/m_passforward.cpp
@@ -96,7 +96,7 @@ class ModulePassForward : public Module
tmp.clear();
FormatStr(tmp,forwardcmd, user);
- ServerInstance->Parser->ProcessBuffer(tmp,user);
+ ServerInstance->Parser.ProcessBuffer(tmp,user);
}
};
diff --git a/src/modules/m_password_hash.cpp b/src/modules/m_password_hash.cpp
index 89b6605b9..09cdbb402 100644
--- a/src/modules/m_password_hash.cpp
+++ b/src/modules/m_password_hash.cpp
@@ -36,14 +36,21 @@ class CommandMkpasswd : public Command
{
if (!algo.compare(0, 5, "hmac-", 5))
{
- std::string type = algo.substr(5);
+ std::string type(algo, 5);
HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + type);
if (!hp)
{
user->WriteNotice("Unknown hash type");
return;
}
- std::string salt = ServerInstance->GenRandomStr(6, false);
+
+ if (hp->IsKDF())
+ {
+ user->WriteNotice(type + " does not support HMAC");
+ return;
+ }
+
+ std::string salt = ServerInstance->GenRandomStr(hp->out_size, false);
std::string target = hp->hmac(salt, stuff);
std::string str = BinToBase64(salt) + "$" + BinToBase64(target, NULL, 0);
@@ -54,7 +61,7 @@ class CommandMkpasswd : public Command
if (hp)
{
/* Now attempt to generate a hash */
- std::string hexsum = hp->hexsum(stuff);
+ std::string hexsum = hp->Generate(stuff);
user->WriteNotice(algo + " hashed password for " + stuff + " is " + hexsum);
}
else
@@ -84,10 +91,17 @@ class ModuleOperHash : public Module
{
if (!hashtype.compare(0, 5, "hmac-", 5))
{
- std::string type = hashtype.substr(5);
+ std::string type(hashtype, 5);
HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + type);
if (!hp)
return MOD_RES_PASSTHRU;
+
+ if (hp->IsKDF())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Tried to use HMAC with %s, which does not support HMAC", type.c_str());
+ return MOD_RES_DENY;
+ }
+
// this is a valid hash, from here on we either accept or deny
std::string::size_type sep = data.find('$');
if (sep == std::string::npos)
@@ -106,15 +120,14 @@ class ModuleOperHash : public Module
/* Is this a valid hash name? */
if (hp)
{
- /* Compare the hash in the config to the generated hash */
- if (data == hp->hexsum(input))
+ if (hp->Compare(input, data))
return MOD_RES_ALLOW;
else
/* No match, and must be hashed, forbid */
return MOD_RES_DENY;
}
- /* Not a hash, fall through to strcmp in core */
+ // We don't handle this type, let other mods or the core decide
return MOD_RES_PASSTHRU;
}
diff --git a/src/modules/m_pbkdf2.cpp b/src/modules/m_pbkdf2.cpp
new file mode 100644
index 000000000..314f6b836
--- /dev/null
+++ b/src/modules/m_pbkdf2.cpp
@@ -0,0 +1,262 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+// Format:
+// Iterations:B64(Hash):B64(Salt)
+// E.g.
+// 10200:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+class PBKDF2Hash
+{
+ public:
+ unsigned int iterations;
+ unsigned int length;
+ std::string salt;
+ std::string hash;
+
+ PBKDF2Hash(unsigned int itr, unsigned int dkl, const std::string& slt, const std::string& hsh = "")
+ : iterations(itr), length(dkl), salt(slt), hash(hsh)
+ {
+ }
+
+ PBKDF2Hash(const std::string& data)
+ {
+ irc::sepstream ss(data, ':');
+ std::string tok;
+
+ ss.GetToken(tok);
+ this->iterations = ConvToInt(tok);
+
+ ss.GetToken(tok);
+ this->hash = Base64ToBin(tok);
+
+ ss.GetToken(tok);
+ this->salt = Base64ToBin(tok);
+
+ this->length = this->hash.length();
+ }
+
+ std::string ToString()
+ {
+ if (!IsValid())
+ return "";
+ return ConvToStr(this->iterations) + ":" + BinToBase64(this->hash) + ":" + BinToBase64(this->salt);
+ }
+
+ bool IsValid()
+ {
+ if (!this->iterations || !this->length || this->salt.empty() || this->hash.empty())
+ return false;
+ return true;
+ }
+};
+
+class PBKDF2Provider : public HashProvider
+{
+ public:
+ HashProvider* provider;
+ unsigned int iterations;
+ unsigned int dkey_length;
+
+ std::string PBKDF2(const std::string& pass, const std::string& salt, unsigned int itr = 0, unsigned int dkl = 0)
+ {
+ size_t blocks = std::ceil((double)dkl / provider->out_size);
+
+ std::string output;
+ std::string tmphash;
+ std::string salt_block = salt;
+ for (size_t block = 1; block <= blocks; block++)
+ {
+ char salt_data[4];
+ for (size_t i = 0; i < sizeof(salt_data); i++)
+ salt_data[i] = block >> (24 - i * 8) & 0x0F;
+
+ salt_block.erase(salt.length());
+ salt_block.append(salt_data, sizeof(salt_data));
+
+ std::string blockdata = provider->hmac(pass, salt_block);
+ std::string lasthash = blockdata;
+ for (size_t iter = 1; iter < itr; iter++)
+ {
+ tmphash = provider->hmac(pass, lasthash);
+ for (size_t i = 0; i < provider->out_size; i++)
+ blockdata[i] ^= tmphash[i];
+
+ lasthash.swap(tmphash);
+ }
+ output += blockdata;
+ }
+
+ output.erase(dkl);
+ return output;
+ }
+
+ std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
+ {
+ PBKDF2Hash hs(this->iterations, this->dkey_length, ServerInstance->GenRandomStr(dkey_length, false));
+ hs.hash = PBKDF2(data, hs.salt, this->iterations, this->dkey_length);
+ return hs.ToString();
+ }
+
+ bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
+ {
+ PBKDF2Hash hs(hash);
+ if (!hs.IsValid())
+ return false;
+
+ std::string cmp = PBKDF2(input, hs.salt, hs.iterations, hs.length);
+ return (cmp == hs.hash);
+ }
+
+ std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
+ {
+ return raw;
+ }
+
+ PBKDF2Provider(Module* mod, HashProvider* hp)
+ : HashProvider(mod, "pbkdf2-hmac-" + hp->name.substr(hp->name.find('/') + 1))
+ , provider(hp)
+ {
+ DisableAutoRegister();
+ }
+};
+
+class ModulePBKDF2 : public Module
+{
+ std::vector<PBKDF2Provider*> providers;
+
+ void GetConfig()
+ {
+ // First set the common values
+ ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2");
+ unsigned int global_iterations = tag->getInt("iterations", 12288, 1);
+ unsigned int global_dkey_length = tag->getInt("length", 32, 1, 1024);
+ for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
+ {
+ PBKDF2Provider* pi = *i;
+ pi->iterations = global_iterations;
+ pi->dkey_length = global_dkey_length;
+ }
+
+ // Then the specific values
+ ConfigTagList tags = ServerInstance->Config->ConfTags("pbkdf2prov");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ tag = i->second;
+ std::string hash_name = "hash/" + tag->getString("hash");
+ for (std::vector<PBKDF2Provider*>::iterator j = providers.begin(); j != providers.end(); ++j)
+ {
+ PBKDF2Provider* pi = *j;
+ if (pi->provider->name != hash_name)
+ continue;
+
+ pi->iterations = tag->getInt("iterations", global_iterations, 1);
+ pi->dkey_length = tag->getInt("length", global_dkey_length, 1, 1024);
+ }
+ }
+ }
+
+ public:
+ ~ModulePBKDF2()
+ {
+ stdalgo::delete_all(providers);
+ }
+
+ void Prioritize() CXX11_OVERRIDE
+ {
+ OnLoadModule(NULL);
+ }
+
+ void OnLoadModule(Module* mod) CXX11_OVERRIDE
+ {
+ bool newProv = false;
+ // As the module doesn't tell us what ServiceProviders it has, let's iterate all (yay ...) the ServiceProviders
+ // Good thing people don't run loading and unloading those all the time
+ for (std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.begin(); i != ServerInstance->Modules->DataProviders.end(); ++i)
+ {
+ ServiceProvider* provider = i->second;
+
+ // Does the service belong to the new mod?
+ // In the case this is our first run (mod == NULL, continue anyway)
+ if (mod && provider->creator != mod)
+ continue;
+
+ // Check if it's a hash provider
+ if (provider->name.compare(0, 5, "hash/"))
+ continue;
+
+ HashProvider* hp = static_cast<HashProvider*>(provider);
+
+ if (hp->IsKDF())
+ continue;
+
+ bool has_prov = false;
+ for (std::vector<PBKDF2Provider*>::const_iterator j = providers.begin(); j != providers.end(); ++j)
+ {
+ if ((*j)->provider == hp)
+ {
+ has_prov = true;
+ break;
+ }
+ }
+ if (has_prov)
+ continue;
+
+ newProv = true;
+
+ PBKDF2Provider* prov = new PBKDF2Provider(this, hp);
+ providers.push_back(prov);
+ ServerInstance->Modules->AddService(*prov);
+ }
+
+ if (newProv)
+ GetConfig();
+ }
+
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE
+ {
+ for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); )
+ {
+ PBKDF2Provider* item = *i;
+ if (item->provider->creator != mod)
+ {
+ ++i;
+ continue;
+ }
+
+ ServerInstance->Modules->DelService(*item);
+ delete item;
+ i = providers.erase(i);
+ }
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ GetConfig();
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Implements PBKDF2 hashing", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModulePBKDF2)
diff --git a/src/modules/m_permchannels.cpp b/src/modules/m_permchannels.cpp
index d23af04bc..d514e62a5 100644
--- a/src/modules/m_permchannels.cpp
+++ b/src/modules/m_permchannels.cpp
@@ -66,8 +66,8 @@ static bool WriteDatabase(PermChannel& permchanmode, Module* mod, bool save_list
std::ofstream stream(permchannelsnewconf.c_str());
if (!stream.is_open())
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot create database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot create database \"%s\"! %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new permchan db \"%s\": %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
return false;
}
@@ -137,25 +137,20 @@ static bool WriteDatabase(PermChannel& permchanmode, Module* mod, bool save_list
if (stream.fail())
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot write to new database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot write to new database \"%s\"! %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new permchan db \"%s\": %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
return false;
}
stream.close();
#ifdef _WIN32
- if (remove(permchannelsconf.c_str()))
- {
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot remove old database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot remove old database: %s (%d)", strerror(errno), errno);
- return false;
- }
+ remove(permchannelsconf.c_str());
#endif
// Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
if (rename(permchannelsnewconf.c_str(), permchannelsconf.c_str()) < 0)
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot move new to old database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot replace old database \"%s\" with new database \"%s\"! %s (%d)", permchannelsconf.c_str(), permchannelsnewconf.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old permchan db \"%s\" with new db \"%s\": %s (%d)", permchannelsconf.c_str(), permchannelsnewconf.c_str(), strerror(errno), errno);
return false;
}
@@ -212,16 +207,16 @@ public:
c = new Channel(channel, TS);
unsigned int topicset = tag->getInt("topicts");
- c->topic = tag->getString("topic");
+ std::string topic = tag->getString("topic");
- if ((topicset != 0) || (!c->topic.empty()))
+ if ((topicset != 0) || (!topic.empty()))
{
if (topicset == 0)
topicset = ServerInstance->Time();
- c->topicset = topicset;
- c->setby = tag->getString("topicsetby");
- if (c->setby.empty())
- c->setby = ServerInstance->Config->ServerName;
+ std::string topicsetby = tag->getString("topicsetby");
+ if (topicsetby.empty())
+ topicsetby = ServerInstance->Config->ServerName;
+ c->SetTopic(ServerInstance->FakeClient, topic, topicset, &topicsetby);
}
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Added %s with topic %s", channel.c_str(), c->topic.c_str());
@@ -241,7 +236,7 @@ public:
ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
if (mode)
{
- if (mode->GetNumParams(true))
+ if (mode->NeedsParam(true))
list.GetToken(par);
else
par.clear();
@@ -249,6 +244,10 @@ public:
mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, c, par, true);
}
}
+
+ // We always apply the permchannels mode to permanent channels.
+ par.clear();
+ p.OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, c, par, true);
}
}
}
diff --git a/src/modules/m_redirect.cpp b/src/modules/m_redirect.cpp
index e822676bf..b14de9ff9 100644
--- a/src/modules/m_redirect.cpp
+++ b/src/modules/m_redirect.cpp
@@ -38,7 +38,7 @@ class Redirect : public ParamMode<Redirect, LocalStringExt>
{
if (!ServerInstance->IsChannel(parameter))
{
- source->WriteNumeric(ERR_NOSUCHCHANNEL, "%s :Invalid channel name", parameter.c_str());
+ source->WriteNumeric(ERR_NOSUCHCHANNEL, parameter, "Invalid channel name");
return MODEACTION_DENY;
}
}
@@ -48,12 +48,12 @@ class Redirect : public ParamMode<Redirect, LocalStringExt>
Channel* c = ServerInstance->FindChan(parameter);
if (!c)
{
- source->WriteNumeric(690, ":Target channel %s must exist to be set as a redirect.",parameter.c_str());
+ source->WriteNumeric(690, InspIRCd::Format("Target channel %s must exist to be set as a redirect.", parameter.c_str()));
return MODEACTION_DENY;
}
else if (c->GetPrefixValue(source) < OP_VALUE)
{
- source->WriteNumeric(690, ":You must be opped on %s to set it as a redirect.",parameter.c_str());
+ source->WriteNumeric(690, InspIRCd::Format("You must be opped on %s to set it as a redirect.", parameter.c_str()));
return MODEACTION_DENY;
}
}
@@ -119,19 +119,19 @@ class ModuleRedirect : public Module
Channel* destchan = ServerInstance->FindChan(channel);
if (destchan && destchan->IsModeSet(re))
{
- user->WriteNumeric(470, "%s * :You may not join this channel. A redirect is set, but you may not be redirected as it is a circular loop.", cname.c_str());
+ user->WriteNumeric(470, cname, '*', "You may not join this channel. A redirect is set, but you may not be redirected as it is a circular loop.");
return MOD_RES_DENY;
}
/* We check the bool value here to make sure we have it enabled, if we don't then
usermode +L might be assigned to something else. */
if (UseUsermode && user->IsModeSet(re_u))
{
- user->WriteNumeric(470, "%s %s :Force redirection stopped.", cname.c_str(), channel.c_str());
+ user->WriteNumeric(470, cname, channel, "Force redirection stopped.");
return MOD_RES_DENY;
}
else
{
- user->WriteNumeric(470, "%s %s :You may not join this channel, so you are automatically being transferred to the redirect channel.", cname.c_str(), channel.c_str());
+ user->WriteNumeric(470, cname, channel, "You may not join this channel, so you are automatically being transferred to the redirect channel.");
Channel::JoinUser(user, channel);
return MOD_RES_DENY;
}
diff --git a/src/modules/m_regonlycreate.cpp b/src/modules/m_regonlycreate.cpp
index 0ffe5e085..78b20ef6b 100644
--- a/src/modules/m_regonlycreate.cpp
+++ b/src/modules/m_regonlycreate.cpp
@@ -49,7 +49,7 @@ class ModuleRegOnlyCreate : public Module
return MOD_RES_PASSTHRU;
// XXX. there may be a better numeric for this..
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must have a registered nickname to create a new channel", cname.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, cname, "You must have a registered nickname to create a new channel");
return MOD_RES_DENY;
}
diff --git a/src/modules/m_remove.cpp b/src/modules/m_remove.cpp
index ed9b6ce25..5872b5978 100644
--- a/src/modules/m_remove.cpp
+++ b/src/modules/m_remove.cpp
@@ -38,6 +38,8 @@ class RemoveBase : public Command
ChanModeReference& nokicksmode;
public:
+ unsigned int protectedrank;
+
RemoveBase(Module* Creator, bool& snk, ChanModeReference& nkm, const char* cmdn)
: Command(Creator, cmdn, 2, 3)
, supportnokicks(snk)
@@ -45,12 +47,15 @@ class RemoveBase : public Command
{
}
- CmdResult HandleRMB(const std::vector<std::string>& parameters, User *user, bool neworder)
+ CmdResult HandleRMB(const std::vector<std::string>& parameters, User *user, bool fpart)
{
User* target;
Channel* channel;
std::string reason;
+ // If the command is a /REMOVE then detect the parameter order
+ bool neworder = ((fpart) || (parameters[0][0] == '#'));
+
/* Set these to the parameters needed, the new version of this module switches it's parameters around
* supplying a new command with the new order while keeping the old /remove with the older order.
* /remove <nick> <channel> [reason ...]
@@ -71,22 +76,19 @@ class RemoveBase : public Command
/* Fix by brain - someone needs to learn to validate their input! */
if ((!target) || (target->registered != REG_ALL) || (!channel))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", !channel ? channame.c_str() : username.c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(channel ? username.c_str() : channame.c_str()));
return CMD_FAILURE;
}
if (!channel->HasUser(target))
{
- user->WriteServ( "NOTICE %s :*** The user %s is not on channel %s", user->nick.c_str(), target->nick.c_str(), channel->name.c_str());
+ user->WriteNotice(InspIRCd::Format("*** The user %s is not on channel %s", target->nick.c_str(), channel->name.c_str()));
return CMD_FAILURE;
}
- int ulevel = channel->GetPrefixValue(user);
- int tlevel = channel->GetPrefixValue(target);
-
if (target->server->IsULine())
{
- user->WriteNumeric(482, "%s :Only a u-line may remove a u-line from a channel.", channame.c_str());
+ user->WriteNumeric(482, channame, "Only a u-line may remove a u-line from a channel.");
return CMD_FAILURE;
}
@@ -96,13 +98,26 @@ class RemoveBase : public Command
/* We'll let everyone remove their level and below, eg:
* ops can remove ops, halfops, voices, and those with no mode (no moders actually are set to 1)
* a ulined target will get a higher level than it's possible for a /remover to get..so they're safe.
- * Nobody may remove a founder.
+ * Nobody may remove people with >= protectedrank rank.
*/
- if ((!IS_LOCAL(user)) || ((ulevel > VOICE_VALUE) && (ulevel >= tlevel) && (tlevel != 50000)))
+ unsigned int ulevel = channel->GetPrefixValue(user);
+ unsigned int tlevel = channel->GetPrefixValue(target);
+ if ((!IS_LOCAL(user)) || ((ulevel > VOICE_VALUE) && (ulevel >= tlevel) && ((protectedrank == 0) || (tlevel < protectedrank))))
{
- // REMOVE/FPART will be sent to the target's server and it will reply with a PART (or do nothing if it doesn't understand the command)
+ // REMOVE will be sent to the target's server and it will reply with a PART (or do nothing if it doesn't understand the command)
if (!IS_LOCAL(target))
+ {
+ // Send an ENCAP REMOVE with parameters being in the old <user> <chan> order which is
+ // compatible with both 2.0 and 3.0. This also turns FPART into REMOVE.
+ std::vector<std::string> p;
+ p.push_back(target->uuid);
+ p.push_back(channel->name);
+ if (parameters.size() > 2)
+ p.push_back(":" + parameters[2]);
+ ServerInstance->PI->SendEncapsulatedData(target->server->GetName(), "REMOVE", p, user);
+
return CMD_SUCCESS;
+ }
std::string reasonparam;
@@ -115,27 +130,26 @@ class RemoveBase : public Command
/* Build up the part reason string. */
reason = "Removed by " + user->nick + ": " + reasonparam;
- channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s removed %s from the channel", channel->name.c_str(), user->nick.c_str(), target->nick.c_str());
+ channel->WriteNotice(InspIRCd::Format("%s removed %s from the channel", user->nick.c_str(), target->nick.c_str()));
target->WriteNotice("*** " + user->nick + " removed you from " + channel->name + " with the message: " + reasonparam);
channel->PartUser(target, reason);
}
else
{
- user->WriteServ( "NOTICE %s :*** You do not have access to /remove %s from %s", user->nick.c_str(), target->nick.c_str(), channel->name.c_str());
+ user->WriteNotice(InspIRCd::Format("*** You do not have access to /remove %s from %s", target->nick.c_str(), channel->name.c_str()));
return CMD_FAILURE;
}
}
else
{
/* m_nokicks.so was loaded and +Q was set, block! */
- user->WriteNumeric(ERR_RESTRICTED, "%s :Can't remove user %s from channel (nokicks mode is set)", channel->name.c_str(), target->nick.c_str());
+ user->WriteNumeric(ERR_RESTRICTED, channel->name, InspIRCd::Format("Can't remove user %s from channel (nokicks mode is set)", target->nick.c_str()));
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) = 0;
};
/** Handle /REMOVE
@@ -146,7 +160,7 @@ class CommandRemove : public RemoveBase
CommandRemove(Module* Creator, bool& snk, ChanModeReference& nkm)
: RemoveBase(Creator, snk, nkm, "REMOVE")
{
- syntax = "<nick> <channel> [<reason>]";
+ syntax = "<channel> <nick> [<reason>]";
TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
}
@@ -154,14 +168,6 @@ class CommandRemove : public RemoveBase
{
return HandleRMB(parameters, user, false);
}
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
- }
};
/** Handle /FPART
@@ -180,14 +186,6 @@ class CommandFpart : public RemoveBase
{
return HandleRMB(parameters, user, true);
}
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- User* dest = ServerInstance->FindNick(parameters[1]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
- }
};
class ModuleRemove : public Module
@@ -212,7 +210,9 @@ class ModuleRemove : public Module
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- supportnokicks = ServerInstance->Config->ConfValue("remove")->getBool("supportnokicks");
+ ConfigTag* tag = ServerInstance->Config->ConfValue("remove");
+ supportnokicks = tag->getBool("supportnokicks");
+ cmd1.protectedrank = cmd2.protectedrank = tag->getInt("protectedrank", 50000);
}
Version GetVersion() CXX11_OVERRIDE
diff --git a/src/modules/m_repeat.cpp b/src/modules/m_repeat.cpp
index d8fccbffc..21bca0f3f 100644
--- a/src/modules/m_repeat.cpp
+++ b/src/modules/m_repeat.cpp
@@ -110,7 +110,7 @@ class RepeatMode : public ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >
{
mx[1][0] = i + 1;
for (unsigned int j = 0; j < l2; j++)
- mx[1][j + 1] = std::min(std::min(mx[1][j] + 1, mx[0][j + 1] + 1), mx[0][j] + ((s1[i] == s2[j]) ? 0 : 1));
+ mx[1][j + 1] = std::min(std::min(mx[1][j] + 1, mx[0][j + 1] + 1), mx[0][j] + ((s1[i] == s2[j]) ? 0 : 1));
mx[0].swap(mx[1]);
}
@@ -122,15 +122,15 @@ class RepeatMode : public ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >
RepeatMode(Module* Creator)
: ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >(Creator, "repeat", 'E')
- , MemberInfoExt("repeat_memb", Creator)
+ , MemberInfoExt("repeat_memb", ExtensionItem::EXT_MEMBERSHIP, Creator)
{
}
void OnUnset(User* source, Channel* chan)
{
// Unset the per-membership extension when the mode is removed
- const UserMembList* users = chan->GetUsers();
- for (UserMembCIter i = users->begin(); i != users->end(); ++i)
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
MemberInfoExt.unset(i->second);
}
@@ -370,17 +370,15 @@ class RepeatModule : public Module
{
if (settings->Action == ChannelSettings::ACT_BLOCK)
{
- user->WriteNotice("*** This line is too similiar to one of your last lines.");
+ user->WriteNotice("*** This line is too similar to one of your last lines.");
return MOD_RES_DENY;
}
if (settings->Action == ChannelSettings::ACT_BAN)
{
- std::vector<std::string> parameters;
- parameters.push_back(memb->chan->name);
- parameters.push_back("+b");
- parameters.push_back("*!*@" + user->dhost);
- ServerInstance->Modes->Process(parameters, ServerInstance->FakeClient);
+ Modes::ChangeList changelist;
+ changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->dhost);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist);
}
memb->chan->KickUser(ServerInstance->FakeClient, user, "Repeat flood");
@@ -396,7 +394,7 @@ class RepeatModule : public Module
Version GetVersion() CXX11_OVERRIDE
{
- return Version("Provides the +E channel mode - for blocking of similiar messages", VF_COMMON|VF_VENDOR, rm.GetModuleSettings());
+ return Version("Provides the +E channel mode - for blocking of similar messages", VF_COMMON|VF_VENDOR, rm.GetModuleSettings());
}
};
diff --git a/src/modules/m_restrictchans.cpp b/src/modules/m_restrictchans.cpp
index b619ee448..9c7ed1213 100644
--- a/src/modules/m_restrictchans.cpp
+++ b/src/modules/m_restrictchans.cpp
@@ -24,7 +24,7 @@
class ModuleRestrictChans : public Module
{
- std::set<std::string, irc::insensitive_swo> allowchans;
+ insp::flat_set<std::string, irc::insensitive_swo> allowchans;
public:
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
@@ -47,7 +47,7 @@ class ModuleRestrictChans : public Module
// user is not an oper and its not in the allow list
if ((!user->IsOper()) && (allowchans.find(cname) == allowchans.end()))
{
- user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Only IRC operators may create new channels", cname.c_str());
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, cname, "Only IRC operators may create new channels");
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_restrictmsg.cpp b/src/modules/m_restrictmsg.cpp
index e0887e587..8ca531ed5 100644
--- a/src/modules/m_restrictmsg.cpp
+++ b/src/modules/m_restrictmsg.cpp
@@ -33,12 +33,13 @@ class ModuleRestrictMsg : public Module
// message allowed if:
// (1) the sender is opered
// (2) the recipient is opered
+ // (3) the recipient is on a ulined server
// anything else, blocked.
- if (u->IsOper() || user->IsOper())
+ if (u->IsOper() || user->IsOper() || u->server->IsULine())
{
return MOD_RES_PASSTHRU;
}
- user->WriteNumeric(ERR_CANTSENDTOUSER, "%s :You are not permitted to send private messages to this user", u->nick.c_str());
+ user->WriteNumeric(ERR_CANTSENDTOUSER, u->nick, "You are not permitted to send private messages to this user");
return MOD_RES_DENY;
}
diff --git a/src/modules/m_ripemd160.cpp b/src/modules/m_ripemd160.cpp
index 261cd1e27..8d3131bc0 100644
--- a/src/modules/m_ripemd160.cpp
+++ b/src/modules/m_ripemd160.cpp
@@ -434,13 +434,13 @@ class RIProv : public HashProvider
return (byte *)hashcode;
}
public:
- std::string sum(const std::string& data)
+ std::string GenerateRaw(const std::string& data)
{
char* rv = (char*)RMD((byte*)data.data(), data.length(), NULL);
return std::string(rv, RMDsize / 8);
}
- RIProv(Module* m) : HashProvider(m, "hash/ripemd160", 20, 64) {}
+ RIProv(Module* m) : HashProvider(m, "ripemd160", 20, 64) {}
};
class ModuleRIPEMD160 : public Module
diff --git a/src/modules/m_rline.cpp b/src/modules/m_rline.cpp
index 2aee89ad2..97fbf169a 100644
--- a/src/modules/m_rline.cpp
+++ b/src/modules/m_rline.cpp
@@ -284,12 +284,12 @@ class ModuleRLine : public Module
initing = false;
}
- ModResult OnStats(char symbol, User* user, string_list &results) CXX11_OVERRIDE
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'R')
+ if (stats.GetSymbol() != 'R')
return MOD_RES_PASSTHRU;
- ServerInstance->XLines->InvokeStats("R", 223, user, results);
+ ServerInstance->XLines->InvokeStats("R", 223, stats);
return MOD_RES_DENY;
}
diff --git a/src/modules/m_rmode.cpp b/src/modules/m_rmode.cpp
index dde9f496e..37c6e62ff 100644
--- a/src/modules/m_rmode.cpp
+++ b/src/modules/m_rmode.cpp
@@ -60,17 +60,18 @@ class CommandRMode : public Command
PrefixMode* pm;
ListModeBase* lm;
ListModeBase::ModeList* ml;
- irc::modestacker modestack(false);
+ Modes::ChangeList changelist;
if ((pm = mh->IsPrefixMode()))
{
// As user prefix modes don't have a GetList() method, let's iterate through the channel's users.
- for (UserMembIter it = chan->userlist.begin(); it != chan->userlist.end(); ++it)
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator it = users.begin(); it != users.end(); ++it)
{
if (!InspIRCd::Match(it->first->nick, pattern))
continue;
- if (it->second->hasMode(modeletter) && !((it->first == user) && (pm->GetPrefixRank() > VOICE_VALUE)))
- modestack.Push(modeletter, it->first->nick);
+ if (it->second->HasMode(pm) && !((it->first == user) && (pm->GetPrefixRank() > VOICE_VALUE)))
+ changelist.push_remove(mh, it->first->nick);
}
}
else if ((lm = mh->IsListModeBase()) && ((ml = lm->GetList(chan)) != NULL))
@@ -79,23 +80,16 @@ class CommandRMode : public Command
{
if (!InspIRCd::Match(it->mask, pattern))
continue;
- modestack.Push(modeletter, it->mask);
+ changelist.push_remove(mh, it->mask);
}
}
else
{
if (chan->IsModeSet(mh))
- modestack.Push(modeletter);
- }
-
- parameterlist stackresult;
- stackresult.push_back(chan->name);
- while (modestack.GetStackedLine(stackresult))
- {
- ServerInstance->Modes->Process(stackresult, user);
- stackresult.erase(stackresult.begin() + 1, stackresult.end());
+ changelist.push_remove(mh);
}
+ ServerInstance->Modes->Process(user, chan, NULL, changelist);
return CMD_SUCCESS;
}
};
diff --git a/src/modules/m_sajoin.cpp b/src/modules/m_sajoin.cpp
index d1321947b..8bf865319 100644
--- a/src/modules/m_sajoin.cpp
+++ b/src/modules/m_sajoin.cpp
@@ -29,7 +29,7 @@ class CommandSajoin : public Command
CommandSajoin(Module* Creator) : Command(Creator,"SAJOIN", 1)
{
allow_empty_last_param = false;
- flags_needed = 'o'; Penalty = 0; syntax = "[<nick>] <channel>[,<channel>]";
+ flags_needed = 'o'; syntax = "[<nick>] <channel>[,<channel>]";
TRANSLATE2(TR_NICK, TR_TEXT);
}
@@ -53,7 +53,7 @@ class CommandSajoin : public Command
if (dest->server->IsULine())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a u-lined client");
return CMD_FAILURE;
}
if (IS_LOCAL(user) && !ServerInstance->IsChannel(channel))
@@ -66,7 +66,7 @@ class CommandSajoin : public Command
Channel* chan = ServerInstance->FindChan(channel);
if ((chan) && (chan->HasUser(dest)))
{
- user->SendText(":" + user->server->GetName() + " NOTICE " + user->nick + " :*** " + dest->nick + " is already on " + channel);
+ user->WriteRemoteNotice("*** " + dest->nick + " is already on " + channel);
return CMD_FAILURE;
}
@@ -103,10 +103,7 @@ class CommandSajoin : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
diff --git a/src/modules/m_sakick.cpp b/src/modules/m_sakick.cpp
index 911b826dc..81a74502b 100644
--- a/src/modules/m_sakick.cpp
+++ b/src/modules/m_sakick.cpp
@@ -27,7 +27,7 @@ class CommandSakick : public Command
public:
CommandSakick(Module* Creator) : Command(Creator,"SAKICK", 2, 3)
{
- flags_needed = 'o'; Penalty = 0; syntax = "<channel> <nick> [reason]";
+ flags_needed = 'o'; syntax = "<channel> <nick> [reason]";
TRANSLATE3(TR_TEXT, TR_NICK, TR_TEXT);
}
@@ -42,7 +42,7 @@ class CommandSakick : public Command
if (dest->server->IsULine())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a u-lined client");
return CMD_FAILURE;
}
@@ -75,10 +75,7 @@ class CommandSakick : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* dest = ServerInstance->FindNick(parameters[1]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[1]);
}
};
diff --git a/src/modules/m_samode.cpp b/src/modules/m_samode.cpp
index 14f79aaf7..6288f5862 100644
--- a/src/modules/m_samode.cpp
+++ b/src/modules/m_samode.cpp
@@ -31,7 +31,7 @@ class CommandSamode : public Command
CommandSamode(Module* Creator) : Command(Creator,"SAMODE", 2)
{
allow_empty_last_param = false;
- flags_needed = 'o'; Penalty = 0; syntax = "<target> <modes> {<mode-parameters>}";
+ flags_needed = 'o'; syntax = "<target> <modes> {<mode-parameters>}";
active = false;
}
@@ -42,21 +42,35 @@ class CommandSamode : public Command
User* target = ServerInstance->FindNickOnly(parameters[0]);
if ((!target) || (target->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
- }
- User* target = ServerInstance->FindNick(parameters[0]);
- if ((target) && (target != user))
- {
- if (!user->HasPrivPermission("users/samode-usermodes", true))
+
+ // Changing the modes of another user requires a special permission
+ if ((target != user) && (!user->HasPrivPermission("users/samode-usermodes", true)))
return CMD_FAILURE;
}
+
+ // XXX: Make ModeParser clear LastParse
+ Modes::ChangeList emptychangelist;
+ ServerInstance->Modes->ProcessSingle(ServerInstance->FakeClient, NULL, ServerInstance->FakeClient, emptychangelist);
+
this->active = true;
- ServerInstance->Modes->Process(parameters, user);
- if (ServerInstance->Modes->GetLastParse().length())
- ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " +ServerInstance->Modes->GetLastParse());
+ CmdResult result = ServerInstance->Parser.CallHandler("MODE", parameters, user);
this->active = false;
+
+ if (result == CMD_SUCCESS)
+ {
+ // If lastparse is empty and the MODE command handler returned CMD_SUCCESS then
+ // the client queried the list of a listmode (e.g. /SAMODE #chan b), which was
+ // handled internally by the MODE command handler.
+ //
+ // Viewing the modes of a user or a channel can also result in CMD_SUCCESS, but
+ // that is not possible with /SAMODE because we require at least 2 parameters.
+ const std::string& lastparse = ServerInstance->Modes.GetLastParse();
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " + (lastparse.empty() ? irc::stringjoiner(parameters) : lastparse));
+ }
+
return CMD_SUCCESS;
}
};
@@ -75,7 +89,7 @@ class ModuleSaMode : public Module
return Version("Provides command SAMODE to allow opers to change modes on channels and users", VF_VENDOR);
}
- ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters) CXX11_OVERRIDE
+ ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
{
if (cmd.active)
return MOD_RES_ALLOW;
@@ -85,7 +99,7 @@ class ModuleSaMode : public Module
void Prioritize()
{
Module *override = ServerInstance->Modules->Find("m_override.so");
- ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_BEFORE, &override);
+ ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_BEFORE, override);
}
};
diff --git a/src/modules/m_sanick.cpp b/src/modules/m_sanick.cpp
index ca6be2211..c9ceba78e 100644
--- a/src/modules/m_sanick.cpp
+++ b/src/modules/m_sanick.cpp
@@ -29,7 +29,7 @@ class CommandSanick : public Command
CommandSanick(Module* Creator) : Command(Creator,"SANICK", 2)
{
allow_empty_last_param = false;
- flags_needed = 'o'; Penalty = 0; syntax = "<nick> <new-nick>";
+ flags_needed = 'o'; syntax = "<nick> <new-nick>";
TRANSLATE2(TR_NICK, TR_TEXT);
}
@@ -42,7 +42,7 @@ class CommandSanick : public Command
{
if (target && target->server->IsULine())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a u-lined client");
return CMD_FAILURE;
}
@@ -64,7 +64,7 @@ class CommandSanick : public Command
{
std::string oldnick = user->nick;
std::string newnick = target->nick;
- if (target->ChangeNick(parameters[1], true))
+ if (target->ChangeNick(parameters[1]))
{
ServerInstance->SNO->WriteGlobalSno('a', oldnick+" used SANICK to change "+newnick+" to "+parameters[1]);
}
@@ -79,10 +79,7 @@ class CommandSanick : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
diff --git a/src/modules/m_sapart.cpp b/src/modules/m_sapart.cpp
index 730bf0823..b51316dc5 100644
--- a/src/modules/m_sapart.cpp
+++ b/src/modules/m_sapart.cpp
@@ -28,7 +28,7 @@ class CommandSapart : public Command
public:
CommandSapart(Module* Creator) : Command(Creator,"SAPART", 2, 3)
{
- flags_needed = 'o'; Penalty = 0; syntax = "<nick> <channel>[,<channel>] [reason]";
+ flags_needed = 'o'; syntax = "<nick> <channel>[,<channel>] [reason]";
TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
}
@@ -48,7 +48,7 @@ class CommandSapart : public Command
if (dest->server->IsULine())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a u-lined client");
return CMD_FAILURE;
}
@@ -80,10 +80,7 @@ class CommandSapart : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
diff --git a/src/modules/m_saquit.cpp b/src/modules/m_saquit.cpp
index aa6aa0180..9f700ec5f 100644
--- a/src/modules/m_saquit.cpp
+++ b/src/modules/m_saquit.cpp
@@ -28,18 +28,18 @@ class CommandSaquit : public Command
public:
CommandSaquit(Module* Creator) : Command(Creator, "SAQUIT", 2, 2)
{
- flags_needed = 'o'; Penalty = 0; syntax = "<nick> <reason>";
+ flags_needed = 'o'; syntax = "<nick> <reason>";
TRANSLATE2(TR_NICK, TR_TEXT);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
{
User* dest = ServerInstance->FindNick(parameters[0]);
- if ((dest) && (!IS_SERVER(dest)) && (dest->registered == REG_ALL))
+ if ((dest) && (dest->registered == REG_ALL))
{
if (dest->server->IsULine())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a u-lined client");
return CMD_FAILURE;
}
@@ -61,10 +61,7 @@ class CommandSaquit : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
diff --git a/src/modules/m_sasl.cpp b/src/modules/m_sasl.cpp
index 074362651..9f7e1527b 100644
--- a/src/modules/m_sasl.cpp
+++ b/src/modules/m_sasl.cpp
@@ -23,17 +23,121 @@
#include "modules/account.h"
#include "modules/sasl.h"
#include "modules/ssl.h"
+#include "modules/spanningtree.h"
+
+static std::string sasl_target;
+
+class ServerTracker : public SpanningTreeEventListener
+{
+ bool online;
+
+ void Update(const Server* server, bool linked)
+ {
+ if (sasl_target == "*")
+ return;
+
+ if (InspIRCd::Match(server->GetName(), sasl_target))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_VERBOSE, "SASL target server \"%s\" %s", sasl_target.c_str(), (linked ? "came online" : "went offline"));
+ online = linked;
+ }
+ }
+
+ void OnServerLink(const Server* server) CXX11_OVERRIDE
+ {
+ Update(server, true);
+ }
+
+ void OnServerSplit(const Server* server) CXX11_OVERRIDE
+ {
+ Update(server, false);
+ }
+
+ public:
+ ServerTracker(Module* mod)
+ : SpanningTreeEventListener(mod)
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ if (sasl_target == "*")
+ {
+ online = true;
+ return;
+ }
+
+ online = false;
+
+ ProtocolInterface::ServerList servers;
+ ServerInstance->PI->GetServerList(servers);
+ for (ProtocolInterface::ServerList::const_iterator i = servers.begin(); i != servers.end(); ++i)
+ {
+ const ProtocolInterface::ServerInfo& server = *i;
+ if (InspIRCd::Match(server.servername, sasl_target))
+ {
+ online = true;
+ break;
+ }
+ }
+ }
+
+ bool IsOnline() const { return online; }
+};
+
+class SASLCap : public Cap::Capability
+{
+ std::string mechlist;
+ const ServerTracker& servertracker;
+
+ bool OnRequest(LocalUser* user, bool adding) CXX11_OVERRIDE
+ {
+ // Requesting this cap is allowed anytime
+ if (adding)
+ return true;
+
+ // But removing it can only be done when unregistered
+ return (user->registered != REG_ALL);
+ }
+
+ bool OnList(LocalUser* user) CXX11_OVERRIDE
+ {
+ return servertracker.IsOnline();
+ }
+
+ const std::string* GetValue(LocalUser* user) const CXX11_OVERRIDE
+ {
+ return &mechlist;
+ }
+
+ public:
+ SASLCap(Module* mod, const ServerTracker& tracker)
+ : Cap::Capability(mod, "sasl")
+ , servertracker(tracker)
+ {
+ }
+
+ void SetMechlist(const std::string& newmechlist)
+ {
+ if (mechlist == newmechlist)
+ return;
+
+ mechlist = newmechlist;
+ NotifyValueChange();
+ }
+};
enum SaslState { SASL_INIT, SASL_COMM, SASL_DONE };
enum SaslResult { SASL_OK, SASL_FAIL, SASL_ABORT };
-static std::string sasl_target = "*";
+static Events::ModuleEventProvider* saslevprov;
static void SendSASL(const parameterlist& params)
{
if (!ServerInstance->PI->SendEncapsulatedData(sasl_target, "SASL", params))
{
- SASLFallback(NULL, params);
+ FOREACH_MOD_CUSTOM(*saslevprov, SASLEventListener, OnSASLAuth, (params));
}
}
@@ -49,10 +153,63 @@ class SaslAuthenticator
SaslResult result;
bool state_announced;
+ /* taken from m_services_account */
+ static bool ReadCGIIRCExt(const char* extname, User* user, std::string& out)
+ {
+ ExtensionItem* wiext = ServerInstance->Extensions.GetItem(extname);
+ if (!wiext)
+ return false;
+
+ if (wiext->creator->ModuleSourceFile != "m_cgiirc.so")
+ return false;
+
+ StringExtItem* stringext = static_cast<StringExtItem*>(wiext);
+ std::string* addr = stringext->get(user);
+ if (!addr)
+ return false;
+
+ out = *addr;
+ return true;
+ }
+
+
+ void SendHostIP()
+ {
+ std::string host, ip;
+
+ if (!ReadCGIIRCExt("cgiirc_webirc_hostname", user, host))
+ {
+ host = user->host;
+ }
+ if (!ReadCGIIRCExt("cgiirc_webirc_ip", user, ip))
+ {
+ ip = user->GetIPString();
+ }
+ else
+ {
+ /* IP addresses starting with a : on irc are a Bad Thing (tm) */
+ if (ip.c_str()[0] == ':')
+ ip.insert(ip.begin(),1,'0');
+ }
+
+ parameterlist params;
+ params.push_back(sasl_target);
+ params.push_back("SASL");
+ params.push_back(user->uuid);
+ params.push_back("*");
+ params.push_back("H");
+ params.push_back(host);
+ params.push_back(ip);
+
+ SendSASL(params);
+ }
+
public:
SaslAuthenticator(User* user_, const std::string& method)
: user(user_), state(SASL_INIT), state_announced(false)
{
+ SendHostIP();
+
parameterlist params;
params.push_back(user->uuid);
params.push_back("*");
@@ -95,6 +252,9 @@ class SaslAuthenticator
if (msg[0] != this->agent)
return this->state;
+ if (msg.size() < 4)
+ return this->state;
+
if (msg[2] == "C")
this->user->Write("AUTHENTICATE %s", msg[3].c_str());
else if (msg[2] == "D")
@@ -103,7 +263,7 @@ class SaslAuthenticator
this->result = this->GetSaslResult(msg[3]);
}
else if (msg[2] == "M")
- this->user->WriteNumeric(908, "%s %s :are available SASL mechanisms", this->user->nick.c_str(), msg[3].c_str());
+ this->user->WriteNumeric(908, msg[3], "are available SASL mechanisms");
else
ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Services sent an unknown SASL message \"%s\" \"%s\"", msg[2].c_str(), msg[3].c_str());
@@ -138,7 +298,7 @@ class SaslAuthenticator
SendSASL(params);
- if (parameters[0][0] == '*')
+ if (parameters[0].c_str()[0] == '*')
{
this->Abort();
return false;
@@ -155,13 +315,13 @@ class SaslAuthenticator
switch (this->result)
{
case SASL_OK:
- this->user->WriteNumeric(903, ":SASL authentication successful");
+ this->user->WriteNumeric(903, "SASL authentication successful");
break;
case SASL_ABORT:
- this->user->WriteNumeric(906, ":SASL authentication aborted");
+ this->user->WriteNumeric(906, "SASL authentication aborted");
break;
case SASL_FAIL:
- this->user->WriteNumeric(904, ":SASL authentication failed");
+ this->user->WriteNumeric(904, "SASL authentication failed");
break;
default:
break;
@@ -175,19 +335,21 @@ class CommandAuthenticate : public Command
{
public:
SimpleExtItem<SaslAuthenticator>& authExt;
- GenericCap& cap;
- CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, GenericCap& Cap)
+ Cap::Capability& cap;
+ CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, Cap::Capability& Cap)
: Command(Creator, "AUTHENTICATE", 1), authExt(ext), cap(Cap)
{
works_before_reg = true;
+ allow_empty_last_param = false;
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
{
- /* Only allow AUTHENTICATE on unregistered clients */
- if (user->registered != REG_ALL)
{
- if (!cap.ext.get(user))
+ if (!cap.get(user))
+ return CMD_FAILURE;
+
+ if (parameters[0].find(' ') != std::string::npos || parameters[0][0] == ':')
return CMD_FAILURE;
SaslAuthenticator *sasl = authExt.get(user);
@@ -214,8 +376,8 @@ class CommandSASL : public Command
CmdResult Handle(const std::vector<std::string>& parameters, User *user)
{
- User* target = ServerInstance->FindNick(parameters[1]);
- if ((!target) || (IS_SERVER(target)))
+ User* target = ServerInstance->FindUUID(parameters[1]);
+ if (!target)
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User not found in sasl ENCAP event: %s", parameters[1].c_str());
return CMD_FAILURE;
@@ -243,14 +405,22 @@ class CommandSASL : public Command
class ModuleSASL : public Module
{
SimpleExtItem<SaslAuthenticator> authExt;
- GenericCap cap;
+ ServerTracker servertracker;
+ SASLCap cap;
CommandAuthenticate auth;
CommandSASL sasl;
+ Events::ModuleEventProvider sasleventprov;
public:
ModuleSASL()
- : authExt("sasl_auth", this), cap(this, "sasl"), auth(this, authExt, cap), sasl(this, authExt)
+ : authExt("sasl_auth", ExtensionItem::EXT_USER, this)
+ , servertracker(this)
+ , cap(this, servertracker)
+ , auth(this, authExt, cap)
+ , sasl(this, authExt)
+ , sasleventprov(this, "event/sasl")
{
+ saslevprov = &sasleventprov;
}
void init() CXX11_OVERRIDE
@@ -262,9 +432,10 @@ class ModuleSASL : public Module
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
sasl_target = ServerInstance->Config->ConfValue("sasl")->getString("target", "*");
+ servertracker.Reset();
}
- ModResult OnUserRegister(LocalUser *user) CXX11_OVERRIDE
+ void OnUserConnect(LocalUser *user) CXX11_OVERRIDE
{
SaslAuthenticator *sasl_ = authExt.get(user);
if (sasl_)
@@ -272,18 +443,17 @@ class ModuleSASL : public Module
sasl_->Abort();
authExt.unset(user);
}
-
- return MOD_RES_PASSTHRU;
}
- Version GetVersion() CXX11_OVERRIDE
+ void OnDecodeMetaData(Extensible* target, const std::string& extname, const std::string& extdata) CXX11_OVERRIDE
{
- return Version("Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE.",VF_VENDOR);
+ if ((target == NULL) && (extname == "saslmechlist"))
+ cap.SetMechlist(extdata);
}
- void OnEvent(Event &ev) CXX11_OVERRIDE
+ Version GetVersion() CXX11_OVERRIDE
{
- cap.HandleEvent(ev);
+ return Version("Provides support for IRC Authentication Layer (aka: SASL) via AUTHENTICATE.", VF_VENDOR);
}
};
diff --git a/src/modules/m_satopic.cpp b/src/modules/m_satopic.cpp
index 4a6f85536..f45d9c8cd 100644
--- a/src/modules/m_satopic.cpp
+++ b/src/modules/m_satopic.cpp
@@ -26,7 +26,7 @@ class CommandSATopic : public Command
public:
CommandSATopic(Module* Creator) : Command(Creator,"SATOPIC", 2, 2)
{
- flags_needed = 'o'; Penalty = 0; syntax = "<target> <topic>";
+ flags_needed = 'o'; syntax = "<target> <topic>";
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
@@ -38,15 +38,21 @@ class CommandSATopic : public Command
if(target)
{
- const std::string& newTopic = parameters[1];
- target->SetTopic(user, newTopic);
+ const std::string newTopic(parameters[1], 0, ServerInstance->Config->Limits.MaxTopic);
+ if (target->topic == newTopic)
+ {
+ user->WriteNotice(InspIRCd::Format("The topic on %s is already what you are trying to change it to.", target->name.c_str()));
+ return CMD_SUCCESS;
+ }
+
+ target->SetTopic(user, newTopic, ServerInstance->Time(), NULL);
ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SATOPIC on " + target->name + ", new topic: " + newTopic);
return CMD_SUCCESS;
}
else
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
}
diff --git a/src/modules/m_securelist.cpp b/src/modules/m_securelist.cpp
index f4042b8f6..b925c3f37 100644
--- a/src/modules/m_securelist.cpp
+++ b/src/modules/m_securelist.cpp
@@ -63,11 +63,11 @@ class ModuleSecureList : public Module
/* Not exempt, BOOK EM DANNO! */
user->WriteNotice("*** You cannot list within the first " + ConvToStr(WaitTime) + " seconds of connecting. Please try again later.");
- /* Some crap clients (read: mIRC, various java chat applets) muck up if they don't
+ /* Some clients (e.g. mIRC, various java chat applets) muck up if they don't
* receive these numerics whenever they send LIST, so give them an empty LIST to mull over.
*/
- user->WriteNumeric(RPL_LISTSTART, "Channel :Users Name");
- user->WriteNumeric(RPL_LISTEND, ":End of channel list.");
+ user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
+ user->WriteNumeric(RPL_LISTEND, "End of channel list.");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
diff --git a/src/modules/m_services_account.cpp b/src/modules/m_services_account.cpp
index edb6f6ef5..e97e1b02f 100644
--- a/src/modules/m_services_account.cpp
+++ b/src/modules/m_services_account.cpp
@@ -46,7 +46,7 @@ class Channel_r : public ModeHandler
}
else
{
- source->WriteNumeric(500, ":Only a server may modify the +r channel mode");
+ source->WriteNumeric(500, "Only a server may modify the +r channel mode");
}
return MODEACTION_DENY;
}
@@ -72,7 +72,7 @@ class User_r : public ModeHandler
}
else
{
- source->WriteNumeric(500, ":Only a server may modify the +r user mode");
+ source->WriteNumeric(500, "Only a server may modify the +r user mode");
}
return MODEACTION_DENY;
}
@@ -104,39 +104,40 @@ class AChannel_M : public SimpleChannelModeHandler
class AccountExtItemImpl : public AccountExtItem
{
+ Events::ModuleEventProvider eventprov;
+
public:
AccountExtItemImpl(Module* mod)
- : AccountExtItem("accountname", mod)
+ : AccountExtItem("accountname", ExtensionItem::EXT_USER, mod)
+ , eventprov(mod, "event/account")
{
}
void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
{
- User* user = dynamic_cast<User*>(container);
- if (!user)
- return;
+ User* user = static_cast<User*>(container);
StringExtItem::unserialize(format, container, value);
+
+ // If we are being reloaded then don't send the numeric or run the event
+ if (format == FORMAT_INTERNAL)
+ return;
+
if (!value.empty())
{
// Logged in
if (IS_LOCAL(user))
{
- user->WriteNumeric(900, "%s %s :You are now logged in as %s",
- user->GetFullHost().c_str(), value.c_str(), value.c_str());
+ user->WriteNumeric(900, user->GetFullHost(), value, InspIRCd::Format("You are now logged in as %s", value.c_str()));
}
-
- AccountEvent(creator, user, value).Send();
- }
- else
- {
- // Logged out
- AccountEvent(creator, user, "").Send();
}
+ // If value is empty then logged out
+
+ FOREACH_MOD_CUSTOM(eventprov, AccountEventListener, OnAccountChange, (user, value));
}
};
-class ModuleServicesAccount : public Module
+class ModuleServicesAccount : public Module, public Whois::EventListener
{
AChannel_R m1;
AChannel_M m2;
@@ -146,8 +147,11 @@ class ModuleServicesAccount : public Module
AccountExtItemImpl accountname;
bool checking_ban;
public:
- ModuleServicesAccount() : m1(this), m2(this), m3(this), m4(this), m5(this),
- accountname(this)
+ ModuleServicesAccount()
+ : Whois::EventListener(this)
+ , m1(this), m2(this), m3(this), m4(this), m5(this)
+ , accountname(this)
+ , checking_ban(false)
{
}
@@ -158,32 +162,27 @@ class ModuleServicesAccount : public Module
}
/* <- :twisted.oscnet.org 330 w00t2 w00t2 w00t :is logged in as */
- void OnWhois(User* source, User* dest) CXX11_OVERRIDE
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- std::string *account = accountname.get(dest);
+ std::string* account = accountname.get(whois.GetTarget());
if (account)
{
- ServerInstance->SendWhoisLine(source, dest, 330, "%s %s :is logged in as", dest->nick.c_str(), account->c_str());
+ whois.SendLine(330, *account, "is logged in as");
}
- if (dest->IsModeSet(m5))
+ if (whois.GetTarget()->IsModeSet(m5))
{
/* user is registered */
- ServerInstance->SendWhoisLine(source, dest, 307, "%s :is a registered nick", dest->nick.c_str());
+ whois.SendLine(307, "is a registered nick");
}
}
void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
{
/* On nickchange, if they have +r, remove it */
- if (user->IsModeSet(m5) && assign(user->nick) != oldnick)
- {
- std::vector<std::string> modechange;
- modechange.push_back(user->nick);
- modechange.push_back("-r");
- ServerInstance->Modes->Process(modechange, ServerInstance->FakeClient, ModeParser::MODE_LOCALONLY);
- }
+ if ((user->IsModeSet(m5)) && (ServerInstance->FindNickOnly(oldnick) != user))
+ m5.RemoveMode(user);
}
ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
@@ -202,7 +201,7 @@ class ModuleServicesAccount : public Module
if (c->IsModeSet(m2) && !is_registered && res != MOD_RES_ALLOW)
{
// user messaging a +M channel and is not registered
- user->WriteNumeric(477, c->name+" :You need to be identified to a registered account to message this channel");
+ user->WriteNumeric(477, c->name, "You need to be identified to a registered account to message this channel");
return MOD_RES_DENY;
}
}
@@ -213,7 +212,7 @@ class ModuleServicesAccount : public Module
if (u->IsModeSet(m3) && !is_registered)
{
// user messaging a +R user and is not registered
- user->WriteNumeric(477, u->nick +" :You need to be identified to a registered account to message this user");
+ user->WriteNumeric(477, u->nick, "You need to be identified to a registered account to message this user");
return MOD_RES_DENY;
}
}
@@ -268,7 +267,7 @@ class ModuleServicesAccount : public Module
if (!is_registered)
{
// joining a +R channel and not identified
- user->WriteNumeric(477, chan->name + " :You need to be identified to a registered account to join this channel");
+ user->WriteNumeric(477, chan->name, "You need to be identified to a registered account to join this channel");
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_servprotect.cpp b/src/modules/m_servprotect.cpp
index 26453020f..97670237b 100644
--- a/src/modules/m_servprotect.cpp
+++ b/src/modules/m_servprotect.cpp
@@ -42,12 +42,14 @@ class ServProtectMode : public ModeHandler
}
};
-class ModuleServProtectMode : public Module
+class ModuleServProtectMode : public Module, public Whois::EventListener, public Whois::LineEventListener
{
ServProtectMode bm;
public:
ModuleServProtectMode()
- : bm(this)
+ : Whois::EventListener(this)
+ , Whois::LineEventListener(this)
+ , bm(this)
{
}
@@ -56,11 +58,11 @@ class ModuleServProtectMode : public Module
return Version("Provides usermode +k to protect services from kicks, kills, and mode changes.", VF_VENDOR);
}
- void OnWhois(User* user, User* dest) CXX11_OVERRIDE
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- if (dest->IsModeSet(bm))
+ if (whois.GetTarget()->IsModeSet(bm))
{
- ServerInstance->SendWhoisLine(user, dest, 310, dest->nick+" :is a Network Service on "+ServerInstance->Config->Network);
+ whois.SendLine(310, "is a Network Service on " + ServerInstance->Config->Network);
}
}
@@ -71,6 +73,10 @@ class ModuleServProtectMode : public Module
*/
if (!adding && chan && IS_LOCAL(user) && !param.empty())
{
+ const PrefixMode* const pm = mh->IsPrefixMode();
+ if (!pm)
+ return MOD_RES_PASSTHRU;
+
/* Check if the parameter is a valid nick/uuid
*/
User *u = ServerInstance->FindNick(param);
@@ -81,10 +87,10 @@ class ModuleServProtectMode : public Module
* This includes any prefix permission mode, even those registered in other modules, e.g. +qaohv. Using ::ModeString()
* here means that the number of modes is restricted to only modes the user has, limiting it to as short a loop as possible.
*/
- if (u->IsModeSet(bm) && memb && memb->hasMode(mh->GetModeChar()))
+ if ((u->IsModeSet(bm)) && (memb) && (memb->HasMode(pm)))
{
/* BZZZT, Denied! */
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You are not permitted to remove privileges from %s services", chan->name.c_str(), ServerInstance->Config->Network.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, InspIRCd::Format("You are not permitted to remove privileges from %s services", ServerInstance->Config->Network.c_str()));
return MOD_RES_DENY;
}
}
@@ -100,7 +106,7 @@ class ModuleServProtectMode : public Module
if (dst->IsModeSet(bm))
{
- src->WriteNumeric(485, ":You are not permitted to kill %s services!", ServerInstance->Config->Network.c_str());
+ src->WriteNumeric(485, InspIRCd::Format("You are not permitted to kill %s services!", ServerInstance->Config->Network.c_str()));
ServerInstance->SNO->WriteGlobalSno('a', src->nick+" tried to kill service "+dst->nick+" ("+reason+")");
return MOD_RES_DENY;
}
@@ -111,17 +117,16 @@ class ModuleServProtectMode : public Module
{
if (memb->user->IsModeSet(bm))
{
- src->WriteNumeric(ERR_RESTRICTED, "%s :You are not permitted to kick services",
- memb->chan->name.c_str());
+ src->WriteNumeric(ERR_RESTRICTED, memb->chan->name, "You are not permitted to kick services");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ModResult OnWhoisLine(User* src, User* dst, int &numeric, std::string &text) CXX11_OVERRIDE
+ ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
- return ((src != dst) && (numeric == 319) && dst->IsModeSet(bm)) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+ return ((numeric.GetNumeric() == 319) && whois.GetTarget()->IsModeSet(bm)) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
}
};
diff --git a/src/modules/m_setidle.cpp b/src/modules/m_setidle.cpp
index dd82aef29..4a15fd0d5 100644
--- a/src/modules/m_setidle.cpp
+++ b/src/modules/m_setidle.cpp
@@ -36,7 +36,7 @@ class CommandSetidle : public SplitCommand
int idle = InspIRCd::Duration(parameters[0]);
if (idle < 1)
{
- user->WriteNumeric(948, ":Invalid idle time.");
+ user->WriteNumeric(948, "Invalid idle time.");
return CMD_FAILURE;
}
user->idle_lastmsg = (ServerInstance->Time() - idle);
@@ -44,7 +44,7 @@ class CommandSetidle : public SplitCommand
if (user->signon > user->idle_lastmsg)
user->signon = user->idle_lastmsg;
ServerInstance->SNO->WriteToSnoMask('a', user->nick+" used SETIDLE to set their idle time to "+ConvToStr(idle)+" seconds");
- user->WriteNumeric(944, ":Idle time set.");
+ user->WriteNumeric(944, "Idle time set.");
return CMD_SUCCESS;
}
diff --git a/src/modules/m_sha1.cpp b/src/modules/m_sha1.cpp
new file mode 100644
index 000000000..5926e4926
--- /dev/null
+++ b/src/modules/m_sha1.cpp
@@ -0,0 +1,199 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+*/
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+union CHAR64LONG16
+{
+ unsigned char c[64];
+ uint32_t l[16];
+};
+
+inline static uint32_t rol(uint32_t value, uint32_t bits) { return (value << bits) | (value >> (32 - bits)); }
+
+// blk0() and blk() perform the initial expand.
+// I got the idea of expanding during the round function from SSLeay
+static bool big_endian;
+inline static uint32_t blk0(CHAR64LONG16& block, uint32_t i)
+{
+ if (big_endian)
+ return block.l[i];
+ else
+ return block.l[i] = (rol(block.l[i], 24) & 0xFF00FF00) | (rol(block.l[i], 8) & 0x00FF00FF);
+}
+inline static uint32_t blk(CHAR64LONG16 &block, uint32_t i) { return block.l[i & 15] = rol(block.l[(i + 13) & 15] ^ block.l[(i + 8) & 15] ^ block.l[(i + 2) & 15] ^ block.l[i & 15],1); }
+
+// (R0+R1), R2, R3, R4 are the different operations used in SHA1
+inline static void R0(CHAR64LONG16& block, uint32_t v, uint32_t &w, uint32_t x, uint32_t y, uint32_t &z, uint32_t i) { z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); w = rol(w, 30); }
+inline static void R1(CHAR64LONG16& block, uint32_t v, uint32_t &w, uint32_t x, uint32_t y, uint32_t &z, uint32_t i) { z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + rol(v, 5); w = rol(w, 30); }
+inline static void R2(CHAR64LONG16& block, uint32_t v, uint32_t &w, uint32_t x, uint32_t y, uint32_t &z, uint32_t i) { z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); }
+inline static void R3(CHAR64LONG16& block, uint32_t v, uint32_t &w, uint32_t x, uint32_t y, uint32_t &z, uint32_t i) { z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + rol(v, 5); w = rol(w, 30); }
+inline static void R4(CHAR64LONG16& block, uint32_t v, uint32_t &w, uint32_t x, uint32_t y, uint32_t &z, uint32_t i) { z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + rol(v, 5); w = rol(w, 30); }
+
+static const uint32_t sha1_iv[5] =
+{
+ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0
+};
+
+class SHA1Context
+{
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+ unsigned char digest[20];
+
+ void Transform(const unsigned char buf[64])
+ {
+ uint32_t a, b, c, d, e;
+
+ CHAR64LONG16 block;
+ memcpy(block.c, buf, 64);
+
+ // Copy state[] to working vars
+ a = this->state[0];
+ b = this->state[1];
+ c = this->state[2];
+ d = this->state[3];
+ e = this->state[4];
+
+ // 4 rounds of 20 operations each. Loop unrolled.
+ R0(block, a, b, c, d, e, 0); R0(block, e, a, b, c, d, 1); R0(block, d, e, a, b, c, 2); R0(block, c, d, e, a, b, 3);
+ R0(block, b, c, d, e, a, 4); R0(block, a, b, c, d, e, 5); R0(block, e, a, b, c, d, 6); R0(block, d, e, a, b, c, 7);
+ R0(block, c, d, e, a, b, 8); R0(block, b, c, d, e, a, 9); R0(block, a, b, c, d, e, 10); R0(block, e, a, b, c, d, 11);
+ R0(block, d, e, a, b, c, 12); R0(block, c, d, e, a, b, 13); R0(block, b, c, d, e, a, 14); R0(block, a, b, c, d, e, 15);
+ R1(block, e, a, b, c, d, 16); R1(block, d, e, a, b, c, 17); R1(block, c, d, e, a, b, 18); R1(block, b, c, d, e, a, 19);
+ R2(block, a, b, c, d, e, 20); R2(block, e, a, b, c, d, 21); R2(block, d, e, a, b, c, 22); R2(block, c, d, e, a, b, 23);
+ R2(block, b, c, d, e, a, 24); R2(block, a, b, c, d, e, 25); R2(block, e, a, b, c, d, 26); R2(block, d, e, a, b, c, 27);
+ R2(block, c, d, e, a, b, 28); R2(block, b, c, d, e, a, 29); R2(block, a, b, c, d, e, 30); R2(block, e, a, b, c, d, 31);
+ R2(block, d, e, a, b, c, 32); R2(block, c, d, e, a, b, 33); R2(block, b, c, d, e, a, 34); R2(block, a, b, c, d, e, 35);
+ R2(block, e, a, b, c, d, 36); R2(block, d, e, a, b, c, 37); R2(block, c, d, e, a, b, 38); R2(block, b, c, d, e, a, 39);
+ R3(block, a, b, c, d, e, 40); R3(block, e, a, b, c, d, 41); R3(block, d, e, a, b, c, 42); R3(block, c, d, e, a, b, 43);
+ R3(block, b, c, d, e, a, 44); R3(block, a, b, c, d, e, 45); R3(block, e, a, b, c, d, 46); R3(block, d, e, a, b, c, 47);
+ R3(block, c, d, e, a, b, 48); R3(block, b, c, d, e, a, 49); R3(block, a, b, c, d, e, 50); R3(block, e, a, b, c, d, 51);
+ R3(block, d, e, a, b, c, 52); R3(block, c, d, e, a, b, 53); R3(block, b, c, d, e, a, 54); R3(block, a, b, c, d, e, 55);
+ R3(block, e, a, b, c, d, 56); R3(block, d, e, a, b, c, 57); R3(block, c, d, e, a, b, 58); R3(block, b, c, d, e, a, 59);
+ R4(block, a, b, c, d, e, 60); R4(block, e, a, b, c, d, 61); R4(block, d, e, a, b, c, 62); R4(block, c, d, e, a, b, 63);
+ R4(block, b, c, d, e, a, 64); R4(block, a, b, c, d, e, 65); R4(block, e, a, b, c, d, 66); R4(block, d, e, a, b, c, 67);
+ R4(block, c, d, e, a, b, 68); R4(block, b, c, d, e, a, 69); R4(block, a, b, c, d, e, 70); R4(block, e, a, b, c, d, 71);
+ R4(block, d, e, a, b, c, 72); R4(block, c, d, e, a, b, 73); R4(block, b, c, d, e, a, 74); R4(block, a, b, c, d, e, 75);
+ R4(block, e, a, b, c, d, 76); R4(block, d, e, a, b, c, 77); R4(block, c, d, e, a, b, 78); R4(block, b, c, d, e, a, 79);
+ // Add the working vars back into state[]
+ this->state[0] += a;
+ this->state[1] += b;
+ this->state[2] += c;
+ this->state[3] += d;
+ this->state[4] += e;
+ }
+
+ public:
+ SHA1Context()
+ {
+ 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)
+ {
+ uint32_t i, j;
+
+ j = (this->count[0] >> 3) & 63;
+ if ((this->count[0] += len << 3) < (len << 3))
+ ++this->count[1];
+ this->count[1] += len >> 29;
+ if (j + len > 63)
+ {
+ memcpy(&this->buffer[j], data, (i = 64 - j));
+ this->Transform(this->buffer);
+ for (; i + 63 < len; i += 64)
+ this->Transform(&data[i]);
+ j = 0;
+ }
+ else
+ i = 0;
+ memcpy(&this->buffer[j], &data[i], len - i);
+ }
+
+ void Finalize()
+ {
+ uint32_t i;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; ++i)
+ finalcount[i] = static_cast<unsigned char>((this->count[i >= 4 ? 0 : 1] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */
+ this->Update(reinterpret_cast<const unsigned char *>("\200"), 1);
+ while ((this->count[0] & 504) != 448)
+ this->Update(reinterpret_cast<const unsigned char *>("\0"), 1);
+ this->Update(finalcount, 8); // Should cause a SHA1Transform()
+ for (i = 0; i < 20; ++i)
+ this->digest[i] = static_cast<unsigned char>((this->state[i>>2] >> ((3 - (i & 3)) * 8)) & 255);
+
+ this->Transform(this->buffer);
+ }
+
+ std::string GetRaw() const
+ {
+ return std::string((const char*)digest, sizeof(digest));
+ }
+};
+
+class SHA1HashProvider : public HashProvider
+{
+ public:
+ SHA1HashProvider(Module* mod)
+ : HashProvider(mod, "hash/sha1", 20, 64)
+ {
+ }
+
+ std::string GenerateRaw(const std::string& data)
+ {
+ SHA1Context ctx;
+ ctx.Update(reinterpret_cast<const unsigned char*>(data.data()), data.length());
+ ctx.Finalize();
+ return ctx.GetRaw();
+ }
+};
+
+class ModuleSHA1 : public Module
+{
+ SHA1HashProvider sha1;
+
+ public:
+ ModuleSHA1()
+ : sha1(this)
+ {
+ big_endian = (htonl(1337) == 1337);
+ }
+
+ Version GetVersion()
+ {
+ return Version("Implements SHA-1 hashing", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleSHA1)
diff --git a/src/modules/m_sha256.cpp b/src/modules/m_sha256.cpp
index d2755bacc..48bfc0041 100644
--- a/src/modules/m_sha256.cpp
+++ b/src/modules/m_sha256.cpp
@@ -247,14 +247,14 @@ class HashSHA256 : public HashProvider
}
public:
- std::string sum(const std::string& data)
+ std::string GenerateRaw(const std::string& data)
{
unsigned char bytes[SHA256_DIGEST_SIZE];
SHA256(data.data(), bytes, data.length());
return std::string((char*)bytes, SHA256_DIGEST_SIZE);
}
- HashSHA256(Module* parent) : HashProvider(parent, "hash/sha256", 32, 64) {}
+ HashSHA256(Module* parent) : HashProvider(parent, "sha256", 32, 64) {}
};
class ModuleSHA256 : public Module
diff --git a/src/modules/m_showfile.cpp b/src/modules/m_showfile.cpp
index c42877eef..57c501e90 100644
--- a/src/modules/m_showfile.cpp
+++ b/src/modules/m_showfile.cpp
@@ -44,23 +44,24 @@ class CommandShowFile : public Command
CmdResult Handle(const std::vector<std::string>& parameters, User* user) CXX11_OVERRIDE
{
- const std::string& sn = ServerInstance->Config->ServerName;
if (method == SF_NUMERIC)
{
if (!introtext.empty())
- user->SendText(":%s %03d %s :%s %s", sn.c_str(), intronumeric, user->nick.c_str(), sn.c_str(), introtext.c_str());
+ user->WriteRemoteNumeric(intronumeric, introtext);
for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
- user->SendText(":%s %03d %s :- %s", sn.c_str(), textnumeric, user->nick.c_str(), i->c_str());
+ user->WriteRemoteNumeric(textnumeric, InspIRCd::Format("- %s", i->c_str()));
- user->SendText(":%s %03d %s :%s", sn.c_str(), endnumeric, user->nick.c_str(), endtext.c_str());
+ user->WriteRemoteNumeric(endnumeric, endtext.c_str());
}
else
{
const char* msgcmd = (method == SF_MSG ? "PRIVMSG" : "NOTICE");
- std::string header = InspIRCd::Format(":%s %s %s :", sn.c_str(), msgcmd, user->nick.c_str());
for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
- user->SendText(header + *i);
+ {
+ const std::string& line = *i;
+ user->WriteCommand(msgcmd, ":" + line);
+ }
}
return CMD_SUCCESS;
}
@@ -104,7 +105,7 @@ class ModuleShowFile : public Module
FileReader reader(file);
CommandShowFile* sfcmd;
- Command* handler = ServerInstance->Parser->GetHandler(cmdname);
+ Command* handler = ServerInstance->Parser.GetHandler(cmdname);
if (handler)
{
// Command exists, check if it is ours
@@ -113,7 +114,7 @@ class ModuleShowFile : public Module
// This is our command, make sure we don't have the same entry twice
sfcmd = static_cast<CommandShowFile*>(handler);
- if (std::find(newcmds.begin(), newcmds.end(), sfcmd) != newcmds.end())
+ if (stdalgo::isin(newcmds, sfcmd))
throw ModuleException("Command " + cmdname + " is already used in a <showfile> tag");
}
else
diff --git a/src/modules/m_showwhois.cpp b/src/modules/m_showwhois.cpp
index ba17942cb..3cb85f3fb 100644
--- a/src/modules/m_showwhois.cpp
+++ b/src/modules/m_showwhois.cpp
@@ -69,7 +69,7 @@ class WhoisNoticeCmd : public Command
}
};
-class ModuleShowwhois : public Module
+class ModuleShowwhois : public Module, public Whois::EventListener
{
bool ShowWhoisFromOpers;
SeeWhois sw;
@@ -78,7 +78,9 @@ class ModuleShowwhois : public Module
public:
ModuleShowwhois()
- : sw(this), cmd(this)
+ : Whois::EventListener(this)
+ , sw(this)
+ , cmd(this)
{
}
@@ -95,9 +97,11 @@ class ModuleShowwhois : public Module
return Version("Allows opers to set +W to see when a user uses WHOIS on them",VF_OPTCOMMON|VF_VENDOR);
}
- void OnWhois(User* source, User* dest) CXX11_OVERRIDE
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- if (!dest->IsModeSet(sw) || source == dest)
+ User* const source = whois.GetSource();
+ User* const dest = whois.GetTarget();
+ if (!dest->IsModeSet(sw) || whois.IsSelfWhois())
return;
if (!ShowWhoisFromOpers && source->IsOper())
diff --git a/src/modules/m_shun.cpp b/src/modules/m_shun.cpp
index 075b80eb7..022726524 100644
--- a/src/modules/m_shun.cpp
+++ b/src/modules/m_shun.cpp
@@ -44,6 +44,9 @@ public:
if (InspIRCd::Match(u->GetFullHost(), matchtext) || InspIRCd::Match(u->GetFullRealHost(), matchtext) || InspIRCd::Match(u->nick+"!"+u->ident+"@"+u->GetIPString(), matchtext))
return true;
+ if (InspIRCd::MatchCIDR(u->GetIPString(), matchtext, ascii_case_insensitive_map))
+ return true;
+
return false;
}
@@ -168,7 +171,7 @@ class ModuleShun : public Module
{
CommandShun cmd;
ShunFactory f;
- std::set<std::string> ShunEnabledCommands;
+ insp::flat_set<std::string> ShunEnabledCommands;
bool NotifyOfShun;
bool affectopers;
@@ -191,15 +194,15 @@ class ModuleShun : public Module
void Prioritize()
{
Module* alias = ServerInstance->Modules->Find("m_alias.so");
- ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_BEFORE, &alias);
+ ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_BEFORE, alias);
}
- ModResult OnStats(char symbol, User* user, string_list& out) CXX11_OVERRIDE
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if (symbol != 'H')
+ if (stats.GetSymbol() != 'H')
return MOD_RES_PASSTHRU;
- ServerInstance->XLines->InvokeStats("SHUN", 223, user, out);
+ ServerInstance->XLines->InvokeStats("SHUN", 223, stats);
return MOD_RES_DENY;
}
@@ -243,9 +246,7 @@ class ModuleShun : public Module
return MOD_RES_PASSTHRU;
}
- std::set<std::string>::iterator i = ShunEnabledCommands.find(command);
-
- if (i == ShunEnabledCommands.end())
+ if (!ShunEnabledCommands.count(command))
{
if (NotifyOfShun)
user->WriteNotice("*** Command " + command + " not processed, as you have been blocked from issuing commands (SHUN)");
diff --git a/src/modules/m_silence.cpp b/src/modules/m_silence.cpp
index 3a213c6e7..cb065d2fc 100644
--- a/src/modules/m_silence.cpp
+++ b/src/modules/m_silence.cpp
@@ -45,8 +45,8 @@
// pair of hostmask and flags
typedef std::pair<std::string, int> silenceset;
-// deque list of pairs
-typedef std::deque<silenceset> silencelist;
+// list of pairs
+typedef std::vector<silenceset> silencelist;
// intmasks for flags
static int SILENCE_PRIVATE = 0x0001; /* p private messages */
@@ -85,7 +85,7 @@ class CommandSVSSilence : public Command
if (IS_LOCAL(u))
{
- ServerInstance->Parser->CallHandler("SILENCE", std::vector<std::string>(parameters.begin() + 1, parameters.end()), u);
+ ServerInstance->Parser.CallHandler("SILENCE", std::vector<std::string>(parameters.begin() + 1, parameters.end()), u);
}
return CMD_SUCCESS;
@@ -93,10 +93,7 @@ class CommandSVSSilence : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* target = ServerInstance->FindNick(parameters[0]);
- if (target)
- return ROUTE_OPT_UCAST(target->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
@@ -106,7 +103,8 @@ class CommandSilence : public Command
public:
SimpleExtItem<silencelist> ext;
CommandSilence(Module* Creator, unsigned int &max) : Command(Creator, "SILENCE", 0),
- maxsilence(max), ext("silence_list", Creator)
+ maxsilence(max)
+ , ext("silence_list", ExtensionItem::EXT_USER, Creator)
{
allow_empty_last_param = false;
syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}";
@@ -124,17 +122,17 @@ class CommandSilence : public Command
for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
{
std::string decomppattern = DecompPattern(c->second);
- user->WriteNumeric(271, "%s %s %s", user->nick.c_str(),c->first.c_str(), decomppattern.c_str());
+ user->WriteNumeric(271, user->nick, c->first, decomppattern);
}
}
- user->WriteNumeric(272, ":End of Silence List");
+ user->WriteNumeric(272, "End of Silence List");
return CMD_SUCCESS;
}
else if (parameters.size() > 0)
{
// one or more parameters, add or delete entry from the list (only the first parameter is used)
- std::string mask = parameters[0].substr(1);
+ std::string mask(parameters[0], 1);
char action = parameters[0][0];
// Default is private and notice so clients do not break
int pattern = CompilePattern("pn");
@@ -169,11 +167,11 @@ class CommandSilence : public Command
for (silencelist::iterator i = sl->begin(); i != sl->end(); i++)
{
// search through for the item
- irc::string listitem = i->first.c_str();
- if (listitem == mask && i->second == pattern)
+ const std::string& listitem = i->first;
+ if ((irc::equals(listitem, mask)) && (i->second == pattern))
{
sl->erase(i);
- user->WriteNumeric(950, "%s :Removed %s %s from silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(950, user->nick, InspIRCd::Format("Removed %s %s from silence list", mask.c_str(), decomppattern.c_str()));
if (!sl->size())
{
ext.unset(user);
@@ -182,7 +180,7 @@ class CommandSilence : public Command
}
}
}
- user->WriteNumeric(952, "%s :%s %s does not exist on your silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(952, user->nick, InspIRCd::Format("%s %s does not exist on your silence list", mask.c_str(), decomppattern.c_str()));
}
else if (action == '+')
{
@@ -195,29 +193,29 @@ class CommandSilence : public Command
}
if (sl->size() > maxsilence)
{
- user->WriteNumeric(952, "%s :Your silence list is full",user->nick.c_str());
+ user->WriteNumeric(952, user->nick, "Your silence list is full");
return CMD_FAILURE;
}
std::string decomppattern = DecompPattern(pattern);
for (silencelist::iterator n = sl->begin(); n != sl->end(); n++)
{
- irc::string listitem = n->first.c_str();
- if (listitem == mask && n->second == pattern)
+ const std::string& listitem = n->first;
+ if ((irc::equals(listitem, mask)) && (n->second == pattern))
{
- user->WriteNumeric(952, "%s :%s %s is already on your silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(952, user->nick, InspIRCd::Format("%s %s is already on your silence list", mask.c_str(), decomppattern.c_str()));
return CMD_FAILURE;
}
}
if (((pattern & SILENCE_EXCLUDE) > 0))
{
- sl->push_front(silenceset(mask,pattern));
+ sl->insert(sl->begin(), silenceset(mask, pattern));
}
else
{
sl->push_back(silenceset(mask,pattern));
}
- user->WriteNumeric(951, "%s :Added %s %s to silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(951, user->nick, InspIRCd::Format("Added %s %s to silence list", mask.c_str(), decomppattern.c_str()));
return CMD_SUCCESS;
}
}
@@ -290,6 +288,7 @@ class CommandSilence : public Command
class ModuleSilence : public Module
{
unsigned int maxsilence;
+ bool ExemptULine;
CommandSilence cmdsilence;
CommandSVSSilence cmdsvssilence;
public:
@@ -301,9 +300,13 @@ class ModuleSilence : public Module
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- maxsilence = ServerInstance->Config->ConfValue("silence")->getInt("maxentries", 32);
+ ConfigTag* tag = ServerInstance->Config->ConfValue("silence");
+
+ maxsilence = tag->getInt("maxentries", 32);
if (!maxsilence)
maxsilence = 32;
+
+ ExemptULine = tag->getBool("exemptuline", true);
}
void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
@@ -312,12 +315,12 @@ class ModuleSilence : public Module
tokens["SILENCE"] = ConvToStr(maxsilence);
}
- void OnBuildExemptList(MessageType message_type, Channel* chan, User* sender, char status, CUList &exempt_list, const std::string &text)
+ void BuildExemptList(MessageType message_type, Channel* chan, User* sender, CUList& exempt_list)
{
int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE);
- const UserMembList *ulist = chan->GetUsers();
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
if (IS_LOCAL(i->first))
{
@@ -338,7 +341,7 @@ class ModuleSilence : public Module
else if (target_type == TYPE_CHANNEL)
{
Channel* chan = (Channel*)dest;
- this->OnBuildExemptList(msgtype, chan, user, status, exempt_list, "");
+ BuildExemptList(msgtype, chan, user, exempt_list);
}
return MOD_RES_PASSTHRU;
}
@@ -350,6 +353,9 @@ class ModuleSilence : public Module
ModResult MatchPattern(User* dest, User* source, int pattern)
{
+ if (ExemptULine && source->server->IsULine())
+ return MOD_RES_PASSTHRU;
+
silencelist* sl = cmdsilence.ext.get(dest);
if (sl)
{
diff --git a/src/modules/m_spanningtree/addline.cpp b/src/modules/m_spanningtree/addline.cpp
index dbf0003bf..1bf847604 100644
--- a/src/modules/m_spanningtree/addline.cpp
+++ b/src/modules/m_spanningtree/addline.cpp
@@ -62,7 +62,7 @@ CmdResult CommandAddLine::Handle(User* usr, std::vector<std::string>& params)
TreeServer* remoteserver = TreeServer::Get(usr);
- if (!remoteserver->bursting)
+ if (!remoteserver->IsBursting())
{
ServerInstance->XLines->ApplyLines();
}
diff --git a/src/modules/m_spanningtree/away.cpp b/src/modules/m_spanningtree/away.cpp
index 9c4ec5783..7c514c49e 100644
--- a/src/modules/m_spanningtree/away.cpp
+++ b/src/modules/m_spanningtree/away.cpp
@@ -23,18 +23,18 @@
#include "utils.h"
#include "commands.h"
-CmdResult CommandAway::HandleRemote(RemoteUser* u, std::vector<std::string>& params)
+CmdResult CommandAway::HandleRemote(::RemoteUser* u, std::vector<std::string>& params)
{
if (params.size())
{
- FOREACH_MOD(OnSetAway, (u, params[params.size() - 1]));
+ FOREACH_MOD(OnSetAway, (u, params.back()));
if (params.size() > 1)
u->awaytime = ConvToInt(params[0]);
else
u->awaytime = ServerInstance->Time();
- u->awaymsg = params[params.size() - 1];
+ u->awaymsg = params.back();
}
else
{
diff --git a/src/modules/m_spanningtree/cachetimer.h b/src/modules/m_spanningtree/cachetimer.h
index 89933cc4b..cffbe3578 100644
--- a/src/modules/m_spanningtree/cachetimer.h
+++ b/src/modules/m_spanningtree/cachetimer.h
@@ -19,9 +19,7 @@
#pragma once
-/** Create a timer which recurs every second, we inherit from Timer.
- * Timer is only one-shot however, so at the end of each Tick() we simply
- * insert another of ourselves into the pending queue :)
+/** Timer that fires when we need to refresh the IP cache of servers
*/
class CacheRefreshTimer : public Timer
{
diff --git a/src/modules/m_spanningtree/capab.cpp b/src/modules/m_spanningtree/capab.cpp
index 5d87b1578..d22481518 100644
--- a/src/modules/m_spanningtree/capab.cpp
+++ b/src/modules/m_spanningtree/capab.cpp
@@ -26,6 +26,17 @@
#include "link.h"
#include "main.h"
+struct CompatMod
+{
+ const char* name;
+ ModuleFlags listflag;
+};
+
+static CompatMod compatmods[] =
+{
+ { "m_watch.so", VF_OPTCOMMON }
+};
+
std::string TreeSocket::MyModules(int filter)
{
const ModuleManager::ModuleMap& modlist = ServerInstance->Modules->GetModules();
@@ -33,8 +44,27 @@ std::string TreeSocket::MyModules(int filter)
std::string capabilities;
for (ModuleManager::ModuleMap::const_iterator i = modlist.begin(); i != modlist.end(); ++i)
{
+ Module* const mod = i->second;
+ // 3.0 advertises its settings for the benefit of services
+ // 2.0 would bork on this
+ if (proto_version < 1205 && i->second->ModuleSourceFile == "m_kicknorejoin.so")
+ continue;
+
+ bool do_compat_include = false;
+ if (proto_version < 1205)
+ {
+ for (size_t j = 0; j < sizeof(compatmods)/sizeof(compatmods[0]); j++)
+ {
+ if ((compatmods[j].listflag & filter) && (mod->ModuleSourceFile == compatmods[j].name))
+ {
+ do_compat_include = true;
+ break;
+ }
+ }
+ }
+
Version v = i->second->GetVersion();
- if (!(v.Flags & filter))
+ if ((!do_compat_include) && (!(v.Flags & filter)))
continue;
if (i != modlist.begin())
@@ -52,24 +82,22 @@ std::string TreeSocket::MyModules(int filter)
static std::string BuildModeList(ModeType type)
{
std::vector<std::string> modes;
- for(char c='A'; c <= 'z'; c++)
+ const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes.GetModes(type);
+ for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(c, type);
- if (mh)
+ const ModeHandler* const mh = i->second;
+ std::string mdesc = mh->name;
+ mdesc.push_back('=');
+ const PrefixMode* const pm = mh->IsPrefixMode();
+ if (pm)
{
- std::string mdesc = mh->name;
- mdesc.push_back('=');
- PrefixMode* pm = mh->IsPrefixMode();
- if (pm)
- {
- if (pm->GetPrefix())
- mdesc.push_back(pm->GetPrefix());
- }
- mdesc.push_back(mh->GetModeChar());
- modes.push_back(mdesc);
+ if (pm->GetPrefix())
+ mdesc.push_back(pm->GetPrefix());
}
+ mdesc.push_back(mh->GetModeChar());
+ modes.push_back(mdesc);
}
- sort(modes.begin(), modes.end());
+ std::sort(modes.begin(), modes.end());
return irc::stringjoiner(modes);
}
@@ -153,7 +181,13 @@ void TreeSocket::SendCapabilities(int phase)
extra+
" PREFIX="+ServerInstance->Modes->BuildPrefixes()+
" CHANMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL)+
- " USERMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_USER)
+ " USERMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_USER)+
+ // XXX: Advertise the presence or absence of m_globops in CAPAB CAPABILITIES.
+ // Services want to know about it, and since m_globops was not marked as VF_(OPT)COMMON
+ // in 2.0, we advertise it here to not break linking to previous versions.
+ // Protocol version 1201 (1.2) does not have this issue because we advertise m_globops
+ // to 1201 protocol servers irrespectively of its module flags.
+ (ServerInstance->Modules->Find("m_globops.so") != NULL ? " GLOBOPS=1" : " GLOBOPS=0")
);
this->WriteLine("CAPAB END");
@@ -380,8 +414,8 @@ bool TreeSocket::Capab(const parameterlist &params)
std::string::size_type equals = item.find('=');
if (equals != std::string::npos)
{
- std::string var = item.substr(0, equals);
- std::string value = item.substr(equals+1, item.length());
+ std::string var(item, 0, equals);
+ std::string value(item, equals+1);
capab->CapKeys[var] = value;
}
}
diff --git a/src/modules/m_spanningtree/commandbuilder.h b/src/modules/m_spanningtree/commandbuilder.h
index cd627227a..59de84052 100644
--- a/src/modules/m_spanningtree/commandbuilder.h
+++ b/src/modules/m_spanningtree/commandbuilder.h
@@ -68,6 +68,20 @@ class CmdBuilder
return *this;
}
+ template <typename T>
+ CmdBuilder& push_raw_int(T i)
+ {
+ content.append(ConvToStr(i));
+ return *this;
+ }
+
+ template <typename InputIterator>
+ CmdBuilder& push_raw(InputIterator first, InputIterator last)
+ {
+ content.append(first, last);
+ return *this;
+ }
+
CmdBuilder& push(const std::string& s)
{
content.push_back(' ');
@@ -128,11 +142,6 @@ class CmdBuilder
Utils->DoOneToAllButSender(*this, omit);
}
- bool Unicast(const std::string& target) const
- {
- return Utils->DoOneToOne(*this, target);
- }
-
void Unicast(User* target) const
{
Utils->DoOneToOne(*this, target->server);
diff --git a/src/modules/m_spanningtree/commands.h b/src/modules/m_spanningtree/commands.h
index d2d138ab2..8eea02915 100644
--- a/src/modules/m_spanningtree/commands.h
+++ b/src/modules/m_spanningtree/commands.h
@@ -21,6 +21,22 @@
#include "servercommand.h"
#include "commandbuilder.h"
+#include "remoteuser.h"
+
+namespace SpanningTree
+{
+ class CommandAway;
+ class CommandNick;
+ class CommandPing;
+ class CommandPong;
+ class CommandServer;
+}
+
+using SpanningTree::CommandAway;
+using SpanningTree::CommandNick;
+using SpanningTree::CommandPing;
+using SpanningTree::CommandPong;
+using SpanningTree::CommandServer;
/** Handle /RCONNECT
*/
@@ -114,13 +130,13 @@ class CommandOpertype : public UserOnlyServerCommand<CommandOpertype>
};
class TreeSocket;
+class FwdFJoinBuilder;
class CommandFJoin : public ServerCommand
{
/** Remove all modes from a channel, including statusmodes (+qaovh etc), simplemodes, parameter modes.
* This does not update the timestamp of the target channel, this must be done seperately.
*/
static void RemoveStatus(Channel* c);
- static void ApplyModeStack(User* srcuser, Channel* c, irc::modestacker& stack);
/**
* Lowers the TS on the given channel: removes all modes, unsets all extensions,
@@ -130,10 +146,40 @@ class CommandFJoin : public ServerCommand
* @param newname The new name of the channel; must be the same or a case change of the current name
*/
static void LowerTS(Channel* chan, time_t TS, const std::string& newname);
- void ProcessModeUUIDPair(const std::string& item, TreeSocket* src_socket, Channel* chan, irc::modestacker* modestack);
+ void ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist, FwdFJoinBuilder& fwdfjoin);
public:
CommandFJoin(Module* Creator) : ServerCommand(Creator, "FJOIN", 3) { }
CmdResult Handle(User* user, std::vector<std::string>& params);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_LOCALONLY; }
+
+ class Builder : public CmdBuilder
+ {
+ /** Maximum possible Membership::Id length in decimal digits, used for determining whether a user will fit into
+ * a message or not
+ */
+ static const size_t membid_max_digits = 20;
+ static const size_t maxline = 510;
+ std::string::size_type pos;
+
+ protected:
+ void add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend);
+ bool has_room(std::string::size_type nummodes) const;
+
+ public:
+ Builder(Channel* chan, TreeServer* source = Utils->TreeRoot);
+ void add(Membership* memb)
+ {
+ add(memb, memb->modes.begin(), memb->modes.end());
+ }
+
+ bool has_room(Membership* memb) const
+ {
+ return has_room(memb->modes.size());
+ }
+
+ void clear();
+ const std::string& finalize();
+ };
};
class CommandFMode : public ServerCommand
@@ -181,7 +227,7 @@ class CommandFName : public UserOnlyServerCommand<CommandFName>
class CommandIJoin : public UserOnlyServerCommand<CommandIJoin>
{
public:
- CommandIJoin(Module* Creator) : UserOnlyServerCommand<CommandIJoin>(Creator, "IJOIN", 1) { }
+ CommandIJoin(Module* Creator) : UserOnlyServerCommand<CommandIJoin>(Creator, "IJOIN", 2) { }
CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
};
@@ -190,13 +236,14 @@ class CommandResync : public ServerOnlyServerCommand<CommandResync>
public:
CommandResync(Module* Creator) : ServerOnlyServerCommand<CommandResync>(Creator, "RESYNC", 1) { }
CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_LOCALONLY; }
};
-class CommandAway : public UserOnlyServerCommand<CommandAway>
+class SpanningTree::CommandAway : public UserOnlyServerCommand<SpanningTree::CommandAway>
{
public:
- CommandAway(Module* Creator) : UserOnlyServerCommand<CommandAway>(Creator, "AWAY", 0, 2) { }
- CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& parameters);
+ CommandAway(Module* Creator) : UserOnlyServerCommand<SpanningTree::CommandAway>(Creator, "AWAY", 0, 2) { }
+ CmdResult HandleRemote(::RemoteUser* user, std::vector<std::string>& parameters);
class Builder : public CmdBuilder
{
@@ -243,14 +290,14 @@ class CommandIdle : public UserOnlyServerCommand<CommandIdle>
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
};
-class CommandNick : public UserOnlyServerCommand<CommandNick>
+class SpanningTree::CommandNick : public UserOnlyServerCommand<SpanningTree::CommandNick>
{
public:
- CommandNick(Module* Creator) : UserOnlyServerCommand<CommandNick>(Creator, "NICK", 2) { }
- CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& parameters);
+ CommandNick(Module* Creator) : UserOnlyServerCommand<SpanningTree::CommandNick>(Creator, "NICK", 2) { }
+ CmdResult HandleRemote(::RemoteUser* user, std::vector<std::string>& parameters);
};
-class CommandPing : public ServerCommand
+class SpanningTree::CommandPing : public ServerCommand
{
public:
CommandPing(Module* Creator) : ServerCommand(Creator, "PING", 1) { }
@@ -258,37 +305,39 @@ class CommandPing : public ServerCommand
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
};
-class CommandPong : public ServerOnlyServerCommand<CommandPong>
+class SpanningTree::CommandPong : public ServerOnlyServerCommand<SpanningTree::CommandPong>
{
public:
- CommandPong(Module* Creator) : ServerOnlyServerCommand<CommandPong>(Creator, "PONG", 1) { }
+ CommandPong(Module* Creator) : ServerOnlyServerCommand<SpanningTree::CommandPong>(Creator, "PONG", 1) { }
CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
};
-class CommandPush : public ServerCommand
-{
- public:
- CommandPush(Module* Creator) : ServerCommand(Creator, "PUSH", 2) { }
- CmdResult Handle(User* user, std::vector<std::string>& parameters);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
-};
-
class CommandSave : public ServerCommand
{
public:
+ /** Timestamp of the uuid nick of all users who collided and got their nick changed to uuid
+ */
+ static const time_t SavedTimestamp = 100;
+
CommandSave(Module* Creator) : ServerCommand(Creator, "SAVE", 2) { }
CmdResult Handle(User* user, std::vector<std::string>& parameters);
};
-class CommandServer : public ServerOnlyServerCommand<CommandServer>
+class SpanningTree::CommandServer : public ServerOnlyServerCommand<SpanningTree::CommandServer>
{
+ static void HandleExtra(TreeServer* newserver, const std::vector<std::string>& params);
+
public:
- CommandServer(Module* Creator) : ServerOnlyServerCommand<CommandServer>(Creator, "SERVER", 5) { }
+ CommandServer(Module* Creator) : ServerOnlyServerCommand<SpanningTree::CommandServer>(Creator, "SERVER", 3) { }
CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
class Builder : public CmdBuilder
{
+ void push_property(const char* key, const std::string& val)
+ {
+ push(key).push_raw('=').push_raw(val);
+ }
public:
Builder(TreeServer* server);
};
@@ -308,25 +357,38 @@ class CommandSNONotice : public ServerCommand
CmdResult Handle(User* user, std::vector<std::string>& parameters);
};
-class CommandVersion : public ServerOnlyServerCommand<CommandVersion>
+class CommandEndBurst : public ServerOnlyServerCommand<CommandEndBurst>
{
public:
- CommandVersion(Module* Creator) : ServerOnlyServerCommand<CommandVersion>(Creator, "VERSION", 1) { }
+ CommandEndBurst(Module* Creator) : ServerOnlyServerCommand<CommandEndBurst>(Creator, "ENDBURST") { }
CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
};
-class CommandBurst : public ServerOnlyServerCommand<CommandBurst>
+class CommandSInfo : public ServerOnlyServerCommand<CommandSInfo>
{
public:
- CommandBurst(Module* Creator) : ServerOnlyServerCommand<CommandBurst>(Creator, "BURST") { }
+ CommandSInfo(Module* Creator) : ServerOnlyServerCommand<CommandSInfo>(Creator, "SINFO", 2) { }
CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(TreeServer* server, const char* type, const std::string& value);
+ };
};
-class CommandEndBurst : public ServerOnlyServerCommand<CommandEndBurst>
+class CommandNum : public ServerOnlyServerCommand<CommandNum>
{
public:
- CommandEndBurst(Module* Creator) : ServerOnlyServerCommand<CommandEndBurst>(Creator, "ENDBURST") { }
+ CommandNum(Module* Creator) : ServerOnlyServerCommand<CommandNum>(Creator, "NUM", 3) { }
CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(SpanningTree::RemoteUser* target, const Numeric::Numeric& numeric);
+ };
};
class SpanningTreeCommands
@@ -346,21 +408,20 @@ class SpanningTreeCommands
CommandFHost fhost;
CommandFIdent fident;
CommandFName fname;
- CommandAway away;
+ SpanningTree::CommandAway away;
CommandAddLine addline;
CommandDelLine delline;
CommandEncap encap;
CommandIdle idle;
- CommandNick nick;
- CommandPing ping;
- CommandPong pong;
- CommandPush push;
+ SpanningTree::CommandNick nick;
+ SpanningTree::CommandPing ping;
+ SpanningTree::CommandPong pong;
CommandSave save;
- CommandServer server;
+ SpanningTree::CommandServer server;
CommandSQuit squit;
CommandSNONotice snonotice;
- CommandVersion version;
- CommandBurst burst;
CommandEndBurst endburst;
+ CommandSInfo sinfo;
+ CommandNum num;
SpanningTreeCommands(ModuleSpanningTree* module);
};
diff --git a/src/modules/m_spanningtree/compat.cpp b/src/modules/m_spanningtree/compat.cpp
index 857e95da9..2436e74f8 100644
--- a/src/modules/m_spanningtree/compat.cpp
+++ b/src/modules/m_spanningtree/compat.cpp
@@ -24,6 +24,13 @@
static std::string newline("\n");
+void TreeSocket::WriteLineNoCompat(const std::string& line)
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), line.c_str());
+ this->WriteData(line);
+ this->WriteData(newline);
+}
+
void TreeSocket::WriteLine(const std::string& original_line)
{
if (LinkState == CONNECTED)
@@ -39,7 +46,7 @@ void TreeSocket::WriteLine(const std::string& original_line)
std::string line = original_line;
std::string::size_type a = line.find(' ');
std::string::size_type b = line.find(' ', a + 1);
- std::string command = line.substr(a + 1, b-a-1);
+ std::string command(line, a + 1, b-a-1);
// now try to find a translation entry
// TODO a more efficient lookup method will be needed later
if (proto_version < 1205)
@@ -47,15 +54,21 @@ void TreeSocket::WriteLine(const std::string& original_line)
if (command == "IJOIN")
{
// Convert
- // :<uid> IJOIN <chan> [<ts> [<flags>]]
+ // :<uid> IJOIN <chan> <membid> [<ts> [<flags>]]
// to
// :<sid> FJOIN <chan> <ts> + [<flags>],<uuid>
std::string::size_type c = line.find(' ', b + 1);
if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ // Erase membership id first
+ line.erase(c, d-c);
+ if (d == std::string::npos)
{
// No TS or modes in the command
// :22DAAAAAB IJOIN #chan
- const std::string channame = line.substr(b+1, c-b-1);
+ const std::string channame(line, b+1, c-b-1);
Channel* chan = ServerInstance->FindChan(channame);
if (!chan)
return;
@@ -66,7 +79,7 @@ void TreeSocket::WriteLine(const std::string& original_line)
}
else
{
- std::string::size_type d = line.find(' ', c + 1);
+ d = line.find(' ', c + 1);
if (d == std::string::npos)
{
// TS present, no modes
@@ -169,29 +182,152 @@ void TreeSocket::WriteLine(const std::string& original_line)
line.erase(colon, 1);
}
}
+ else if (command == "INVITE")
+ {
+ // :22D INVITE 22DAAAAAN #chan TS ExpirationTime
+ // A B C D E
+ if (b == std::string::npos)
+ return;
+
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ if (d == std::string::npos)
+ return;
+
+ std::string::size_type e = line.find(' ', d + 1);
+ // If there is no expiration time then everything will be erased from 'd'
+ line.erase(d, e-d);
+ }
+ else if (command == "FJOIN")
+ {
+ // Strip membership ids
+ // :22D FJOIN #chan 1234 +f 4:3 :o,22DAAAAAB:15 o,22DAAAAAA:15
+ // :22D FJOIN #chan 1234 +f 4:3 o,22DAAAAAB:15
+ // :22D FJOIN #chan 1234 +Pf 4:3 :
+
+ // If the last parameter is prefixed by a colon then it's a userlist which may have 0 or more users;
+ // if it isn't, then it is a single member
+ std::string::size_type spcolon = line.find(" :");
+ if (spcolon != std::string::npos)
+ {
+ spcolon++;
+ // Loop while there is a ':' in the userlist, this is never true if the channel is empty
+ std::string::size_type pos = std::string::npos;
+ while ((pos = line.rfind(':', pos-1)) > spcolon)
+ {
+ // Find the next space after the ':'
+ std::string::size_type sp = line.find(' ', pos);
+ // Erase characters between the ':' and the next space after it, including the ':' but not the space;
+ // if there is no next space, everything will be erased between pos and the end of the line
+ line.erase(pos, sp-pos);
+ }
+ }
+ else
+ {
+ // Last parameter is a single member
+ std::string::size_type sp = line.rfind(' ');
+ std::string::size_type colon = line.find(':', sp);
+ line.erase(colon);
+ }
+ }
+ else if (command == "KICK")
+ {
+ // Strip membership id if the KICK has one
+ if (b == std::string::npos)
+ return;
+
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ if ((d < line.size()-1) && (original_line[d+1] != ':'))
+ {
+ // There is a third parameter which doesn't begin with a colon, erase it
+ std::string::size_type e = line.find(' ', d + 1);
+ line.erase(d, e-d);
+ }
+ }
+ else if (command == "SINFO")
+ {
+ // :22D SINFO version :InspIRCd-3.0
+ // A B C
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ // Only translating SINFO version, discard everything else
+ if (line.compare(b, 9, " version ", 9))
+ return;
+
+ line = line.substr(0, 5) + "VERSION" + line.substr(c);
+ }
+ else if (command == "SERVER")
+ {
+ // :001 SERVER inspircd.test 002 [<anything> ...] :gecos
+ // A B C
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = c + 4;
+ std::string::size_type spcolon = line.find(" :", d);
+ if (spcolon == std::string::npos)
+ return;
+
+ line.erase(d, spcolon-d);
+ line.insert(c, " * 0");
+
+ if (burstsent)
+ {
+ WriteLineNoCompat(line);
+
+ // Synthesize a :<newserver> BURST <time> message
+ spcolon = line.find(" :");
+ line = CmdBuilder(line.substr(spcolon-3, 3), "BURST").push_int(ServerInstance->Time()).str();
+ }
+ }
+ else if (command == "NUM")
+ {
+ // :<sid> NUM <numeric source sid> <target uuid> <3 digit number> <params>
+ // Translate to
+ // :<sid> PUSH <target uuid> :<numeric source name> <3 digit number> <target nick> <params>
+
+ TreeServer* const numericsource = Utils->FindServerID(line.substr(9, 3));
+ if (!numericsource)
+ return;
+
+ // The nick of the target is necessary for building the PUSH message
+ User* const target = ServerInstance->FindUUID(line.substr(13, UIDGenerator::UUID_LENGTH));
+ if (!target)
+ return;
+
+ std::string push = InspIRCd::Format(":%.*s PUSH %s ::%s %.*s %s", 3, line.c_str()+1, target->uuid.c_str(), numericsource->GetName().c_str(), 3, line.c_str()+23, target->nick.c_str());
+ push.append(line, 26, std::string::npos);
+ push.swap(line);
+ }
}
- ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), line.c_str());
- this->WriteData(line);
- this->WriteData(newline);
+ WriteLineNoCompat(line);
return;
}
}
- ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), original_line.c_str());
- this->WriteData(original_line);
- this->WriteData(newline);
+ WriteLineNoCompat(original_line);
}
namespace
{
- bool InsertCurrentChannelTS(std::vector<std::string>& params)
+ bool InsertCurrentChannelTS(std::vector<std::string>& params, unsigned int chanindex = 0, unsigned int pos = 1)
{
- Channel* chan = ServerInstance->FindChan(params[0]);
+ Channel* chan = ServerInstance->FindChan(params[chanindex]);
if (!chan)
return false;
- // Insert the current TS of the channel between the first and the second parameters
- params.insert(params.begin()+1, ConvToStr(chan->age));
+ // Insert the current TS of the channel after the pos-th parameter
+ params.insert(params.begin()+pos, ConvToStr(chan->age));
return true;
}
}
@@ -288,7 +424,7 @@ bool TreeSocket::PreProcessOldProtocolMessage(User*& who, std::string& cmd, std:
}
else if (cmd == "MODENOTICE")
{
- // MODENOTICE is always supported by 2.0 but it's optional in 2.2.
+ // MODENOTICE is always supported by 2.0 but it's optional in 3.0.
params.insert(params.begin(), "*");
params.insert(params.begin()+1, cmd);
cmd = "ENCAP";
@@ -297,6 +433,148 @@ bool TreeSocket::PreProcessOldProtocolMessage(User*& who, std::string& cmd, std:
{
return false;
}
+ else if (cmd == "INVITE")
+ {
+ // :20D INVITE 22DAAABBB #chan
+ // :20D INVITE 22DAAABBB #chan 123456789
+ // Insert channel timestamp after the channel name; the 3rd parameter, if there, is the invite expiration time
+ return InsertCurrentChannelTS(params, 1, 2);
+ }
+ else if (cmd == "VERSION")
+ {
+ // :20D VERSION :InspIRCd-2.0
+ // change to
+ // :20D SINFO version :InspIRCd-2.0
+ cmd = "SINFO";
+ params.insert(params.begin(), "version");
+ }
+ else if (cmd == "JOIN")
+ {
+ // 2.0 allows and forwards legacy JOINs but we don't, so translate them to FJOINs before processing
+ if ((params.size() != 1) || (IS_SERVER(who)))
+ return false; // Huh?
+
+ cmd = "FJOIN";
+ Channel* chan = ServerInstance->FindChan(params[0]);
+ params.push_back(ConvToStr(chan ? chan->age : ServerInstance->Time()));
+ params.push_back("+");
+ params.push_back(",");
+ params.back().append(who->uuid);
+ who = TreeServer::Get(who)->ServerUser;
+ }
+ else if ((cmd == "FMODE") && (params.size() >= 2))
+ {
+ // Translate user mode changes with timestamp to MODE
+ if (params[0][0] != '#')
+ {
+ User* user = ServerInstance->FindUUID(params[0]);
+ if (!user)
+ return false;
+
+ // Emulate the old nonsensical behavior
+ if (user->age < ServerCommand::ExtractTS(params[1]))
+ return false;
+
+ cmd = "MODE";
+ params.erase(params.begin()+1);
+ }
+ }
+ else if ((cmd == "SERVER") && (params.size() > 4))
+ {
+ // This does not affect the initial SERVER line as it is sent before the link state is CONNECTED
+ // :20D SERVER <name> * 0 <sid> <desc>
+ // change to
+ // :20D SERVER <name> <sid> <desc>
+
+ params[1].swap(params[3]);
+ params.erase(params.begin()+2, params.begin()+4);
+
+ // If the source of this SERVER message is not bursting, then new servers it introduces are bursting
+ TreeServer* server = TreeServer::Get(who);
+ if (!server->IsBursting())
+ params.insert(params.begin()+2, "burst=" + ConvToStr(((uint64_t)ServerInstance->Time())*1000));
+ }
+ else if (cmd == "BURST")
+ {
+ // A server is introducing another one, drop unnecessary BURST
+ return false;
+ }
+ else if (cmd == "SVSWATCH")
+ {
+ // SVSWATCH was removed because nothing was using it, but better be sure
+ return false;
+ }
+ else if (cmd == "PUSH")
+ {
+ if ((params.size() != 2) || (!this->MyRoot))
+ return false; // Huh?
+
+ irc::tokenstream ts(params.back());
+
+ std::string srcstr;
+ ts.GetToken(srcstr);
+ srcstr.erase(0, 1);
+
+ std::string token;
+ ts.GetToken(token);
+
+ // See if it's a numeric being sent to the target via PUSH
+ unsigned int numeric_number = 0;
+ if (token.length() == 3)
+ numeric_number = ConvToInt(token);
+
+ if ((numeric_number > 0) && (numeric_number < 1000))
+ {
+ // It's a numeric, translate to NUM
+
+ // srcstr must be a valid server name
+ TreeServer* const numericsource = Utils->FindServer(srcstr);
+ if (!numericsource)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unable to translate PUSH numeric %s to user %s from 1202 protocol server %s: source \"%s\" doesn't exist", token.c_str(), params[0].c_str(), this->MyRoot->GetName().c_str(), srcstr.c_str());
+ return false;
+ }
+
+ cmd = "NUM";
+
+ // Second parameter becomes the target uuid
+ params[0].swap(params[1]);
+ // Replace first param (now the PUSH payload, not needed) with the source sid
+ params[0] = numericsource->GetID();
+
+ params.push_back(InspIRCd::Format("%03u", numeric_number));
+
+ // Ignore the nickname in the numeric in PUSH
+ ts.GetToken(token);
+
+ // Rest of the tokens are the numeric parameters, add them to NUM
+ while (ts.GetToken(token))
+ params.push_back(token);
+ }
+ else if ((token == "PRIVMSG") || (token == "NOTICE"))
+ {
+ // Command is a PRIVMSG/NOTICE
+ cmd.swap(token);
+
+ // Check if the PRIVMSG/NOTICE target is a nickname
+ ts.GetToken(token);
+ if (token.c_str()[0] == '#')
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unable to translate PUSH %s to user %s from 1202 protocol server %s, target \"%s\"", cmd.c_str(), params[0].c_str(), this->MyRoot->GetName().c_str(), token.c_str());
+ return false;
+ }
+
+ // Replace second parameter with the message
+ ts.GetToken(params[1]);
+ }
+ else
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Unable to translate PUSH to user %s from 1202 protocol server %s", params[0].c_str(), this->MyRoot->GetName().c_str());
+ return false;
+ }
+
+ return true;
+ }
return true; // Passthru
}
diff --git a/src/modules/m_spanningtree/delline.cpp b/src/modules/m_spanningtree/delline.cpp
index c76af2fb7..f790dc885 100644
--- a/src/modules/m_spanningtree/delline.cpp
+++ b/src/modules/m_spanningtree/delline.cpp
@@ -26,7 +26,7 @@ CmdResult CommandDelLine::Handle(User* user, std::vector<std::string>& params)
{
const std::string& setter = user->nick;
- /* NOTE: No check needed on 'user', this function safely handles NULL */
+ // XLineManager::DelLine() returns true if the xline existed, false if it didn't
if (ServerInstance->XLines->DelLine(params[1].c_str(), params[0], user))
{
ServerInstance->SNO->WriteToSnoMask('X',"%s removed %s%s on %s", setter.c_str(),
diff --git a/src/modules/m_spanningtree/encap.cpp b/src/modules/m_spanningtree/encap.cpp
index 566f15da8..8059d2a39 100644
--- a/src/modules/m_spanningtree/encap.cpp
+++ b/src/modules/m_spanningtree/encap.cpp
@@ -20,6 +20,7 @@
#include "inspircd.h"
#include "commands.h"
+#include "main.h"
/** ENCAP */
CmdResult CommandEncap::Handle(User* user, std::vector<std::string>& params)
@@ -27,8 +28,18 @@ CmdResult CommandEncap::Handle(User* user, std::vector<std::string>& params)
if (ServerInstance->Config->GetSID() == params[0] || InspIRCd::Match(ServerInstance->Config->ServerName, params[0]))
{
parameterlist plist(params.begin() + 2, params.end());
+
+ // XXX: Workaround for SVS* commands provided by spanningtree not being registered in the core
+ if ((params[1] == "SVSNICK") || (params[1] == "SVSJOIN") || (params[1] == "SVSPART"))
+ {
+ ServerCommand* const scmd = Utils->Creator->CmdManager.GetHandler(params[1]);
+ if (scmd)
+ scmd->Handle(user, plist);
+ return CMD_SUCCESS;
+ }
+
Command* cmd = NULL;
- ServerInstance->Parser->CallHandler(params[1], plist, user, &cmd);
+ ServerInstance->Parser.CallHandler(params[1], plist, user, &cmd);
// Discard return value, ENCAP shall succeed even if the command does not exist
if ((cmd) && (cmd->force_manual_route))
diff --git a/src/modules/m_spanningtree/fjoin.cpp b/src/modules/m_spanningtree/fjoin.cpp
index 26c3413f9..c292373b3 100644
--- a/src/modules/m_spanningtree/fjoin.cpp
+++ b/src/modules/m_spanningtree/fjoin.cpp
@@ -25,6 +25,22 @@
#include "treeserver.h"
#include "treesocket.h"
+/** FJOIN builder for rebuilding incoming FJOINs and splitting them up into multiple messages if necessary
+ */
+class FwdFJoinBuilder : public CommandFJoin::Builder
+{
+ TreeServer* const sourceserver;
+
+ public:
+ FwdFJoinBuilder(Channel* chan, TreeServer* server)
+ : CommandFJoin::Builder(chan, server)
+ , sourceserver(server)
+ {
+ }
+
+ void add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend);
+};
+
/** FJOIN, almost identical to TS6 SJOIN, except for nicklist handling. */
CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
{
@@ -54,19 +70,51 @@ CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
* losing side, so only its own modes get applied. Life is simple for those
* who succeed at internets. :-)
*
+ * Outside of netbursts, the winning side also resyncs the losing side if it
+ * detects that the other side recreated the channel.
+ *
* Syntax:
- * :<sid> FJOIN <chan> <TS> <modes> :[[modes,]<uuid> [[modes,]<uuid> ... ]]
- * The last parameter is a list consisting of zero or more (modelist, uuid)
- * pairs (permanent channels may have zero users). The mode list for each
- * user is a concatenation of the mode letters the user has on the channel
+ * :<sid> FJOIN <chan> <TS> <modes> :[<member> [<member> ...]]
+ * The last parameter is a list consisting of zero or more channel members
+ * (permanent channels may have zero users). Each entry on the list is in the
+ * following format:
+ * [[<modes>,]<uuid>[:<membid>]
+ * <modes> is a concatenation of the mode letters the user has on the channel
* (e.g.: "ov" if the user is opped and voiced). The order of the mode letters
* are not important but if a server ecounters an unknown mode letter, it will
* drop the link to avoid desync.
*
* InspIRCd 2.0 and older required a comma before the uuid even if the user
- * had no prefix modes on the channel, InspIRCd 2.2 and later does not require
+ * had no prefix modes on the channel, InspIRCd 3.0 and later does not require
* a comma in this case anymore.
*
+ * <membid> is a positive integer representing the id of the membership.
+ * If not present (in FJOINs coming from pre-1205 servers), 0 is assumed.
+ *
+ * Forwarding:
+ * FJOIN messages are forwarded with the new TS and modes. Prefix modes of
+ * members on the losing side are not forwarded.
+ * This is required to only have one server on each side of the network who
+ * decides the fate of a channel during a network merge. Otherwise, if the
+ * clock of a server is slightly off it may make a different decision than
+ * the rest of the network and desync.
+ * The prefix modes are always forwarded as-is, or not at all.
+ * One incoming FJOIN may result in more than one FJOIN being generated
+ * and forwarded mainly due to compatibility reasons with non-InspIRCd
+ * servers that don't handle more than 512 char long lines.
+ *
+ * Forwarding examples:
+ * Existing channel #chan with TS 1000, modes +n.
+ * Incoming: :220 FJOIN #chan 1000 +t :o,220AAAAAB:0
+ * Forwarded: :220 FJOIN #chan 1000 +nt :o,220AAAAAB:0
+ * Merge modes and forward the result. Forward their prefix modes as well.
+ *
+ * Existing channel #chan with TS 1000, modes +nt.
+ * Incoming: :220 FJOIN #CHAN 2000 +i :ov,220AAAAAB:0 o,220AAAAAC:20
+ * Forwarded: :220 FJOIN #chan 1000 +nt :,220AAAAAB:0 ,220AAAAAC:20
+ * Drop their modes, forward our modes and TS, use our channel name
+ * capitalization. Don't forward prefix modes.
+ *
*/
time_t TS = ServerCommand::ExtractTS(params[1]);
@@ -74,6 +122,7 @@ CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
const std::string& channel = params[0];
Channel* chan = ServerInstance->FindChan(channel);
bool apply_other_sides_modes = true;
+ TreeServer* const sourceserver = TreeServer::Get(srcuser);
if (!chan)
{
@@ -84,11 +133,19 @@ CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
time_t ourTS = chan->age;
if (TS != ourTS)
{
- ServerInstance->SNO->WriteToSnoMask('d', "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %lu",
- chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (unsigned long)(ourTS - TS));
+ ServerInstance->SNO->WriteToSnoMask('d', "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %ld",
+ chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (long)(ourTS - TS));
/* If our TS is less than theirs, we dont accept their modes */
if (ourTS < TS)
{
+ // If the source server isn't bursting then this FJOIN is the result of them recreating the channel with a higher TS.
+ // This happens if the last user on the channel hops and before the PART propagates a user on another server joins. Fix it by doing a resync.
+ // Servers behind us won't react this way because the forwarded FJOIN will have the correct TS.
+ if (!sourceserver->IsBursting())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s recreated channel %s with higher TS, resyncing", sourceserver->GetName().c_str(), chan->name.c_str());
+ sourceserver->GetSocket()->SyncChannel(chan);
+ }
apply_other_sides_modes = false;
}
else if (ourTS > TS)
@@ -107,66 +164,46 @@ CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
}
}
- /* First up, apply their channel modes if they won the TS war */
+ // Apply their channel modes if we have to
+ Modes::ChangeList modechangelist;
if (apply_other_sides_modes)
{
- // Need to use a modestacker here due to maxmodes
- irc::modestacker stack(true);
- std::vector<std::string>::const_iterator paramit = params.begin() + 3;
- const std::vector<std::string>::const_iterator lastparamit = ((params.size() > 3) ? (params.end() - 1) : params.end());
- for (std::string::const_iterator i = params[2].begin(); i != params[2].end(); ++i)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
- if (!mh)
- continue;
-
- std::string modeparam;
- if ((paramit != lastparamit) && (mh->GetNumParams(true)))
- {
- modeparam = *paramit;
- ++paramit;
- }
-
- stack.Push(*i, modeparam);
- }
-
- std::vector<std::string> modelist;
-
- // Mode parser needs to know what channel to act on.
- modelist.push_back(params[0]);
-
- while (stack.GetStackedLine(modelist))
- {
- ServerInstance->Modes->Process(modelist, srcuser, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE);
- modelist.erase(modelist.begin() + 1, modelist.end());
- }
+ ServerInstance->Modes.ModeParamsToChangeList(srcuser, MODETYPE_CHANNEL, params, modechangelist, 2, params.size() - 1);
+ ServerInstance->Modes->Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE);
+ // Reuse for prefix modes
+ modechangelist.clear();
}
- irc::modestacker modestack(true);
- TreeSocket* src_socket = TreeServer::Get(srcuser)->GetSocket();
+ // Build a new FJOIN for forwarding. Put the correct TS in it and the current modes of the channel
+ // after applying theirs. If they lost, the prefix modes from their message are not forwarded.
+ FwdFJoinBuilder fwdfjoin(chan, sourceserver);
- /* Now, process every 'modes,uuid' pair */
- irc::tokenstream users(*params.rbegin());
+ // Process every member in the message
+ irc::tokenstream users(params.back());
std::string item;
- irc::modestacker* modestackptr = (apply_other_sides_modes ? &modestack : NULL);
+ Modes::ChangeList* modechangelistptr = (apply_other_sides_modes ? &modechangelist : NULL);
while (users.GetToken(item))
{
- ProcessModeUUIDPair(item, src_socket, chan, modestackptr);
+ ProcessModeUUIDPair(item, sourceserver, chan, modechangelistptr, fwdfjoin);
}
- /* Flush mode stacker if we lost the FJOIN or had equal TS */
+ fwdfjoin.finalize();
+ fwdfjoin.Forward(sourceserver->GetRoute());
+
+ // Set prefix modes on their users if we lost the FJOIN or had equal TS
if (apply_other_sides_modes)
- CommandFJoin::ApplyModeStack(srcuser, chan, modestack);
+ ServerInstance->Modes->Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY);
return CMD_SUCCESS;
}
-void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeSocket* src_socket, Channel* chan, irc::modestacker* modestack)
+void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist, FwdFJoinBuilder& fwdfjoin)
{
std::string::size_type comma = item.find(',');
// Comma not required anymore if the user has no modes
- std::string uuid = ((comma == std::string::npos) ? item : item.substr(comma+1));
+ const std::string::size_type ubegin = (comma == std::string::npos ? 0 : comma+1);
+ std::string uuid(item, ubegin, UIDGenerator::UUID_LENGTH);
User* who = ServerInstance->FindUUID(uuid);
if (!who)
{
@@ -174,6 +211,7 @@ void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeSocket* src_
return;
}
+ TreeSocket* src_socket = sourceserver->GetSocket();
/* Check that the user's 'direction' is correct */
TreeServer* route_back_again = TreeServer::Get(who);
if (route_back_again->GetSocket() != src_socket)
@@ -181,79 +219,126 @@ void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeSocket* src_
return;
}
+ std::string::const_iterator modeendit = item.begin(); // End of the "ov" mode string
/* Check if the user received at least one mode */
- if ((modestack) && (comma > 0) && (comma != std::string::npos))
+ if ((modechangelist) && (comma != std::string::npos))
{
+ modeendit += comma;
/* Iterate through the modes and see if they are valid here, if so, apply */
- std::string::const_iterator commait = item.begin()+comma;
- for (std::string::const_iterator i = item.begin(); i != commait; ++i)
+ for (std::string::const_iterator i = item.begin(); i != modeendit; ++i)
{
- if (!ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL))
+ ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
+ if (!mh)
throw ProtocolException("Unrecognised mode '" + std::string(1, *i) + "'");
/* Add any modes this user had to the mode stack */
- modestack->Push(*i, who->nick);
+ modechangelist->push_add(mh, who->nick);
}
}
- chan->ForceJoin(who, NULL, route_back_again->bursting);
+ Membership* memb = chan->ForceJoin(who, NULL, sourceserver->IsBursting());
+ if (!memb)
+ {
+ // User was already on the channel, forward because of the modes they potentially got
+ memb = chan->GetUser(who);
+ if (memb)
+ fwdfjoin.add(memb, item.begin(), modeendit);
+ return;
+ }
+
+ // Assign the id to the new Membership
+ Membership::Id membid = 0;
+ const std::string::size_type colon = item.rfind(':');
+ if (colon != std::string::npos)
+ membid = Membership::IdFromString(item.substr(colon+1));
+ memb->id = membid;
+
+ // Add member to fwdfjoin with prefix modes
+ fwdfjoin.add(memb, item.begin(), modeendit);
}
void CommandFJoin::RemoveStatus(Channel* c)
{
- irc::modestacker stack(false);
+ Modes::ChangeList changelist;
const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
{
ModeHandler* mh = i->second;
- /* Passing a pointer to a modestacker here causes the mode to be put onto the mode stack,
- * rather than applied immediately. Module unloads require this to be done immediately,
- * for this function we require tidyness instead. Fixes bug #493
- */
- mh->RemoveMode(c, stack);
+ // Add the removal of this mode to the changelist. This handles all kinds of modes, including prefix modes.
+ mh->RemoveMode(c, changelist);
}
- ApplyModeStack(ServerInstance->FakeClient, c, stack);
-}
-
-void CommandFJoin::ApplyModeStack(User* srcuser, Channel* c, irc::modestacker& stack)
-{
- parameterlist stackresult;
- stackresult.push_back(c->name);
-
- while (stack.GetStackedLine(stackresult))
- {
- ServerInstance->Modes->Process(stackresult, srcuser, ModeParser::MODE_LOCALONLY);
- stackresult.erase(stackresult.begin() + 1, stackresult.end());
- }
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, c, NULL, changelist, ModeParser::MODE_LOCALONLY);
}
void CommandFJoin::LowerTS(Channel* chan, time_t TS, const std::string& newname)
{
if (Utils->AnnounceTSChange)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), newname.c_str(), (unsigned long) chan->age, (unsigned long) TS);
+ chan->WriteNotice(InspIRCd::Format("TS for %s changed from %lu to %lu", newname.c_str(), (unsigned long) chan->age, (unsigned long) TS));
// While the name is equal in case-insensitive compare, it might differ in case; use the remote version
chan->name = newname;
chan->age = TS;
- // Remove all pending invites
- chan->ClearInvites();
-
// Clear all modes
CommandFJoin::RemoveStatus(chan);
// Unset all extensions
chan->FreeAllExtItems();
- // Clear the topic, if it isn't empty then send a topic change message to local users
- if (!chan->topic.empty())
+ // Clear the topic
+ chan->SetTopic(ServerInstance->FakeClient, std::string(), 0);
+ chan->setby.clear();
+}
+
+CommandFJoin::Builder::Builder(Channel* chan, TreeServer* source)
+ : CmdBuilder(source->GetID(), "FJOIN")
+{
+ push(chan->name).push_int(chan->age).push_raw(" +");
+ pos = str().size();
+ push_raw(chan->ChanModes(true)).push_raw(" :");
+}
+
+void CommandFJoin::Builder::add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend)
+{
+ push_raw(mbegin, mend).push_raw(',').push_raw(memb->user->uuid);
+ push_raw(':').push_raw_int(memb->id);
+ push_raw(' ');
+}
+
+bool CommandFJoin::Builder::has_room(std::string::size_type nummodes) const
+{
+ return ((str().size() + nummodes + UIDGenerator::UUID_LENGTH + 2 + membid_max_digits + 1) <= maxline);
+}
+
+void CommandFJoin::Builder::clear()
+{
+ content.erase(pos);
+ push_raw(" :");
+}
+
+const std::string& CommandFJoin::Builder::finalize()
+{
+ if (*content.rbegin() == ' ')
+ content.erase(content.size()-1);
+ return str();
+}
+
+void FwdFJoinBuilder::add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend)
+{
+ // Pseudoserver compatibility:
+ // Some pseudoservers do not handle lines longer than 512 so we split long FJOINs into multiple messages.
+ // The forwarded FJOIN can end up being longer than the original one if we have more modes set and won, for example.
+
+ // Check if the member fits into the current message. If not, send it and prepare a new one.
+ if (!has_room(std::distance(mbegin, mend)))
{
- chan->topic.clear();
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "TOPIC %s :", chan->name.c_str());
+ finalize();
+ Forward(sourceserver);
+ clear();
}
- chan->setby.clear();
- chan->topicset = 0;
+ // Add the member and their modes exactly as they sent them
+ CommandFJoin::Builder::add(memb, mbegin, mend);
}
diff --git a/src/modules/m_spanningtree/fmode.cpp b/src/modules/m_spanningtree/fmode.cpp
index 036a94947..e6f49c5b9 100644
--- a/src/modules/m_spanningtree/fmode.cpp
+++ b/src/modules/m_spanningtree/fmode.cpp
@@ -21,51 +21,35 @@
#include "inspircd.h"
#include "commands.h"
-/** FMODE command - server mode with timestamp checks */
+/** FMODE command - channel mode change with timestamp checks */
CmdResult CommandFMode::Handle(User* who, std::vector<std::string>& params)
{
time_t TS = ServerCommand::ExtractTS(params[1]);
- /* Extract the TS value of the object, either User or Channel */
- time_t ourTS;
- if (params[0][0] == '#')
- {
- Channel* chan = ServerInstance->FindChan(params[0]);
- if (!chan)
- /* Oops, channel doesn't exist! */
- return CMD_FAILURE;
-
- ourTS = chan->age;
- }
- else
- {
- User* user = ServerInstance->FindUUID(params[0]);
- if (!user)
- return CMD_FAILURE;
-
- if (IS_SERVER(user))
- throw ProtocolException("Invalid target");
+ Channel* const chan = ServerInstance->FindChan(params[0]);
+ if (!chan)
+ // Channel doesn't exist
+ return CMD_FAILURE;
- ourTS = user->age;
- }
+ // Extract the TS of the channel in question
+ time_t ourTS = chan->age;
/* If the TS is greater than ours, we drop the mode and don't pass it anywhere.
*/
if (TS > ourTS)
return CMD_FAILURE;
- /* TS is equal or less: Merge the mode changes into ours and pass on.
+ /* TS is equal or less: apply the mode change locally and forward the message
*/
- std::vector<std::string> modelist;
- modelist.reserve(params.size()-1);
- /* Insert everything into modelist except the TS (params[1]) */
- modelist.push_back(params[0]);
- modelist.insert(modelist.end(), params.begin()+2, params.end());
+
+ // Turn modes into a Modes::ChangeList; may have more elements than max modes
+ Modes::ChangeList changelist;
+ ServerInstance->Modes.ModeParamsToChangeList(who, MODETYPE_CHANNEL, params, changelist, 2);
ModeParser::ModeProcessFlag flags = ModeParser::MODE_LOCALONLY;
if ((TS == ourTS) && IS_SERVER(who))
flags |= ModeParser::MODE_MERGE;
- ServerInstance->Modes->Process(modelist, who, flags);
+ ServerInstance->Modes->Process(who, chan, NULL, changelist, flags);
return CMD_SUCCESS;
}
diff --git a/src/modules/m_spanningtree/ftopic.cpp b/src/modules/m_spanningtree/ftopic.cpp
index 3c76c928a..de72d162a 100644
--- a/src/modules/m_spanningtree/ftopic.cpp
+++ b/src/modules/m_spanningtree/ftopic.cpp
@@ -63,19 +63,7 @@ CmdResult CommandFTopic::Handle(User* user, std::vector<std::string>& params)
return CMD_FAILURE;
}
- if (c->topic != newtopic)
- {
- // Update topic only when it differs from current topic
- c->topic.assign(newtopic, 0, ServerInstance->Config->Limits.MaxTopic);
- c->WriteChannel(user, "TOPIC %s :%s", c->name.c_str(), c->topic.c_str());
- }
-
- // Update setter and settime
- c->setby.assign(setter, 0, 128);
- c->topicset = ts;
-
- FOREACH_MOD(OnPostTopicChange, (user, c, c->topic));
-
+ c->SetTopic(user, newtopic, ts, &setter);
return CMD_SUCCESS;
}
diff --git a/src/modules/m_spanningtree/hmac.cpp b/src/modules/m_spanningtree/hmac.cpp
index 895323a02..2001d560d 100644
--- a/src/modules/m_spanningtree/hmac.cpp
+++ b/src/modules/m_spanningtree/hmac.cpp
@@ -69,37 +69,41 @@ bool TreeSocket::ComparePass(const Link& link, const std::string &theirs)
capab->auth_fingerprint = !link.Fingerprint.empty();
capab->auth_challenge = !capab->ourchallenge.empty() && !capab->theirchallenge.empty();
+ std::string fp = SSLClientCert::GetFingerprint(this);
+ if (capab->auth_fingerprint)
+ {
+ /* Require fingerprint to exist and match */
+ if (link.Fingerprint != fp)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"Invalid SSL certificate fingerprint on link %s: need \"%s\" got \"%s\"",
+ link.Name.c_str(), link.Fingerprint.c_str(), fp.c_str());
+ SendError("Invalid SSL certificate fingerprint " + fp + " - expected " + link.Fingerprint);
+ return false;
+ }
+ }
+
if (capab->auth_challenge)
{
std::string our_hmac = MakePass(link.RecvPass, capab->ourchallenge);
- /* Straight string compare of hashes */
- if (our_hmac != theirs)
+ // Use the timing-safe compare function to compare the hashes
+ if (!InspIRCd::TimingSafeCompare(our_hmac, theirs))
return false;
}
else
{
- /* Straight string compare of plaintext */
- if (link.RecvPass != theirs)
+ // Use the timing-safe compare function to compare the passwords
+ if (!InspIRCd::TimingSafeCompare(link.RecvPass, theirs))
return false;
}
- std::string fp = SSLClientCert::GetFingerprint(this);
- if (capab->auth_fingerprint)
+ // Tell opers to set up fingerprint verification if it's not already set up and the SSL mod gave us a fingerprint
+ // this time
+ if ((!capab->auth_fingerprint) && (!fp.empty()))
{
- /* Require fingerprint to exist and match */
- if (link.Fingerprint != fp)
- {
- ServerInstance->SNO->WriteToSnoMask('l',"Invalid SSL fingerprint on link %s: need \"%s\" got \"%s\"",
- link.Name.c_str(), link.Fingerprint.c_str(), fp.c_str());
- SendError("Provided invalid SSL fingerprint " + fp + " - expected " + link.Fingerprint);
- return false;
- }
- }
- else if (!fp.empty())
- {
- ServerInstance->SNO->WriteToSnoMask('l', "SSL fingerprint for link %s is \"%s\". "
+ ServerInstance->SNO->WriteToSnoMask('l', "SSL certificate fingerprint for link %s is \"%s\". "
"You can improve security by specifying this in <link:fingerprint>.", link.Name.c_str(), fp.c_str());
}
+
return true;
}
diff --git a/src/modules/m_spanningtree/idle.cpp b/src/modules/m_spanningtree/idle.cpp
index 1b020701b..ad58e52f0 100644
--- a/src/modules/m_spanningtree/idle.cpp
+++ b/src/modules/m_spanningtree/idle.cpp
@@ -35,7 +35,7 @@ CmdResult CommandIdle::HandleRemote(RemoteUser* issuer, std::vector<std::string>
*/
User* target = ServerInstance->FindUUID(params[0]);
- if ((!target) || (IS_SERVER(target) || (target->registered != REG_ALL)))
+ if ((!target) || (target->registered != REG_ALL))
return CMD_FAILURE;
LocalUser* localtarget = IS_LOCAL(target);
@@ -47,7 +47,7 @@ CmdResult CommandIdle::HandleRemote(RemoteUser* issuer, std::vector<std::string>
if (params.size() >= 2)
{
- ServerInstance->Parser->CallHandler("WHOIS", params, issuer);
+ ServerInstance->Parser.CallHandler("WHOIS", params, issuer);
}
else
{
diff --git a/src/modules/m_spanningtree/ijoin.cpp b/src/modules/m_spanningtree/ijoin.cpp
index 34bd44a9b..c2dbcf7f5 100644
--- a/src/modules/m_spanningtree/ijoin.cpp
+++ b/src/modules/m_spanningtree/ijoin.cpp
@@ -38,17 +38,20 @@ CmdResult CommandIJoin::HandleRemote(RemoteUser* user, std::vector<std::string>&
}
bool apply_modes;
- if (params.size() > 1)
+ if (params.size() > 3)
{
- time_t RemoteTS = ServerCommand::ExtractTS(params[1]);
- if (RemoteTS < chan->age)
- throw ProtocolException("Attempted to lower TS via IJOIN. LocalTS=" + ConvToStr(chan->age));
- apply_modes = ((params.size() > 2) && (RemoteTS == chan->age));
+ time_t RemoteTS = ServerCommand::ExtractTS(params[2]);
+ apply_modes = (RemoteTS <= chan->age);
}
else
apply_modes = false;
- chan->ForceJoin(user, apply_modes ? &params[2] : NULL);
+ // Join the user and set the membership id to what they sent
+ Membership* memb = chan->ForceJoin(user, apply_modes ? &params[3] : NULL);
+ if (!memb)
+ return CMD_FAILURE;
+
+ memb->id = Membership::IdFromString(params[1]);
return CMD_SUCCESS;
}
diff --git a/src/modules/m_spanningtree/link.h b/src/modules/m_spanningtree/link.h
index 21213fb3e..632982623 100644
--- a/src/modules/m_spanningtree/link.h
+++ b/src/modules/m_spanningtree/link.h
@@ -24,7 +24,7 @@ class Link : public refcountbase
{
public:
reference<ConfigTag> tag;
- irc::string Name;
+ std::string Name;
std::string IPAddr;
int Port;
std::string SendPass;
diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp
index 0dc680ca0..6bf9e8044 100644
--- a/src/modules/m_spanningtree/main.cpp
+++ b/src/modules/m_spanningtree/main.cpp
@@ -25,6 +25,7 @@
#include "socket.h"
#include "xline.h"
#include "iohook.h"
+#include "modules/spanningtree.h"
#include "resolvers.h"
#include "main.h"
@@ -33,11 +34,15 @@
#include "link.h"
#include "treesocket.h"
#include "commands.h"
-#include "protocolinterface.h"
+#include "translate.h"
ModuleSpanningTree::ModuleSpanningTree()
: rconnect(this), rsquit(this), map(this)
- , commands(NULL), DNS(this, "DNS")
+ , commands(this)
+ , currmembid(0)
+ , eventprov(this, "event/spanningtree")
+ , DNS(this, "DNS")
+ , loopCall(false)
{
}
@@ -46,9 +51,9 @@ SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module)
uid(module), opertype(module), fjoin(module), ijoin(module), resync(module),
fmode(module), ftopic(module), fhost(module), fident(module), fname(module),
away(module), addline(module), delline(module), encap(module), idle(module),
- nick(module), ping(module), pong(module), push(module), save(module),
- server(module), squit(module), snonotice(module), version(module),
- burst(module), endburst(module)
+ nick(module), ping(module), pong(module), save(module),
+ server(module), squit(module), snonotice(module),
+ endburst(module), sinfo(module), num(module)
{
}
@@ -56,32 +61,40 @@ namespace
{
void SetLocalUsersServer(Server* newserver)
{
+ // Does not change the server of quitting users because those are not in the list
+
ServerInstance->FakeClient->server = newserver;
- const LocalUserList& list = ServerInstance->Users->local_users;
- for (LocalUserList::const_iterator i = list.begin(); i != list.end(); ++i)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
(*i)->server = newserver;
}
+
+ void ResetMembershipIds()
+ {
+ // Set all membership ids to 0
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* user = *i;
+ for (User::ChanList::iterator j = user->chans.begin(); j != user->chans.end(); ++j)
+ (*j)->id = 0;
+ }
+ }
}
void ModuleSpanningTree::init()
{
ServerInstance->SNO->EnableSnomask('l', "LINK");
+ ResetMembershipIds();
+
Utils = new SpanningTreeUtilities(this);
Utils->TreeRoot = new TreeServer;
- commands = new SpanningTreeCommands(this);
- delete ServerInstance->PI;
- ServerInstance->PI = new SpanningTreeProtocolInterface;
+ ServerInstance->PI = &protocolinterface;
delete ServerInstance->FakeClient->server;
SetLocalUsersServer(Utils->TreeRoot);
-
- loopCall = false;
- SplitInProgress = false;
-
- // update our local user count
- Utils->TreeRoot->UserCount = ServerInstance->Users->local_users.size();
}
void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
@@ -115,16 +128,15 @@ void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
else if ((Current->Hidden) && (!user->IsOper()))
return;
- user->WriteNumeric(RPL_LINKS, "%s %s :%d %s", Current->GetName().c_str(),
- (Utils->FlatLinks && (!user->IsOper())) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
- (Utils->FlatLinks && (!user->IsOper())) ? 0 : hops,
- Current->GetDesc().c_str());
+ user->WriteNumeric(RPL_LINKS, Current->GetName(),
+ (((Utils->FlatLinks) && (!user->IsOper())) ? ServerInstance->Config->ServerName : Parent),
+ InspIRCd::Format("%d %s", (((Utils->FlatLinks) && (!user->IsOper())) ? 0 : hops), Current->GetDesc().c_str()));
}
void ModuleSpanningTree::HandleLinks(const std::vector<std::string>& parameters, User* user)
{
ShowLinks(Utils->TreeRoot,user,0);
- user->WriteNumeric(RPL_ENDOFLINKS, "* :End of /LINKS list.");
+ user->WriteNumeric(RPL_ENDOFLINKS, '*', "End of /LINKS list.");
}
std::string ModuleSpanningTree::TimeToStr(time_t secs)
@@ -141,70 +153,6 @@ std::string ModuleSpanningTree::TimeToStr(time_t secs)
+ ConvToStr(secs) + "s");
}
-void ModuleSpanningTree::DoPingChecks(time_t curtime)
-{
- /*
- * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
- * This prevents lost REMOTECONNECT notices
- */
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
-
-restart:
- for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
- {
- TreeServer *s = i->second;
-
- // Skip myself
- if (s->IsRoot())
- continue;
-
- if (s->GetSocket()->GetLinkState() == DYING)
- {
- s->GetSocket()->Close();
- goto restart;
- }
-
- // Do not ping servers that are not fully connected yet!
- // Servers which are connected to us have IsLocal() == true and if they're fully connected
- // then Socket->LinkState == CONNECTED. Servers that are linked to another server are always fully connected.
- if (s->IsLocal() && s->GetSocket()->GetLinkState() != CONNECTED)
- continue;
-
- // Now do PING checks on all servers
- // Only ping if this server needs one
- if (curtime >= s->NextPingTime())
- {
- // And if they answered the last
- if (s->AnsweredLastPing())
- {
- // They did, send a ping to them
- s->SetNextPingTime(curtime + Utils->PingFreq);
- s->GetSocket()->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " + s->GetID());
- s->LastPingMsec = ts;
- }
- else
- {
- // They didn't answer the last ping, if they are locally connected, get rid of them.
- if (s->IsLocal())
- {
- TreeSocket* sock = s->GetSocket();
- sock->SendError("Ping timeout");
- sock->Close();
- goto restart;
- }
- }
- }
-
- // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
- if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
- {
- /* The server hasnt responded, send a warning to opers */
- ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", s->GetName().c_str(), Utils->PingWarnTime);
- s->Warned = true;
- }
- }
-}
-
void ModuleSpanningTree::ConnectServer(Autoconnect* a, bool on_timer)
{
if (!a)
@@ -246,13 +194,12 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
{
bool ipvalid = true;
- if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name), rfc_case_insensitive_map))
+ if (InspIRCd::Match(ServerInstance->Config->ServerName, x->Name, ascii_case_insensitive_map))
{
ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Not connecting to myself.");
return;
}
- DNS::QueryType start_type = DNS::QUERY_AAAA;
if (strchr(x->IPAddr.c_str(),':'))
{
in6_addr n;
@@ -269,7 +216,7 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
/* Do we already have an IP? If so, no need to resolve it. */
if (ipvalid)
{
- /* Gave a hook, but it wasnt one we know */
+ // Create a TreeServer object that will start connecting immediately in the background
TreeSocket* newsocket = new TreeSocket(x, y, x->IPAddr);
if (newsocket->GetFd() > -1)
{
@@ -288,6 +235,15 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
}
else
{
+ // Guess start_type from bindip aftype
+ DNS::QueryType start_type = DNS::QUERY_AAAA;
+ irc::sockets::sockaddrs bind;
+ if ((!x->Bind.empty()) && (irc::sockets::aptosa(x->Bind, 0, bind)))
+ {
+ if (bind.sa.sa_family == AF_INET)
+ start_type = DNS::QUERY_A;
+ }
+
ServernameResolver* snr = new ServernameResolver(*DNS, x->IPAddr, x, start_type, y);
try
{
@@ -340,7 +296,7 @@ void ModuleSpanningTree::DoConnectTimeout(time_t curtime)
ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& parameters, User* user)
{
- // we've already checked if pcnt > 0, so this is safe
+ // We've already confirmed that !parameters.empty(), so this is safe
TreeServer* found = Utils->FindServerMask(parameters[0]);
if (found)
{
@@ -349,77 +305,78 @@ ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& para
// Pass to default VERSION handler.
return MOD_RES_PASSTHRU;
}
- std::string Version = found->GetVersion();
- user->WriteNumeric(RPL_VERSION, ":%s", Version.c_str());
+
+ // If an oper wants to see the version then show the full version string instead of the normal,
+ // but only if it is non-empty.
+ // If it's empty it might be that the server is still syncing (full version hasn't arrived yet)
+ // or the server is a 2.0 server and does not send a full version.
+ bool showfull = ((user->IsOper()) && (!found->GetFullVersion().empty()));
+ const std::string& Version = (showfull ? found->GetFullVersion() : found->GetVersion());
+ user->WriteNumeric(RPL_VERSION, Version);
}
else
{
- user->WriteNumeric(ERR_NOSUCHSERVER, "%s :No such server", parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHSERVER, parameters[0], "No such server");
}
return MOD_RES_DENY;
}
-/* This method will attempt to get a message to a remote user.
- */
-void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...)
-{
- std::string text;
- VAFORMAT(text, format, format);
-
- if (IS_LOCAL(user))
- user->WriteNotice(text);
- else
- ServerInstance->PI->SendUserNotice(user, text);
-}
-
ModResult ModuleSpanningTree::HandleConnect(const std::vector<std::string>& parameters, User* user)
{
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
{
Link* x = *i;
- if (InspIRCd::Match(x->Name.c_str(),parameters[0], rfc_case_insensitive_map))
+ if (InspIRCd::Match(x->Name, parameters[0], ascii_case_insensitive_map))
{
- if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name), rfc_case_insensitive_map))
+ if (InspIRCd::Match(ServerInstance->Config->ServerName, x->Name, ascii_case_insensitive_map))
{
- RemoteMessage(user, "*** CONNECT: Server \002%s\002 is ME, not connecting.",x->Name.c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: Server \002%s\002 is ME, not connecting.", x->Name.c_str()));
return MOD_RES_DENY;
}
- TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
+ TreeServer* CheckDupe = Utils->FindServer(x->Name);
if (!CheckDupe)
{
- RemoteMessage(user, "*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
+ user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: Connecting to server: \002%s\002 (%s:%d)", x->Name.c_str(), (x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()), x->Port));
ConnectServer(x);
return MOD_RES_DENY;
}
else
{
- RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), CheckDupe->GetParent()->GetName().c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), CheckDupe->GetParent()->GetName().c_str()));
return MOD_RES_DENY;
}
}
}
- RemoteMessage(user, "*** CONNECT: No server matching \002%s\002 could be found in the config file.",parameters[0].c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: No server matching \002%s\002 could be found in the config file.", parameters[0].c_str()));
return MOD_RES_DENY;
}
-void ModuleSpanningTree::On005Numeric(std::map<std::string, std::string>& tokens)
-{
- tokens["MAP"];
-}
-
-void ModuleSpanningTree::OnUserInvite(User* source,User* dest,Channel* channel, time_t expiry)
+void ModuleSpanningTree::OnUserInvite(User* source, User* dest, Channel* channel, time_t expiry, unsigned int notifyrank, CUList& notifyexcepts)
{
if (IS_LOCAL(source))
{
CmdBuilder params(source, "INVITE");
params.push_back(dest->uuid);
params.push_back(channel->name);
+ params.push_int(channel->age);
params.push_back(ConvToStr(expiry));
params.Broadcast();
}
}
+ModResult ModuleSpanningTree::OnPreTopicChange(User* user, Channel* chan, const std::string& topic)
+{
+ // XXX: Deny topic changes if the current topic set time is the current time or is in the future because
+ // other servers will drop our FTOPIC. This restriction will be removed when the protocol is updated.
+ if ((chan->topicset >= ServerInstance->Time()) && (Utils->serverlist.size() > 1))
+ {
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, "Retry topic change later");
+ return MOD_RES_DENY;
+ }
+ return MOD_RES_PASSTHRU;
+}
+
void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std::string &topic)
{
// Drop remote events on the floor.
@@ -463,7 +420,6 @@ void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type,
void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
{
AutoConnectServers(curtime);
- DoPingChecks(curtime);
DoConnectTimeout(curtime);
}
@@ -494,19 +450,21 @@ void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created_by
if (!IS_LOCAL(memb->user))
return;
+ // Assign the current membership id to the new Membership and increase it
+ memb->id = currmembid++;
+
if (created_by_local)
{
- CmdBuilder params("FJOIN");
- params.push_back(memb->chan->name);
- params.push_back(ConvToStr(memb->chan->age));
- params.push_raw(" +").push_raw(memb->chan->ChanModes(true));
- params.push(memb->modes).push_raw(',').push_raw(memb->user->uuid);
+ CommandFJoin::Builder params(memb->chan);
+ params.add(memb);
+ params.finalize();
params.Broadcast();
}
else
{
CmdBuilder params(memb->user, "IJOIN");
params.push_back(memb->chan->name);
+ params.push_int(memb->id);
if (!memb->modes.empty())
{
params.push_back(ConvToStr(memb->chan->age));
@@ -566,7 +524,8 @@ void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const
// Hide the message if one of the following is true:
// - User is being quit due to a netsplit and quietbursts is on
// - Server is a silent uline
- bool hide = (((this->SplitInProgress) && (Utils->quiet_bursts)) || (user->server->IsSilentULine()));
+ TreeServer* server = TreeServer::Get(user);
+ bool hide = (((server->IsDead()) && (Utils->quiet_bursts)) || (server->IsSilentULine()));
if (!hide)
{
ServerInstance->SNO->WriteToSnoMask('Q', "Client exiting on server %s: %s (%s) [%s]",
@@ -574,7 +533,7 @@ void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const
}
}
- // Regardless, We need to modify the user Counts..
+ // Regardless, update the UserCount
TreeServer::Get(user)->UserCount--;
}
@@ -588,12 +547,9 @@ void ModuleSpanningTree::OnUserPostNick(User* user, const std::string &oldnick)
params.push_back(ConvToStr(user->age));
params.Broadcast();
}
- else if (!loopCall && user->nick == user->uuid)
+ else if (!loopCall)
{
- CmdBuilder params("SAVE");
- params.push_back(user->uuid);
- params.push_back(ConvToStr(user->age));
- params.Broadcast();
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Changed nick of remote user %s from %s to %s TS %lu by ourselves!", user->uuid.c_str(), oldnick.c_str(), user->nick.c_str(), (unsigned long) user->age);
}
}
@@ -605,6 +561,9 @@ void ModuleSpanningTree::OnUserKick(User* source, Membership* memb, const std::s
CmdBuilder params(source, "KICK");
params.push_back(memb->chan->name);
params.push_back(memb->user->uuid);
+ // If a remote user is being kicked by us then send the membership id in the kick too
+ if (!IS_LOCAL(memb->user))
+ params.push_int(memb->id);
params.push_last(reason);
params.Broadcast();
}
@@ -627,6 +586,16 @@ void ModuleSpanningTree::OnPreRehash(User* user, const std::string &parameter)
void ModuleSpanningTree::ReadConfig(ConfigStatus& status)
{
+ // Did this rehash change the description of this server?
+ const std::string& newdesc = ServerInstance->Config->ServerDesc;
+ if (newdesc != Utils->TreeRoot->GetDesc())
+ {
+ // Broadcast a SINFO desc message to let the network know about the new description. This is the description
+ // string that is sent in the SERVER message initially and shown for example in WHOIS.
+ // We don't need to update the field itself in the Server object - the core does that.
+ CommandSInfo::Builder(Utils->TreeRoot, "desc", newdesc).Broadcast();
+ }
+
// Re-read config stuff
try
{
@@ -665,33 +634,51 @@ void ModuleSpanningTree::OnUnloadModule(Module* mod)
return;
ServerInstance->PI->SendMetaData("modules", "-" + mod->ModuleSourceFile);
+ if (mod == this)
+ {
+ // We are being unloaded, inform modules about all servers splitting which cannot be done later when the servers are actually disconnected
+ const server_hash& servers = Utils->serverlist;
+ for (server_hash::const_iterator i = servers.begin(); i != servers.end(); ++i)
+ {
+ TreeServer* server = i->second;
+ if (!server->IsRoot())
+ FOREACH_MOD_CUSTOM(GetEventProvider(), SpanningTreeEventListener, OnServerSplit, (server));
+ }
+ return;
+ }
+
+ // Some other module is being unloaded. If it provides an IOHook we use, we must close that server connection now.
+
+restart:
// Close all connections which use an IO hook provided by this module
const TreeServer::ChildServers& list = Utils->TreeRoot->GetChildren();
for (TreeServer::ChildServers::const_iterator i = list.begin(); i != list.end(); ++i)
{
TreeSocket* sock = (*i)->GetSocket();
- if (sock->GetIOHook() && sock->GetIOHook()->prov->creator == mod)
+ if (sock->GetModHook(mod))
{
sock->SendError("SSL module unloaded");
sock->Close();
+ // XXX: The list we're iterating is modified by TreeServer::SQuit() which is called by Close()
+ goto restart;
}
}
for (SpanningTreeUtilities::TimeoutList::const_iterator i = Utils->timeoutlist.begin(); i != Utils->timeoutlist.end(); ++i)
{
TreeSocket* sock = i->first;
- if (sock->GetIOHook() && sock->GetIOHook()->prov->creator == mod)
+ if (sock->GetModHook(mod))
sock->Close();
}
}
-// note: the protocol does not allow direct umode +o except
-// via NICK with 8 params. sending OPERTYPE infers +o modechange
-// locally.
void ModuleSpanningTree::OnOper(User* user, const std::string &opertype)
{
if (user->registered != REG_ALL || !IS_LOCAL(user))
return;
+
+ // Note: The protocol does not allow direct umode +o;
+ // sending OPERTYPE infers +o modechange locally.
CommandOpertype::Builder(user).Broadcast();
}
@@ -728,6 +715,33 @@ ModResult ModuleSpanningTree::OnSetAway(User* user, const std::string &awaymsg)
return MOD_RES_PASSTHRU;
}
+void ModuleSpanningTree::OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags, const std::string& output_mode)
+{
+ if (processflags & ModeParser::MODE_LOCALONLY)
+ return;
+
+ if (u)
+ {
+ if (u->registered != REG_ALL)
+ return;
+
+ CmdBuilder params(source, "MODE");
+ params.push(u->uuid);
+ params.push(output_mode);
+ params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
+ params.Broadcast();
+ }
+ else
+ {
+ CmdBuilder params(source, "FMODE");
+ params.push(c->name);
+ params.push_int(c->age);
+ params.push(output_mode);
+ params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
+ params.Broadcast();
+ }
+}
+
CullResult ModuleSpanningTree::cull()
{
if (Utils)
@@ -737,16 +751,12 @@ CullResult ModuleSpanningTree::cull()
ModuleSpanningTree::~ModuleSpanningTree()
{
- delete ServerInstance->PI;
- ServerInstance->PI = new ProtocolInterface;
+ ServerInstance->PI = &ServerInstance->DefaultProtocolInterface;
Server* newsrv = new Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc);
SetLocalUsersServer(newsrv);
- /* This will also free the listeners */
delete Utils;
-
- delete commands;
}
Version ModuleSpanningTree::GetVersion()
@@ -758,12 +768,13 @@ Version ModuleSpanningTree::GetVersion()
* so that any activity it sees is FINAL, e.g. we arent going to send out
* a NICK message before m_cloaking has finished putting the +x on the user,
* etc etc.
- * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
+ * Therefore, we set our priority to PRIORITY_LAST to make sure we end up at the END of
* the module call queue.
*/
void ModuleSpanningTree::Prioritize()
{
ServerInstance->Modules->SetPriority(this, PRIORITY_LAST);
+ ServerInstance->Modules.SetPriority(this, I_OnPreTopicChange, PRIORITY_FIRST);
}
MODULE_INIT(ModuleSpanningTree)
diff --git a/src/modules/m_spanningtree/main.h b/src/modules/m_spanningtree/main.h
index 513e86a2f..46c21b4e9 100644
--- a/src/modules/m_spanningtree/main.h
+++ b/src/modules/m_spanningtree/main.h
@@ -24,9 +24,11 @@
#pragma once
#include "inspircd.h"
+#include "event.h"
#include "modules/dns.h"
#include "servercommand.h"
#include "commands.h"
+#include "protocolinterface.h"
/** If you make a change which breaks the protocol, increment this.
* If you completely change the protocol, completely change the number.
@@ -42,7 +44,6 @@ const long MinCompatProtocol = 1202;
/** Forward declarations
*/
-class SpanningTreeCommands;
class SpanningTreeUtilities;
class CacheRefreshTimer;
class TreeServer;
@@ -61,7 +62,19 @@ class ModuleSpanningTree : public Module
/** Server to server only commands, not registered in the core
*/
- SpanningTreeCommands* commands;
+ SpanningTreeCommands commands;
+
+ /** Next membership id assigned when a local user joins a channel
+ */
+ Membership::Id currmembid;
+
+ /** The specialized ProtocolInterface that is assigned to ServerInstance->PI on load
+ */
+ SpanningTreeProtocolInterface protocolinterface;
+
+ /** Event provider for our events
+ */
+ Events::ModuleEventProvider eventprov;
public:
dynamic_reference<DNS::Manager> DNS;
@@ -73,10 +86,6 @@ class ModuleSpanningTree : public Module
*/
bool loopCall;
- /** True if users are quitting due to a netsplit
- */
- bool SplitInProgress;
-
/** Constructor
*/
ModuleSpanningTree();
@@ -98,10 +107,6 @@ class ModuleSpanningTree : public Module
*/
ModResult HandleRemoteWhois(const std::vector<std::string>& parameters, User* user);
- /** Ping all local servers
- */
- void DoPingChecks(time_t curtime);
-
/** Connect a server locally
*/
void ConnectServer(Link* x, Autoconnect* y = NULL);
@@ -126,14 +131,12 @@ class ModuleSpanningTree : public Module
*/
ModResult HandleConnect(const std::vector<std::string>& parameters, User* user);
- /** Attempt to send a message to a user
- */
- void RemoteMessage(User* user, const char* format, ...) CUSTOM_PRINTF(3, 4);
-
/** Display a time as a human readable string
*/
static std::string TimeToStr(time_t secs);
+ const Events::ModuleEventProvider& GetEventProvider() const { return eventprov; }
+
/**
** *** MODULE EVENTS ***
**/
@@ -141,7 +144,8 @@ class ModuleSpanningTree : public Module
ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE;
void OnPostCommand(Command*, const std::vector<std::string>& parameters, LocalUser* user, CmdResult result, const std::string& original_line) CXX11_OVERRIDE;
void OnUserConnect(LocalUser* source) CXX11_OVERRIDE;
- void OnUserInvite(User* source,User* dest,Channel* channel, time_t) CXX11_OVERRIDE;
+ void OnUserInvite(User* source, User* dest, Channel* channel, time_t timeout, unsigned int notifyrank, CUList& notifyexcepts) CXX11_OVERRIDE;
+ ModResult OnPreTopicChange(User* user, Channel* chan, const std::string& topic) CXX11_OVERRIDE;
void OnPostTopicChange(User* user, Channel* chan, const std::string &topic) CXX11_OVERRIDE;
void OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE;
void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE;
@@ -156,15 +160,14 @@ class ModuleSpanningTree : public Module
void OnPreRehash(User* user, const std::string &parameter) CXX11_OVERRIDE;
void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
void OnOper(User* user, const std::string &opertype) CXX11_OVERRIDE;
- void OnLine(User* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason);
void OnAddLine(User *u, XLine *x) CXX11_OVERRIDE;
void OnDelLine(User *u, XLine *x) CXX11_OVERRIDE;
- ModResult OnStats(char statschar, User* user, string_list &results) CXX11_OVERRIDE;
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE;
ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE;
void OnLoadModule(Module* mod) CXX11_OVERRIDE;
void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
ModResult OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
- void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE;
+ void OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags, const std::string& output_mode) CXX11_OVERRIDE;
CullResult cull();
~ModuleSpanningTree();
Version GetVersion() CXX11_OVERRIDE;
diff --git a/src/modules/m_spanningtree/metadata.cpp b/src/modules/m_spanningtree/metadata.cpp
index 13ccabc35..47c2f8bc5 100644
--- a/src/modules/m_spanningtree/metadata.cpp
+++ b/src/modules/m_spanningtree/metadata.cpp
@@ -49,19 +49,19 @@ CmdResult CommandMetadata::Handle(User* srcuser, std::vector<std::string>& param
std::string value = params.size() < 4 ? "" : params[3];
ExtensionItem* item = ServerInstance->Extensions.GetItem(params[2]);
- if (item)
+ if ((item) && (item->type == ExtensionItem::EXT_CHANNEL))
item->unserialize(FORMAT_NETWORK, c, value);
FOREACH_MOD(OnDecodeMetaData, (c,params[2],value));
}
else
{
User* u = ServerInstance->FindUUID(params[0]);
- if ((u) && (!IS_SERVER(u)))
+ if (u)
{
ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]);
std::string value = params.size() < 3 ? "" : params[2];
- if (item)
+ if ((item) && (item->type == ExtensionItem::EXT_USER))
item->unserialize(FORMAT_NETWORK, u, value);
FOREACH_MOD(OnDecodeMetaData, (u,params[1],value));
}
diff --git a/src/modules/m_spanningtree/misccommands.cpp b/src/modules/m_spanningtree/misccommands.cpp
index 5b04c73bc..00f31d668 100644
--- a/src/modules/m_spanningtree/misccommands.cpp
+++ b/src/modules/m_spanningtree/misccommands.cpp
@@ -35,12 +35,6 @@ CmdResult CommandSNONotice::Handle(User* user, std::vector<std::string>& params)
return CMD_SUCCESS;
}
-CmdResult CommandBurst::HandleServer(TreeServer* server, std::vector<std::string>& params)
-{
- server->bursting = true;
- return CMD_SUCCESS;
-}
-
CmdResult CommandEndBurst::HandleServer(TreeServer* server, std::vector<std::string>& params)
{
server->FinishBurst();
diff --git a/src/modules/m_spanningtree/netburst.cpp b/src/modules/m_spanningtree/netburst.cpp
index a33cf8a13..cdafa9ded 100644
--- a/src/modules/m_spanningtree/netburst.cpp
+++ b/src/modules/m_spanningtree/netburst.cpp
@@ -27,7 +27,6 @@
#include "treeserver.h"
#include "main.h"
#include "commands.h"
-#include "protocolinterface.h"
/**
* Creates FMODE messages, used only when syncing channels
@@ -105,27 +104,38 @@ void TreeSocket::DoBurst(TreeServer* s)
{
ServerInstance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s%s).",
s->GetName().c_str(),
- capab->auth_fingerprint ? "SSL Fingerprint and " : "",
+ capab->auth_fingerprint ? "SSL certificate fingerprint and " : "",
capab->auth_challenge ? "challenge-response" : "plaintext password");
this->CleanNegotiationInfo();
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " BURST " + ConvToStr(ServerInstance->Time()));
- /* send our version string */
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " VERSION :"+ServerInstance->GetVersionString());
- /* Send server tree */
+ this->WriteLine(CmdBuilder("BURST").push_int(ServerInstance->Time()));
+ // Introduce all servers behind us
this->SendServers(Utils->TreeRoot, s);
BurstState bs(this);
- /* Send users and their oper status */
+ // Introduce all users
this->SendUsers(bs);
+ // Sync all channels
const chan_hash& chans = ServerInstance->GetChans();
for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
SyncChannel(i->second, bs);
+ // Send all xlines
this->SendXLines();
FOREACH_MOD(OnSyncNetwork, (bs.server));
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " ENDBURST");
+ this->WriteLine(CmdBuilder("ENDBURST"));
ServerInstance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+ s->GetName()+"\2.");
+
+ this->burstsent = true;
+}
+
+void TreeSocket::SendServerInfo(TreeServer* from)
+{
+ // Send public version string
+ this->WriteLine(CommandSInfo::Builder(from, "version", from->GetVersion()));
+
+ // Send full version string that contains more information and is shown to opers
+ this->WriteLine(CommandSInfo::Builder(from, "fullversion", from->GetFullVersion()));
}
/** Recursively send the server tree.
@@ -133,10 +143,11 @@ void TreeSocket::DoBurst(TreeServer* s)
* (and any of ITS servers too) of what servers we know about.
* If at any point any of these servers already exist on the other
* end, our connection may be terminated.
- * The hopcount parameter (3rd) is deprecated, and is always 0.
*/
void TreeSocket::SendServers(TreeServer* Current, TreeServer* s)
{
+ SendServerInfo(Current);
+
const TreeServer::ChildServers& children = Current->GetChildren();
for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
{
@@ -144,7 +155,6 @@ void TreeSocket::SendServers(TreeServer* Current, TreeServer* s)
if (recursive_server != s)
{
this->WriteLine(CommandServer::Builder(recursive_server));
- this->WriteLine(":" + recursive_server->GetID() + " VERSION :" + recursive_server->GetVersion());
/* down to next level */
this->SendServers(recursive_server, s);
}
@@ -152,32 +162,25 @@ void TreeSocket::SendServers(TreeServer* Current, TreeServer* s)
}
/** Send one or more FJOINs for a channel of users.
- * If the length of a single line is more than 480-NICKMAX
- * in length, it is split over multiple lines.
- * Send one or more FMODEs for a channel with the
- * channel bans, if there's any.
+ * If the length of a single line is too long, it is split over multiple lines.
*/
void TreeSocket::SendFJoins(Channel* c)
{
- std::string line(":");
- line.append(ServerInstance->Config->GetSID()).append(" FJOIN ").append(c->name).append(1, ' ').append(ConvToStr(c->age)).append(" +");
- std::string::size_type erase_from = line.length();
- line.append(c->ChanModes(true)).append(" :");
-
- const UserMembList *ulist = c->GetUsers();
+ CommandFJoin::Builder fjoin(c);
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); ++i)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
- const std::string& modestr = i->second->modes;
- if ((line.length() + modestr.length() + UIDGenerator::UUID_LENGTH + 2) > 480)
+ Membership* memb = i->second;
+ if (!fjoin.has_room(memb))
{
- this->WriteLine(line);
- line.erase(erase_from);
- line.append(" :");
+ // No room for this user, send the line and prepare a new one
+ this->WriteLine(fjoin.finalize());
+ fjoin.clear();
}
- line.append(modestr).append(1, ',').append(i->first->uuid).push_back(' ');
+ fjoin.add(memb);
}
- this->WriteLine(line);
+ this->WriteLine(fjoin.finalize());
}
/** Send all XLines we know about */
@@ -238,7 +241,7 @@ void TreeSocket::SendListModes(Channel* chan)
this->WriteLine(fmode.finalize());
}
-/** Send channel topic, modes and metadata */
+/** Send channel users, topic, modes and global metadata */
void TreeSocket::SyncChannel(Channel* chan, BurstState& bs)
{
SendFJoins(chan);
@@ -267,7 +270,7 @@ void TreeSocket::SyncChannel(Channel* chan)
SyncChannel(chan, bs);
}
-/** send all users and their oper state/modes */
+/** Send all users and their state, including oper and away status and global metadata */
void TreeSocket::SendUsers(BurstState& bs)
{
ProtocolInterface::Server& piserver = bs.server;
diff --git a/src/modules/m_spanningtree/nick.cpp b/src/modules/m_spanningtree/nick.cpp
index 733901632..9e290e07f 100644
--- a/src/modules/m_spanningtree/nick.cpp
+++ b/src/modules/m_spanningtree/nick.cpp
@@ -30,33 +30,35 @@
#include "commands.h"
#include "treeserver.h"
-CmdResult CommandNick::HandleRemote(RemoteUser* user, std::vector<std::string>& params)
+CmdResult CommandNick::HandleRemote(::RemoteUser* user, std::vector<std::string>& params)
{
if ((isdigit(params[0][0])) && (params[0] != user->uuid))
throw ProtocolException("Attempted to change nick to an invalid or non-matching UUID");
- /* Update timestamp on user when they change nicks */
- const time_t newts = ConvToInt(params[1]);
+ // Timestamp of the new nick
+ time_t newts = ServerCommand::ExtractTS(params[1]);
/*
* On nick messages, check that the nick doesn't already exist here.
* If it does, perform collision logic.
*/
User* x = ServerInstance->FindNickOnly(params[0]);
- if ((x) && (x != user))
+ if ((x) && (x != user) && (x->registered == REG_ALL))
{
- /* x is local, who is remote */
- int collideret = Utils->DoCollision(x, TreeServer::Get(user), newts, user->ident, user->GetIPString(), user->uuid);
- if (collideret != 1)
+ // 'x' is the already existing user using the same nick as params[0]
+ // 'user' is the user trying to change nick to the in use nick
+ bool they_change = Utils->DoCollision(x, TreeServer::Get(user), newts, user->ident, user->GetIPString(), user->uuid, "NICK");
+ if (they_change)
{
- /*
- * Remote client lost, or both lost, parsing or passing on this
- * nickchange would be pointless, as the incoming client's server will
- * soon receive SAVE to change its nick to its UID. :) -- w00t
- */
- return CMD_FAILURE;
+ // Remote client lost, or both lost, rewrite this nick change as a change to uuid before
+ // calling ChangeNick() and forwarding the message
+ params[0] = user->uuid;
+ params[1] = ConvToStr(CommandSave::SavedTimestamp);
+ newts = CommandSave::SavedTimestamp;
}
}
- user->ForceNickChange(params[0], newts);
+
+ user->ChangeNick(params[0], newts);
+
return CMD_SUCCESS;
}
diff --git a/src/modules/m_spanningtree/nickcollide.cpp b/src/modules/m_spanningtree/nickcollide.cpp
index 62e43a0b1..62e200921 100644
--- a/src/modules/m_spanningtree/nickcollide.cpp
+++ b/src/modules/m_spanningtree/nickcollide.cpp
@@ -24,15 +24,20 @@
#include "treeserver.h"
#include "utils.h"
#include "commandbuilder.h"
+#include "commands.h"
/*
* Yes, this function looks a little ugly.
* However, in some circumstances we may not have a User, so we need to do things this way.
- * Returns 1 if colliding local client, 2 if colliding remote, 3 if colliding both.
- * Sends SAVEs as appropriate and forces nickchanges too.
+ * Returns true if remote or both lost, false otherwise.
+ * Sends SAVEs as appropriate and forces nick change of the user 'u' if our side loses or if both lose.
+ * Does not change the nick of the user that is trying to claim the nick of 'u', i.e. the "remote" user.
*/
-int SpanningTreeUtilities::DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid)
+bool SpanningTreeUtilities::DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid, const char* collidecmd)
{
+ // At this point we're sure that a collision happened, increment the counter regardless of who wins
+ ServerInstance->stats.Collisions++;
+
/*
* Under old protocol rules, we would have had to kill both clients.
* Really, this sucks.
@@ -53,21 +58,14 @@ int SpanningTreeUtilities::DoCollision(User* u, TreeServer* server, time_t remot
bool bChangeLocal = true;
bool bChangeRemote = true;
- /* for brevity, don't use the User - use defines to avoid any copy */
- #define localts u->age
- #define localident u->ident
- #define localip u->GetIPString()
-
- /* mmk. let's do this again. */
- if (remotets == localts)
- {
- /* equal. fuck them both! do nada, let the handler at the bottom figure this out. */
- }
- else
+ // If the timestamps are not equal only one of the users has to change nick,
+ // otherwise both have to change
+ const time_t localts = u->age;
+ if (remotets != localts)
{
- /* fuck. now it gets complex. */
-
/* first, let's see if ident@host matches. */
+ const std::string& localident = u->ident;
+ const std::string& localip = u->GetIPString();
bool SamePerson = (localident == remoteident)
&& (localip == remoteip);
@@ -78,19 +76,22 @@ int SpanningTreeUtilities::DoCollision(User* u, TreeServer* server, time_t remot
if((SamePerson && remotets < localts) ||
(!SamePerson && remotets > localts))
{
- /* remote needs to change */
+ // Only remote needs to change
bChangeLocal = false;
}
else
{
- /* ours needs to change */
+ // Only ours needs to change
bChangeRemote = false;
}
}
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Nick collision on \"%s\" caused by %s: %s/%lu/%s@%s %d <-> %s/%lu/%s@%s %d", u->nick.c_str(), collidecmd,
+ u->uuid.c_str(), (unsigned long)localts, u->ident.c_str(), u->GetIPString().c_str(), bChangeLocal,
+ remoteuid.c_str(), (unsigned long)remotets, remoteident.c_str(), remoteip.c_str(), bChangeRemote);
+
/*
- * Cheat a little here. Instead of a dedicated command to change UID,
- * use SAVE and accept the losing client with its UID (as we know the SAVE will
+ * Send SAVE and accept the losing client with its UID (as we know the SAVE will
* not fail under any circumstances -- UIDs are netwide exclusive).
*
* This means that each side of a collide will generate one extra NICK back to where
@@ -104,38 +105,23 @@ int SpanningTreeUtilities::DoCollision(User* u, TreeServer* server, time_t remot
{
/*
* Local-side nick needs to change. Just in case we are hub, and
- * this "local" nick is actually behind us, send an SAVE out.
+ * this "local" nick is actually behind us, send a SAVE out.
*/
CmdBuilder params("SAVE");
params.push_back(u->uuid);
params.push_back(ConvToStr(u->age));
params.Broadcast();
- u->ForceNickChange(u->uuid);
-
- if (!bChangeRemote)
- return 1;
+ u->ChangeNick(u->uuid, CommandSave::SavedTimestamp);
}
if (bChangeRemote)
{
- User *remote = ServerInstance->FindUUID(remoteuid);
/*
- * remote side needs to change. If this happens, we will modify
- * the UID or halt the propagation of the nick change command,
- * so other servers don't need to see the SAVE
+ * Remote side needs to change. If this happens, we modify the UID or NICK and
+ * send back a SAVE to the source.
*/
- TreeSocket* sock = server->GetSocket();
- sock->WriteLine(":"+ServerInstance->Config->GetSID()+" SAVE "+remoteuid+" "+ ConvToStr(remotets));
-
- if (remote)
- {
- /* nick change collide. Force change their nick. */
- remote->ForceNickChange(remoteuid);
- }
-
- if (!bChangeLocal)
- return 2;
+ CmdBuilder("SAVE").push(remoteuid).push_int(remotets).Unicast(server->ServerUser);
}
- return 3;
+ return bChangeRemote;
}
diff --git a/src/modules/m_spanningtree/num.cpp b/src/modules/m_spanningtree/num.cpp
new file mode 100644
index 000000000..2c8697c9a
--- /dev/null
+++ b/src/modules/m_spanningtree/num.cpp
@@ -0,0 +1,62 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "utils.h"
+#include "commands.h"
+#include "remoteuser.h"
+
+CmdResult CommandNum::HandleServer(TreeServer* server, std::vector<std::string>& params)
+{
+ User* const target = ServerInstance->FindUUID(params[1]);
+ if (!target)
+ return CMD_FAILURE;
+
+ LocalUser* const localtarget = IS_LOCAL(target);
+ if (!localtarget)
+ return CMD_SUCCESS;
+
+ Numeric::Numeric numeric(ConvToInt(params[2]));
+ // Passing NULL is ok, in that case the numeric source becomes this server
+ numeric.SetServer(Utils->FindServerID(params[0]));
+ numeric.GetParams().insert(numeric.GetParams().end(), params.begin()+3, params.end());
+
+ localtarget->WriteNumeric(numeric);
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandNum::GetRouting(User* user, const std::vector<std::string>& params)
+{
+ return ROUTE_UNICAST(params[1]);
+}
+
+CommandNum::Builder::Builder(SpanningTree::RemoteUser* target, const Numeric::Numeric& numeric)
+ : CmdBuilder("NUM")
+{
+ TreeServer* const server = (numeric.GetServer() ? (static_cast<TreeServer*>(numeric.GetServer())) : Utils->TreeRoot);
+ push(server->GetID()).push(target->uuid).push(InspIRCd::Format("%03u", numeric.GetNumeric()));
+ const std::vector<std::string>& params = numeric.GetParams();
+ if (!params.empty())
+ {
+ for (std::vector<std::string>::const_iterator i = params.begin(); i != params.end()-1; ++i)
+ push(*i);
+ push_last(params.back());
+ }
+}
diff --git a/src/modules/m_spanningtree/opertype.cpp b/src/modules/m_spanningtree/opertype.cpp
index 1a9e36f72..ab531c171 100644
--- a/src/modules/m_spanningtree/opertype.cpp
+++ b/src/modules/m_spanningtree/opertype.cpp
@@ -35,7 +35,7 @@ CmdResult CommandOpertype::HandleRemote(RemoteUser* u, std::vector<std::string>&
ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
u->SetMode(opermh, true);
- OperIndex::iterator iter = ServerInstance->Config->OperTypes.find(opertype);
+ ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->OperTypes.find(opertype);
if (iter != ServerInstance->Config->OperTypes.end())
u->oper = iter->second;
else
@@ -51,7 +51,7 @@ CmdResult CommandOpertype::HandleRemote(RemoteUser* u, std::vector<std::string>&
* then do nothing. -- w00t
*/
TreeServer* remoteserver = TreeServer::Get(u);
- if (remoteserver->bursting || remoteserver->IsSilentULine())
+ if (remoteserver->IsBehindBursting() || remoteserver->IsSilentULine())
return CMD_SUCCESS;
}
diff --git a/src/modules/m_spanningtree/override_map.cpp b/src/modules/m_spanningtree/override_map.cpp
index 68551e84f..a22fa48ac 100644
--- a/src/modules/m_spanningtree/override_map.cpp
+++ b/src/modules/m_spanningtree/override_map.cpp
@@ -82,9 +82,7 @@ static std::vector<std::string> GetMap(User* user, TreeServer* current, unsigned
// Pad with spaces until its at max len, max_len must always be >= my names length
buffer.append(max_len - current->GetName().length(), ' ');
- char buf[16];
- snprintf(buf, sizeof(buf), "%5d [%5.2f%%]", current->UserCount, percent);
- buffer += buf;
+ buffer += InspIRCd::Format("%5d [%5.2f%%]", current->UserCount, percent);
if (user->IsOper())
{
@@ -168,11 +166,11 @@ CmdResult CommandMap::Handle(const std::vector<std::string>& parameters, User* u
{
if (parameters.size() > 0)
{
- /* Remote MAP, the server is within the 1st parameter */
+ // Remote MAP, the target server is the 1st parameter
TreeServer* s = Utils->FindServerMask(parameters[0]);
if (!s)
{
- user->WriteNumeric(ERR_NOSUCHSERVER, "%s :No such server", parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHSERVER, parameters[0], "No such server");
return CMD_FAILURE;
}
@@ -199,17 +197,14 @@ CmdResult CommandMap::Handle(const std::vector<std::string>& parameters, User* u
std::vector<std::string> map = GetMap(user, Utils->TreeRoot, max, 0);
for (std::vector<std::string>::const_iterator i = map.begin(); i != map.end(); ++i)
- user->SendText(":%s %03d %s :%s", ServerInstance->Config->ServerName.c_str(),
- RPL_MAP, user->nick.c_str(), i->c_str());
+ user->WriteRemoteNumeric(RPL_MAP, *i);
size_t totusers = ServerInstance->Users->GetUsers().size();
float avg_users = (float) totusers / Utils->serverlist.size();
- user->SendText(":%s %03d %s :%u server%s and %u user%s, average %.2f users per server",
- ServerInstance->Config->ServerName.c_str(), RPL_MAPUSERS, user->nick.c_str(),
- (unsigned int)Utils->serverlist.size(), (Utils->serverlist.size() > 1 ? "s" : ""), (unsigned int)totusers, (totusers > 1 ? "s" : ""), avg_users);
- user->SendText(":%s %03d %s :End of /MAP", ServerInstance->Config->ServerName.c_str(),
- RPL_ENDMAP, user->nick.c_str());
+ user->WriteRemoteNumeric(RPL_MAPUSERS, InspIRCd::Format("%u server%s and %u user%s, average %.2f users per server",
+ (unsigned int)Utils->serverlist.size(), (Utils->serverlist.size() > 1 ? "s" : ""), (unsigned int)totusers, (totusers > 1 ? "s" : ""), avg_users));
+ user->WriteRemoteNumeric(RPL_ENDMAP, "End of /MAP");
return CMD_SUCCESS;
}
diff --git a/src/modules/m_spanningtree/override_squit.cpp b/src/modules/m_spanningtree/override_squit.cpp
index 84cb01f50..9cec527d3 100644
--- a/src/modules/m_spanningtree/override_squit.cpp
+++ b/src/modules/m_spanningtree/override_squit.cpp
@@ -36,13 +36,10 @@ ModResult ModuleSpanningTree::HandleSquit(const std::vector<std::string>& parame
return MOD_RES_DENY;
}
- TreeSocket* sock = s->GetSocket();
-
if (s->IsLocal())
{
ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0].c_str(),user->nick.c_str());
- sock->Squit(s,"Server quit by " + user->GetFullRealHost());
- sock->Close();
+ s->SQuit("Server quit by " + user->GetFullRealHost());
}
else
{
diff --git a/src/modules/m_spanningtree/override_stats.cpp b/src/modules/m_spanningtree/override_stats.cpp
index 14b3f5ef7..9b73837cb 100644
--- a/src/modules/m_spanningtree/override_stats.cpp
+++ b/src/modules/m_spanningtree/override_stats.cpp
@@ -24,27 +24,34 @@
#include "utils.h"
#include "link.h"
-ModResult ModuleSpanningTree::OnStats(char statschar, User* user, string_list &results)
+ModResult ModuleSpanningTree::OnStats(Stats::Context& stats)
{
- if ((statschar == 'c') || (statschar == 'n'))
+ if ((stats.GetSymbol() == 'c') || (stats.GetSymbol() == 'n'))
{
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i != Utils->LinkBlocks.end(); ++i)
{
Link* L = *i;
- results.push_back("213 "+user->nick+" "+statschar+" *@"+(L->HiddenFromStats ? "<hidden>" : L->IPAddr)+" * "+(*i)->Name.c_str()+" "+ConvToStr(L->Port)+" "+(L->Hook.empty() ? "plaintext" : L->Hook));
- if (statschar == 'c')
- results.push_back("244 "+user->nick+" H * * "+L->Name.c_str());
+ std::string ipaddr = "*@";
+ if (L->HiddenFromStats)
+ ipaddr.append("<hidden>");
+ else
+ ipaddr.append(L->IPAddr);
+
+ const std::string hook = (L->Hook.empty() ? "plaintext" : L->Hook);
+ stats.AddRow(213, stats.GetSymbol(), ipaddr, '*', L->Name, L->Port, hook);
+ if (stats.GetSymbol() == 'c')
+ stats.AddRow(244, 'H', '*', '*', L->Name);
}
return MOD_RES_DENY;
}
- else if (statschar == 'U')
+ else if (stats.GetSymbol() == 'U')
{
ConfigTagList tags = ServerInstance->Config->ConfTags("uline");
for (ConfigIter i = tags.first; i != tags.second; ++i)
{
std::string name = i->second->getString("server");
if (!name.empty())
- results.push_back("248 "+user->nick+" U "+name);
+ stats.AddRow(248, 'U', name);
}
return MOD_RES_DENY;
}
diff --git a/src/modules/m_spanningtree/override_whois.cpp b/src/modules/m_spanningtree/override_whois.cpp
index d7030e30a..7f7189854 100644
--- a/src/modules/m_spanningtree/override_whois.cpp
+++ b/src/modules/m_spanningtree/override_whois.cpp
@@ -23,20 +23,17 @@
ModResult ModuleSpanningTree::HandleRemoteWhois(const std::vector<std::string>& parameters, User* user)
{
- if ((IS_LOCAL(user)) && (parameters.size() > 1))
+ User* remote = ServerInstance->FindNickOnly(parameters[1]);
+ if (remote && !IS_LOCAL(remote))
{
- User* remote = ServerInstance->FindNickOnly(parameters[1]);
- if (remote && !IS_LOCAL(remote))
- {
- CmdBuilder(user, "IDLE").push(remote->uuid).Unicast(remote);
- return MOD_RES_DENY;
- }
- else if (!remote)
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[1].c_str());
- user->WriteNumeric(RPL_ENDOFWHOIS, "%s :End of /WHOIS list.", parameters[1].c_str());
- return MOD_RES_DENY;
- }
+ CmdBuilder(user, "IDLE").push(remote->uuid).Unicast(remote);
+ return MOD_RES_DENY;
+ }
+ else if (!remote)
+ {
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+ user->WriteNumeric(RPL_ENDOFWHOIS, parameters[0], "End of /WHOIS list.");
+ return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
diff --git a/src/modules/m_spanningtree/pingtimer.cpp b/src/modules/m_spanningtree/pingtimer.cpp
new file mode 100644
index 000000000..1c96259bf
--- /dev/null
+++ b/src/modules/m_spanningtree/pingtimer.cpp
@@ -0,0 +1,102 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "pingtimer.h"
+#include "treeserver.h"
+#include "commandbuilder.h"
+
+PingTimer::PingTimer(TreeServer* ts)
+ : Timer(Utils->PingFreq)
+ , server(ts)
+ , state(PS_SENDPING)
+{
+}
+
+PingTimer::State PingTimer::TickInternal()
+{
+ // Timer expired, take next action based on what happened last time
+ if (state == PS_SENDPING)
+ {
+ // Last ping was answered, send next ping
+ server->GetSocket()->WriteLine(CmdBuilder("PING").push(server->GetID()));
+ LastPingMsec = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ // Warn next unless warnings are disabled. If they are, jump straight to timeout.
+ if (Utils->PingWarnTime)
+ return PS_WARN;
+ else
+ return PS_TIMEOUT;
+ }
+ else if (state == PS_WARN)
+ {
+ // No pong arrived in PingWarnTime seconds, send a warning to opers
+ ServerInstance->SNO->WriteToSnoMask('l', "Server \002%s\002 has not responded to PING for %d seconds, high latency.", server->GetName().c_str(), GetInterval());
+ return PS_TIMEOUT;
+ }
+ else // PS_TIMEOUT
+ {
+ // They didn't answer the last ping, if they are locally connected, get rid of them
+ if (server->IsLocal())
+ {
+ TreeSocket* sock = server->GetSocket();
+ sock->SendError("Ping timeout");
+ sock->Close();
+ }
+
+ // If the server is non-locally connected, don't do anything until we get a PONG.
+ // This is to avoid pinging the server and warning opers more than once.
+ // If they do answer eventually, we will move to the PS_SENDPING state and ping them again.
+ return PS_IDLE;
+ }
+}
+
+void PingTimer::SetState(State newstate)
+{
+ state = newstate;
+
+ // Set when should the next Tick() happen based on the state
+ if (state == PS_SENDPING)
+ SetInterval(Utils->PingFreq);
+ else if (state == PS_WARN)
+ SetInterval(Utils->PingWarnTime);
+ else if (state == PS_TIMEOUT)
+ SetInterval(Utils->PingFreq - Utils->PingWarnTime);
+
+ // If state == PS_IDLE, do not set the timer, see above why
+}
+
+bool PingTimer::Tick(time_t currtime)
+{
+ if (server->IsDead())
+ return false;
+
+ SetState(TickInternal());
+ return false;
+}
+
+void PingTimer::OnPong()
+{
+ // Calculate RTT
+ long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ server->rtt = ts - LastPingMsec;
+
+ // Change state to send ping next, also reschedules the timer appropriately
+ SetState(PS_SENDPING);
+}
diff --git a/src/modules/m_spanningtree/pingtimer.h b/src/modules/m_spanningtree/pingtimer.h
new file mode 100644
index 000000000..753558689
--- /dev/null
+++ b/src/modules/m_spanningtree/pingtimer.h
@@ -0,0 +1,77 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+class TreeServer;
+
+/** Handles PINGing servers and killing them on timeout
+ */
+class PingTimer : public Timer
+{
+ enum State
+ {
+ /** Send PING next */
+ PS_SENDPING,
+ /** Warn opers next */
+ PS_WARN,
+ /** Kill the server next due to ping timeout */
+ PS_TIMEOUT,
+ /** Do nothing */
+ PS_IDLE
+ };
+
+ /** Server the timer is interacting with
+ */
+ TreeServer* const server;
+
+ /** What to do when the timer ticks next
+ */
+ State state;
+
+ /** Last ping time in milliseconds, used to calculate round trip time
+ */
+ unsigned long LastPingMsec;
+
+ /** Update internal state and reschedule timer according to the new state
+ * @param newstate State to change to
+ */
+ void SetState(State newstate);
+
+ /** Process timer tick event
+ * @return State to change to
+ */
+ State TickInternal();
+
+ /** Called by the TimerManager when the timer expires
+ * @param currtime Time now
+ * @return Always false, we reschedule ourselves instead
+ */
+ bool Tick(time_t currtime) CXX11_OVERRIDE;
+
+ public:
+ /** Construct the timer. This doesn't schedule the timer.
+ * @param server TreeServer to interact with
+ */
+ PingTimer(TreeServer* server);
+
+ /** Register a PONG from the server
+ */
+ void OnPong();
+};
diff --git a/src/modules/m_spanningtree/pong.cpp b/src/modules/m_spanningtree/pong.cpp
index 6a29163e4..5d97f2af2 100644
--- a/src/modules/m_spanningtree/pong.cpp
+++ b/src/modules/m_spanningtree/pong.cpp
@@ -26,7 +26,7 @@
CmdResult CommandPong::HandleServer(TreeServer* server, std::vector<std::string>& params)
{
- if (server->bursting)
+ if (server->IsBursting())
{
ServerInstance->SNO->WriteGlobalSno('l', "Server \002%s\002 has not finished burst, forcing end of burst (send ENDBURST!)", server->GetName().c_str());
server->FinishBurst();
@@ -35,9 +35,7 @@ CmdResult CommandPong::HandleServer(TreeServer* server, std::vector<std::string>
if (params[0] == ServerInstance->Config->GetSID())
{
// PONG for us
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
- server->rtt = ts - server->LastPingMsec;
- server->SetPingFlag();
+ server->OnPong();
}
return CMD_SUCCESS;
}
diff --git a/src/modules/m_spanningtree/postcommand.cpp b/src/modules/m_spanningtree/postcommand.cpp
index 0695ce632..64ca72977 100644
--- a/src/modules/m_spanningtree/postcommand.cpp
+++ b/src/modules/m_spanningtree/postcommand.cpp
@@ -51,10 +51,12 @@ void SpanningTreeUtilities::RouteCommand(TreeServer* origin, CommandBase* thiscm
sdest = static_cast<TreeServer*>(routing.server);
if (!sdest)
{
- sdest = FindServer(routing.serverdest);
+ // Assume the command handler already validated routing.serverdest and have only returned success if the target is something that the
+ // user executing the command is allowed to look up e.g. target is not an uuid if user is local.
+ sdest = FindRouteTarget(routing.serverdest);
if (!sdest)
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Trying to route %s%s to nonexistant server %s", (encap ? "ENCAP " : ""), command.c_str(), routing.serverdest.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Trying to route %s%s to nonexistent server %s", (encap ? "ENCAP " : ""), command.c_str(), routing.serverdest.c_str());
return;
}
}
@@ -89,7 +91,7 @@ void SpanningTreeUtilities::RouteCommand(TreeServer* origin, CommandBase* thiscm
if (ServerInstance->Modes->FindPrefix(dest[0]))
{
pfx = dest[0];
- dest = dest.substr(1);
+ dest.erase(dest.begin());
}
if (dest[0] == '#')
{
diff --git a/src/modules/m_spanningtree/protocolinterface.cpp b/src/modules/m_spanningtree/protocolinterface.cpp
index 192f7cff2..be95845a7 100644
--- a/src/modules/m_spanningtree/protocolinterface.cpp
+++ b/src/modules/m_spanningtree/protocolinterface.cpp
@@ -102,43 +102,11 @@ void SpanningTreeProtocolInterface::Server::SendMetaData(const std::string& key,
sock->WriteLine(CommandMetadata::Builder(key, data));
}
-void SpanningTreeProtocolInterface::SendTopic(Channel* channel, std::string &topic)
-{
- CommandFTopic::Builder(ServerInstance->FakeClient, channel).Broadcast();
-}
-
-void SpanningTreeProtocolInterface::SendMode(User* source, User* u, Channel* c, const std::vector<std::string>& modedata, const std::vector<TranslateType>& translate)
-{
- if (u)
- {
- if (u->registered != REG_ALL)
- return;
-
- CmdBuilder params(source, "MODE");
- params.push_back(u->uuid);
- params.insert(modedata);
- params.Broadcast();
- }
- else
- {
- CmdBuilder params(source, "FMODE");
- params.push_back(c->name);
- params.push_back(ConvToStr(c->age));
- params.push_back(CommandParser::TranslateUIDs(translate, modedata));
- params.Broadcast();
- }
-}
-
void SpanningTreeProtocolInterface::SendSNONotice(char snomask, const std::string &text)
{
CmdBuilder("SNONOTICE").push(snomask).push_last(text).Broadcast();
}
-void SpanningTreeProtocolInterface::PushToClient(User* target, const std::string &rawline)
-{
- CmdBuilder("PUSH").push(target->uuid).push_last(rawline).Unicast(target);
-}
-
void SpanningTreeProtocolInterface::SendMessage(Channel* target, char status, const std::string& text, MessageType msgtype)
{
const char* cmd = (msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
diff --git a/src/modules/m_spanningtree/protocolinterface.h b/src/modules/m_spanningtree/protocolinterface.h
index 97648f4b4..e7fed5475 100644
--- a/src/modules/m_spanningtree/protocolinterface.h
+++ b/src/modules/m_spanningtree/protocolinterface.h
@@ -36,10 +36,7 @@ class SpanningTreeProtocolInterface : public ProtocolInterface
void SendMetaData(User* user, const std::string& key, const std::string& data) CXX11_OVERRIDE;
void SendMetaData(Channel* chan, const std::string& key, const std::string& data) CXX11_OVERRIDE;
void SendMetaData(const std::string& key, const std::string& data) CXX11_OVERRIDE;
- void SendTopic(Channel* channel, std::string &topic);
- void SendMode(User* source, User* usertarget, Channel* chantarget, const parameterlist& modedata, const std::vector<TranslateType>& types);
void SendSNONotice(char snomask, const std::string& text) CXX11_OVERRIDE;
- void PushToClient(User* target, const std::string &rawline);
void SendMessage(Channel* target, char status, const std::string& text, MessageType msgtype);
void SendMessage(User* target, const std::string& text, MessageType msgtype);
void GetServerList(ServerList& sl);
diff --git a/src/modules/m_spanningtree/rconnect.cpp b/src/modules/m_spanningtree/rconnect.cpp
index c5d3a5b52..8b8757a07 100644
--- a/src/modules/m_spanningtree/rconnect.cpp
+++ b/src/modules/m_spanningtree/rconnect.cpp
@@ -36,7 +36,7 @@ CmdResult CommandRConnect::Handle (const std::vector<std::string>& parameters, U
/* First see if the server which is being asked to connect to another server in fact exists */
if (!Utils->FindServerMask(parameters[0]))
{
- ((ModuleSpanningTree*)(Module*)creator)->RemoteMessage(user, "*** RCONNECT: Server \002%s\002 isn't connected to the network!", parameters[0].c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** RCONNECT: Server \002%s\002 isn't connected to the network!", parameters[0].c_str()));
return CMD_FAILURE;
}
diff --git a/src/modules/m_spanningtree/version.cpp b/src/modules/m_spanningtree/remoteuser.cpp
index 193b51083..717a6fd9f 100644
--- a/src/modules/m_spanningtree/version.cpp
+++ b/src/modules/m_spanningtree/remoteuser.cpp
@@ -1,7 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -20,12 +20,14 @@
#include "inspircd.h"
#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "commands.h"
+#include "remoteuser.h"
-CmdResult CommandVersion::HandleServer(TreeServer* server, std::vector<std::string>& params)
+SpanningTree::RemoteUser::RemoteUser(const std::string& uid, Server* srv)
+ : ::RemoteUser(uid, srv)
{
- server->SetVersion(params[0]);
- return CMD_SUCCESS;
+}
+
+void SpanningTree::RemoteUser::WriteRemoteNumeric(const Numeric::Numeric& numeric)
+{
+ CommandNum::Builder(this, numeric).Unicast(this);
}
diff --git a/src/modules/m_spanningtree/push.cpp b/src/modules/m_spanningtree/remoteuser.h
index b29b780c8..416f2f760 100644
--- a/src/modules/m_spanningtree/push.cpp
+++ b/src/modules/m_spanningtree/remoteuser.h
@@ -1,7 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -17,19 +17,16 @@
*/
-#include "inspircd.h"
+#pragma once
-#include "utils.h"
-#include "commands.h"
-
-CmdResult CommandPush::Handle(User* user, std::vector<std::string>& params)
+namespace SpanningTree
{
- User* u = ServerInstance->FindNick(params[0]);
- if (!u)
- return CMD_FAILURE;
- if (IS_LOCAL(u))
- {
- u->Write(params[1]);
- }
- return CMD_SUCCESS;
+ class RemoteUser;
}
+
+class SpanningTree::RemoteUser : public ::RemoteUser
+{
+ public:
+ RemoteUser(const std::string& uid, Server* srv);
+ void WriteRemoteNumeric(const Numeric::Numeric& numeric) CXX11_OVERRIDE;
+};
diff --git a/src/modules/m_spanningtree/resolvers.cpp b/src/modules/m_spanningtree/resolvers.cpp
index 80e8aeb0e..ded0573af 100644
--- a/src/modules/m_spanningtree/resolvers.cpp
+++ b/src/modules/m_spanningtree/resolvers.cpp
@@ -42,16 +42,21 @@ ServernameResolver::ServernameResolver(DNS::Manager* mgr, const std::string& hos
void ServernameResolver::OnLookupComplete(const DNS::Query *r)
{
- const DNS::ResourceRecord &ans_record = r->answers[0];
+ const DNS::ResourceRecord* const ans_record = r->FindAnswerOfType(this->question.type);
+ if (!ans_record)
+ {
+ OnError(r);
+ return;
+ }
/* Initiate the connection, now that we have an IP to use.
* Passing a hostname directly to BufferedSocket causes it to
* just bail and set its FD to -1.
*/
- TreeServer* CheckDupe = Utils->FindServer(MyLink->Name.c_str());
+ TreeServer* CheckDupe = Utils->FindServer(MyLink->Name);
if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */
{
- TreeSocket* newsocket = new TreeSocket(MyLink, myautoconnect, ans_record.rdata);
+ TreeSocket* newsocket = new TreeSocket(MyLink, myautoconnect, ans_record->rdata);
if (newsocket->GetFd() > -1)
{
/* We're all OK */
@@ -68,7 +73,12 @@ void ServernameResolver::OnLookupComplete(const DNS::Query *r)
void ServernameResolver::OnError(const DNS::Query *r)
{
- /* Ooops! */
+ if (r->error == DNS::ERROR_UNLOADED)
+ {
+ // We're being unloaded, skip the snotice and ConnectServer() below to prevent autoconnect creating new sockets
+ return;
+ }
+
if (query == DNS::QUERY_AAAA)
{
ServernameResolver* snr = new ServernameResolver(this->manager, host, MyLink, DNS::QUERY_A, myautoconnect);
@@ -95,14 +105,17 @@ SecurityIPResolver::SecurityIPResolver(Module* me, DNS::Manager* mgr, const std:
void SecurityIPResolver::OnLookupComplete(const DNS::Query *r)
{
- const DNS::ResourceRecord &ans_record = r->answers[0];
-
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i != Utils->LinkBlocks.end(); ++i)
{
Link* L = *i;
if (L->IPAddr == host)
{
- Utils->ValidIPs.push_back(ans_record.rdata);
+ for (std::vector<DNS::ResourceRecord>::const_iterator j = r->answers.begin(); j != r->answers.end(); ++j)
+ {
+ const DNS::ResourceRecord& ans_record = *j;
+ if (ans_record.type == this->question.type)
+ Utils->ValidIPs.push_back(ans_record.rdata);
+ }
break;
}
}
@@ -110,6 +123,7 @@ void SecurityIPResolver::OnLookupComplete(const DNS::Query *r)
void SecurityIPResolver::OnError(const DNS::Query *r)
{
+ // This can be called because of us being unloaded but we don't have to do anything differently
if (query == DNS::QUERY_AAAA)
{
SecurityIPResolver* res = new SecurityIPResolver(mine, this->manager, host, MyLink, DNS::QUERY_A);
@@ -128,7 +142,7 @@ void SecurityIPResolver::OnError(const DNS::Query *r)
}
CacheRefreshTimer::CacheRefreshTimer()
- : Timer(3600, ServerInstance->Time(), true)
+ : Timer(3600, true)
{
}
diff --git a/src/modules/m_spanningtree/rsquit.cpp b/src/modules/m_spanningtree/rsquit.cpp
index 988918c3f..487db2826 100644
--- a/src/modules/m_spanningtree/rsquit.cpp
+++ b/src/modules/m_spanningtree/rsquit.cpp
@@ -39,24 +39,22 @@ CmdResult CommandRSQuit::Handle (const std::vector<std::string>& parameters, Use
server_target = Utils->FindServerMask(parameters[0]);
if (!server_target)
{
- ((ModuleSpanningTree*)(Module*)creator)->RemoteMessage(user, "*** RSQUIT: Server \002%s\002 isn't connected to the network!", parameters[0].c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** RSQUIT: Server \002%s\002 isn't connected to the network!", parameters[0].c_str()));
return CMD_FAILURE;
}
if (server_target->IsRoot())
{
- ((ModuleSpanningTree*)(Module*)creator)->RemoteMessage(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)", parameters[0].c_str());
+ user->WriteRemoteNotice(InspIRCd::Format("*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)", parameters[0].c_str()));
return CMD_FAILURE;
}
if (server_target->IsLocal())
{
// We have been asked to remove server_target.
- TreeSocket* sock = server_target->GetSocket();
const char* reason = parameters.size() == 2 ? parameters[1].c_str() : "No reason";
ServerInstance->SNO->WriteToSnoMask('l',"RSQUIT: Server \002%s\002 removed from network by %s (%s)", parameters[0].c_str(), user->nick.c_str(), reason);
- sock->Squit(server_target, "Server quit by " + user->GetFullRealHost() + " (" + reason + ")");
- sock->Close();
+ server_target->SQuit("Server quit by " + user->GetFullRealHost() + " (" + reason + ")");
}
return CMD_SUCCESS;
diff --git a/src/modules/m_spanningtree/save.cpp b/src/modules/m_spanningtree/save.cpp
index 03d401211..7131b49fe 100644
--- a/src/modules/m_spanningtree/save.cpp
+++ b/src/modules/m_spanningtree/save.cpp
@@ -28,19 +28,14 @@
*/
CmdResult CommandSave::Handle(User* user, std::vector<std::string>& params)
{
- User* u = ServerInstance->FindNick(params[0]);
- if ((!u) || (IS_SERVER(u)))
+ User* u = ServerInstance->FindUUID(params[0]);
+ if (!u)
return CMD_FAILURE;
time_t ts = atol(params[1].c_str());
if (u->age == ts)
- {
- if (!u->ForceNickChange(u->uuid))
- {
- ServerInstance->Users->QuitUser(u, "Nickname collision");
- }
- }
+ u->ChangeNick(u->uuid, SavedTimestamp);
return CMD_SUCCESS;
}
diff --git a/src/modules/m_spanningtree/server.cpp b/src/modules/m_spanningtree/server.cpp
index 69cae001c..50f63e117 100644
--- a/src/modules/m_spanningtree/server.cpp
+++ b/src/modules/m_spanningtree/server.cpp
@@ -19,6 +19,7 @@
#include "inspircd.h"
+#include "modules/ssl.h"
#include "main.h"
#include "utils.h"
@@ -33,11 +34,9 @@
*/
CmdResult CommandServer::HandleServer(TreeServer* ParentOfThis, std::vector<std::string>& params)
{
- std::string servername = params[0];
- // password is not used for a remote server
- // hopcount is not used (ever)
- std::string sid = params[3];
- std::string description = params[4];
+ const std::string& servername = params[0];
+ const std::string& sid = params[1];
+ const std::string& description = params.back();
TreeSocket* socket = ParentOfThis->GetSocket();
if (!InspIRCd::IsSID(sid))
@@ -65,42 +64,57 @@ CmdResult CommandServer::HandleServer(TreeServer* ParentOfThis, std::vector<std:
TreeServer* Node = new TreeServer(servername, description, sid, ParentOfThis, ParentOfThis->GetSocket(), lnk ? lnk->Hidden : false);
- ParentOfThis->AddChild(Node);
+ HandleExtra(Node, params);
+
ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+ParentOfThis->GetName()+"\002 introduced server \002"+servername+"\002 ("+description+")");
return CMD_SUCCESS;
}
+void CommandServer::HandleExtra(TreeServer* newserver, const std::vector<std::string>& params)
+{
+ for (std::vector<std::string>::const_iterator i = params.begin() + 2; i != params.end() - 1; ++i)
+ {
+ const std::string& prop = *i;
+ std::string::size_type p = prop.find('=');
-/*
- * This is used after the other side of a connection has accepted our credentials.
- * They are then introducing themselves to us, BEFORE either of us burst. -- w
- */
-bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
+ std::string key = prop;
+ std::string val;
+ if (p != std::string::npos)
+ {
+ key.erase(p);
+ val.assign(prop, p+1, std::string::npos);
+ }
+
+ if (key == "burst")
+ newserver->BeginBurst(ConvToUInt64(val));
+ }
+}
+
+Link* TreeSocket::AuthRemote(const parameterlist& params)
{
if (params.size() < 5)
{
SendError("Protocol error - Not enough parameters for SERVER command");
- return false;
+ return NULL;
}
- irc::string servername = params[0].c_str();
- std::string sname = params[0];
- std::string password = params[1];
- std::string sid = params[3];
- std::string description = params[4];
+ const std::string& sname = params[0];
+ const std::string& password = params[1];
+ const std::string& sid = params[3];
+ const std::string& description = params.back();
this->SendCapabilities(2);
if (!ServerInstance->IsSID(sid))
{
this->SendError("Invalid format server ID: "+sid+"!");
- return false;
+ return NULL;
}
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
{
Link* x = *i;
- if (x->Name != servername && x->Name != "*") // open link allowance
+ if ((!stdalgo::string::equalsci(x->Name, sname)) && (x->Name != "*")) // open link allowance
continue;
if (!ComparePass(*x, password))
@@ -109,22 +123,36 @@ bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
continue;
}
- TreeServer* CheckDupe = Utils->FindServer(sname);
- if (CheckDupe)
- {
- std::string pname = CheckDupe->GetParent() ? CheckDupe->GetParent()->GetName() : "<ourself>";
- SendError("Server "+sname+" already exists on server "+pname+"!");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+pname);
- return false;
- }
- CheckDupe = Utils->FindServer(sid);
- if (CheckDupe)
+ if (!CheckDuplicate(sname, sid))
+ return NULL;
+
+ ServerInstance->SNO->WriteToSnoMask('l',"Verified server connection " + linkID + " ("+description+")");
+
+ const SSLIOHook* const ssliohook = SSLIOHook::IsSSL(this);
+ if (ssliohook)
{
- this->SendError("Server ID "+sid+" already exists on the network! You may want to specify the server ID for the server manually with <server:id> so they do not conflict.");
- ServerInstance->SNO->WriteToSnoMask('l',"Server \2"+assign(servername)+"\2 being introduced denied, server ID already exists on the network. Closing link.");
- return false;
+ std::string ciphersuite;
+ ssliohook->GetCiphersuite(ciphersuite);
+ ServerInstance->SNO->WriteToSnoMask('l', "Negotiated ciphersuite %s on link %s", ciphersuite.c_str(), x->Name.c_str());
}
+ return x;
+ }
+
+ this->SendError("Mismatched server name or password (check the other server's snomask output for details - e.g. umode +s +Ll)");
+ ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
+ return NULL;
+}
+
+/*
+ * This is used after the other side of a connection has accepted our credentials.
+ * They are then introducing themselves to us, BEFORE either of us burst. -- w
+ */
+bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
+{
+ const Link* x = AuthRemote(params);
+ if (x)
+ {
/*
* They're in WAIT_AUTH_2 (having accepted our credentials).
* Set our state to CONNECTED (since everything's peachy so far) and send our
@@ -133,29 +161,17 @@ bool TreeSocket::Outbound_Reply_Server(parameterlist &params)
* While we're at it, create a treeserver object so we know about them.
* -- w
*/
- this->LinkState = CONNECTED;
-
- Utils->timeoutlist.erase(this);
- linkID = sname;
-
- MyRoot = new TreeServer(sname, description, sid, Utils->TreeRoot, this, x->Hidden);
- Utils->TreeRoot->AddChild(MyRoot);
- this->DoBurst(MyRoot);
-
- // This will send a * in place of the password/hmac
- CommandServer::Builder(MyRoot).Forward(MyRoot);
+ FinishAuth(params[0], params[3], params.back(), x->Hidden);
return true;
}
- this->SendError("Invalid credentials (check the other server's linking snomask for more information)");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
return false;
}
bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid)
{
- /* Check for fully initialized instances of the server by name */
+ // Check if the server name is not in use by a server that's already fully connected
TreeServer* CheckDupe = Utils->FindServer(sname);
if (CheckDupe)
{
@@ -165,7 +181,7 @@ bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid
return false;
}
- /* Check for fully initialized instances of the server by id */
+ // Check if the id is not in use by a server that's already fully connected
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Looking for dupe SID %s", sid.c_str());
CheckDupe = Utils->FindServerID(sid);
@@ -186,50 +202,14 @@ bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid
*/
bool TreeSocket::Inbound_Server(parameterlist &params)
{
- if (params.size() < 5)
+ const Link* x = AuthRemote(params);
+ if (x)
{
- SendError("Protocol error - Missing SID");
- return false;
- }
-
- irc::string servername = params[0].c_str();
- std::string sname = params[0];
- std::string password = params[1];
- std::string sid = params[3];
- std::string description = params[4];
-
- this->SendCapabilities(2);
-
- if (!ServerInstance->IsSID(sid))
- {
- this->SendError("Invalid format server ID: "+sid+"!");
- return false;
- }
-
- for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
- {
- Link* x = *i;
- if (x->Name != servername && x->Name != "*") // open link allowance
- continue;
-
- if (!ComparePass(*x, password))
- {
- ServerInstance->SNO->WriteToSnoMask('l',"Invalid password on link: %s", x->Name.c_str());
- continue;
- }
-
- if (!CheckDuplicate(sname, sid))
- return false;
-
- ServerInstance->SNO->WriteToSnoMask('l',"Verified incoming server connection " + linkID + " ("+description+")");
-
- this->SendCapabilities(2);
-
// Save these for later, so when they accept our credentials (indicated by BURST) we remember them
this->capab->hidden = x->Hidden;
- this->capab->sid = sid;
- this->capab->description = description;
- this->capab->name = sname;
+ this->capab->sid = params[3];
+ this->capab->description = params.back();
+ this->capab->name = params[0];
// Send our details: Our server name and description and hopcount of 0,
// along with the sendpass from this block.
@@ -240,8 +220,6 @@ bool TreeSocket::Inbound_Server(parameterlist &params)
return true;
}
- this->SendError("Invalid credentials");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
return false;
}
@@ -249,7 +227,8 @@ CommandServer::Builder::Builder(TreeServer* server)
: CmdBuilder(server->GetParent()->GetID(), "SERVER")
{
push(server->GetName());
- push_raw(" * 0 ");
- push_raw(server->GetID());
+ push(server->GetID());
+ if (server->IsBursting())
+ push_property("burst", ConvToStr(server->StartBurst));
push_last(server->GetDesc());
}
diff --git a/src/modules/m_spanningtree/servercommand.cpp b/src/modules/m_spanningtree/servercommand.cpp
index 3034eee7a..ef55cd00e 100644
--- a/src/modules/m_spanningtree/servercommand.cpp
+++ b/src/modules/m_spanningtree/servercommand.cpp
@@ -24,8 +24,11 @@
ServerCommand::ServerCommand(Module* Creator, const std::string& Name, unsigned int MinParams, unsigned int MaxParams)
: CommandBase(Creator, Name, MinParams, MaxParams)
{
- this->ServiceProvider::DisableAutoRegister();
- ModuleSpanningTree* st = static_cast<ModuleSpanningTree*>(Creator);
+}
+
+void ServerCommand::RegisterService()
+{
+ ModuleSpanningTree* st = static_cast<ModuleSpanningTree*>(static_cast<Module*>(creator));
st->CmdManager.AddCommand(this);
}
diff --git a/src/modules/m_spanningtree/servercommand.h b/src/modules/m_spanningtree/servercommand.h
index 524520a88..07dfc4898 100644
--- a/src/modules/m_spanningtree/servercommand.h
+++ b/src/modules/m_spanningtree/servercommand.h
@@ -38,6 +38,10 @@ class ServerCommand : public CommandBase
public:
ServerCommand(Module* Creator, const std::string& Name, unsigned int MinPara = 0, unsigned int MaxPara = 0);
+ /** Register this object in the ServerCommandManager
+ */
+ void RegisterService() CXX11_OVERRIDE;
+
virtual CmdResult Handle(User* user, std::vector<std::string>& parameters) = 0;
virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
diff --git a/src/modules/m_spanningtree/sinfo.cpp b/src/modules/m_spanningtree/sinfo.cpp
new file mode 100644
index 000000000..0989ea9a5
--- /dev/null
+++ b/src/modules/m_spanningtree/sinfo.cpp
@@ -0,0 +1,51 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+
+#include "treeserver.h"
+#include "commands.h"
+
+CmdResult CommandSInfo::HandleServer(TreeServer* server, std::vector<std::string>& params)
+{
+ const std::string& key = params.front();
+ const std::string& value = params.back();
+
+ if (key == "fullversion")
+ {
+ server->SetFullVersion(value);
+ }
+ else if (key == "version")
+ {
+ server->SetVersion(value);
+ }
+ else if (key == "desc")
+ {
+ // Only sent when the description of a server changes because of a rehash; not sent on burst
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Server description of " + server->GetName() + " changed: " + value);
+ server->SetDesc(value);
+ }
+
+ return CMD_SUCCESS;
+}
+
+CommandSInfo::Builder::Builder(TreeServer* server, const char* key, const std::string& val)
+ : CmdBuilder(server->GetID(), "SINFO")
+{
+ push(key).push_last(val);
+}
diff --git a/src/modules/m_spanningtree/svsjoin.cpp b/src/modules/m_spanningtree/svsjoin.cpp
index 552e08dd3..c85e4f412 100644
--- a/src/modules/m_spanningtree/svsjoin.cpp
+++ b/src/modules/m_spanningtree/svsjoin.cpp
@@ -36,14 +36,23 @@ CmdResult CommandSVSJoin::Handle(User* user, std::vector<std::string>& parameter
/* only join if it's local, otherwise just pass it on! */
LocalUser* localuser = IS_LOCAL(u);
if (localuser)
- Channel::JoinUser(localuser, parameters[1]);
+ {
+ bool override = false;
+ std::string key;
+ if (parameters.size() >= 3)
+ {
+ key = parameters[2];
+ if (key.empty())
+ override = true;
+ }
+
+ Channel::JoinUser(localuser, parameters[1], override, key);
+ }
+
return CMD_SUCCESS;
}
RouteDescriptor CommandSVSJoin::GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* u = ServerInstance->FindUUID(parameters[0]);
- if (u)
- return ROUTE_OPT_UCAST(u->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
diff --git a/src/modules/m_spanningtree/svsnick.cpp b/src/modules/m_spanningtree/svsnick.cpp
index 43fa0f296..84cf8558c 100644
--- a/src/modules/m_spanningtree/svsnick.cpp
+++ b/src/modules/m_spanningtree/svsnick.cpp
@@ -29,6 +29,29 @@ CmdResult CommandSVSNick::Handle(User* user, std::vector<std::string>& parameter
if (u && IS_LOCAL(u))
{
+ // The 4th parameter is optional and it is the expected nick TS of the target user. If this parameter is
+ // present and it doesn't match the user's nick TS, the SVSNICK is not acted upon.
+ // This makes it possible to detect the case when services wants to change the nick of a user, but the
+ // user changes their nick before the SVSNICK arrives, making the SVSNICK nick change (usually to a guest nick)
+ // unnecessary. Consider the following for example:
+ //
+ // 1. test changes nick to Attila which is protected by services
+ // 2. Services SVSNICKs the user to Guest12345
+ // 3. Attila changes nick to Attila_ which isn't protected by services
+ // 4. SVSNICK arrives
+ // 5. Attila_ gets his nick changed to Guest12345 unnecessarily
+ //
+ // In this case when the SVSNICK is processed the target has already changed his nick to something
+ // which isn't protected, so changing the nick again to a Guest nick is not desired.
+ // However, if the expected nick TS parameter is present in the SVSNICK then the nick change in step 5
+ // won't happen because the timestamps won't match.
+ if (parameters.size() > 3)
+ {
+ time_t ExpectedTS = ConvToInt(parameters[3]);
+ if (u->age != ExpectedTS)
+ return CMD_FAILURE; // Ignore SVSNICK
+ }
+
std::string nick = parameters[1];
if (isdigit(nick[0]))
nick = u->uuid;
@@ -37,13 +60,10 @@ CmdResult CommandSVSNick::Handle(User* user, std::vector<std::string>& parameter
if (NickTS <= 0)
return CMD_FAILURE;
- if (!u->ForceNickChange(nick, NickTS))
+ if (!u->ChangeNick(nick, NickTS))
{
- /* buh. UID them */
- if (!u->ForceNickChange(u->uuid))
- {
- ServerInstance->Users->QuitUser(u, "Nickname collision");
- }
+ // Changing to 'nick' failed (it may already be in use), change to the uuid
+ u->ChangeNick(u->uuid);
}
}
@@ -52,8 +72,5 @@ CmdResult CommandSVSNick::Handle(User* user, std::vector<std::string>& parameter
RouteDescriptor CommandSVSNick::GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* u = ServerInstance->FindNick(parameters[0]);
- if (u)
- return ROUTE_OPT_UCAST(u->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
diff --git a/src/modules/m_spanningtree/svspart.cpp b/src/modules/m_spanningtree/svspart.cpp
index f86afa367..c4163ef3d 100644
--- a/src/modules/m_spanningtree/svspart.cpp
+++ b/src/modules/m_spanningtree/svspart.cpp
@@ -42,8 +42,5 @@ CmdResult CommandSVSPart::Handle(User* user, std::vector<std::string>& parameter
RouteDescriptor CommandSVSPart::GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* u = ServerInstance->FindUUID(parameters[0]);
- if (u)
- return ROUTE_OPT_UCAST(u->server);
- return ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
diff --git a/src/modes/cmode_v.cpp b/src/modules/m_spanningtree/translate.cpp
index c8ce30ab1..66e1bb35b 100644
--- a/src/modes/cmode_v.cpp
+++ b/src/modules/m_spanningtree/translate.cpp
@@ -1,10 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -21,16 +18,31 @@
#include "inspircd.h"
-#include "configreader.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "builtinmodes.h"
+#include "translate.h"
-ModeChannelVoice::ModeChannelVoice() : PrefixMode(NULL, "voice", 'v')
+std::string Translate::ModeChangeListToParams(const Modes::ChangeList::List& modes)
{
- prefix = '+';
- levelrequired = HALFOP_VALUE;
- prefixrank = VOICE_VALUE;
+ std::string ret;
+ for (Modes::ChangeList::List::const_iterator i = modes.begin(); i != modes.end(); ++i)
+ {
+ const Modes::Change& item = *i;
+ ModeHandler* mh = item.mh;
+ if (!mh->NeedsParam(item.adding))
+ continue;
+
+ ret.push_back(' ');
+
+ if (mh->IsPrefixMode())
+ {
+ User* target = ServerInstance->FindNick(item.param);
+ if (target)
+ {
+ ret.append(target->uuid);
+ continue;
+ }
+ }
+
+ ret.append(item.param);
+ }
+ return ret;
}
diff --git a/src/modes/cmode_o.cpp b/src/modules/m_spanningtree/translate.h
index 6e96afa67..a2bc6df78 100644
--- a/src/modes/cmode_o.cpp
+++ b/src/modules/m_spanningtree/translate.h
@@ -1,10 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -20,17 +17,14 @@
*/
-#include "inspircd.h"
-#include "configreader.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "builtinmodes.h"
+#pragma once
-ModeChannelOp::ModeChannelOp() : PrefixMode(NULL, "op", 'o')
+namespace Translate
{
- prefix = '@';
- levelrequired = OP_VALUE;
- prefixrank = OP_VALUE;
+ /** Generate a list of mode parameters suitable for FMODE/MODE from a Modes::ChangeList::List
+ * @param modes List of mode changes
+ * @return List of mode parameters built from the input. Does not include the modes themselves,
+ * only the parameters.
+ */
+ std::string ModeChangeListToParams(const Modes::ChangeList::List& modes);
}
diff --git a/src/modules/m_spanningtree/treeserver.cpp b/src/modules/m_spanningtree/treeserver.cpp
index 3d57b1314..b29bea134 100644
--- a/src/modules/m_spanningtree/treeserver.cpp
+++ b/src/modules/m_spanningtree/treeserver.cpp
@@ -35,28 +35,32 @@
TreeServer::TreeServer()
: Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc)
, Parent(NULL), Route(NULL)
- , VersionString(ServerInstance->GetVersionString()), Socket(NULL), sid(ServerInstance->Config->GetSID()), ServerUser(ServerInstance->FakeClient)
- , age(ServerInstance->Time()), Warned(false), bursting(false), UserCount(0), OperCount(0), rtt(0), StartBurst(0), Hidden(false)
+ , VersionString(ServerInstance->GetVersionString())
+ , fullversion(ServerInstance->GetVersionString(true))
+ , Socket(NULL), sid(ServerInstance->Config->GetSID()), behind_bursting(0), isdead(false)
+ , pingtimer(this)
+ , ServerUser(ServerInstance->FakeClient)
+ , age(ServerInstance->Time()), UserCount(ServerInstance->Users.LocalUserCount())
+ , OperCount(0), rtt(0), StartBurst(0), Hidden(false)
{
AddHashEntry();
}
/** When we create a new server, we call this constructor to initialize it.
* This constructor initializes the server's Route and Parent, and sets up
- * its ping counters so that it will be pinged one minute from now.
+ * the ping timer for the server.
*/
TreeServer::TreeServer(const std::string& Name, const std::string& Desc, const std::string& id, TreeServer* Above, TreeSocket* Sock, bool Hide)
: Server(Name, Desc)
- , Parent(Above), Socket(Sock), sid(id), ServerUser(new FakeUser(id, this))
- , age(ServerInstance->Time()), Warned(false), bursting(true), UserCount(0), OperCount(0), rtt(0), Hidden(Hide)
+ , Parent(Above), Socket(Sock), sid(id), behind_bursting(Parent->behind_bursting), isdead(false)
+ , pingtimer(this)
+ , ServerUser(new FakeUser(id, this))
+ , age(ServerInstance->Time()), UserCount(0), OperCount(0), rtt(0), StartBurst(0), Hidden(Hide)
{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "New server %s behind_bursting %u", GetName().c_str(), behind_bursting);
CheckULine();
- SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
- SetPingFlag();
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
- this->StartBurst = ts;
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s started bursting at time %lu", sid.c_str(), ts);
+ ServerInstance->Timers.AddTimer(&pingtimer);
/* find the 'route' for this server (e.g. the one directly connected
* to the local server, which we can use to reach it)
@@ -110,18 +114,31 @@ TreeServer::TreeServer(const std::string& Name, const std::string& Desc, const s
*/
this->AddHashEntry();
+ Parent->Children.push_back(this);
+
+ FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), SpanningTreeEventListener, OnServerLink, (this));
}
-const std::string& TreeServer::GetID()
+void TreeServer::BeginBurst(uint64_t startms)
{
- return sid;
+ behind_bursting++;
+
+ uint64_t now = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ // If the start time is in the future (clocks are not synced) then use current time
+ if ((!startms) || (startms > now))
+ startms = now;
+ this->StartBurst = startms;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s started bursting at time %s behind_bursting %u", sid.c_str(), ConvToStr(startms).c_str(), behind_bursting);
}
void TreeServer::FinishBurstInternal()
{
- this->bursting = false;
- SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
- SetPingFlag();
+ // Check is needed because 1202 protocol servers don't send the bursting state of a server, so servers
+ // introduced during a netburst may later send ENDBURST which would normally decrease this counter
+ if (behind_bursting > 0)
+ behind_bursting--;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "FinishBurstInternal() %s behind_bursting %u", GetName().c_str(), behind_bursting);
+
for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
{
TreeServer* child = *i;
@@ -131,16 +148,68 @@ void TreeServer::FinishBurstInternal()
void TreeServer::FinishBurst()
{
- FinishBurstInternal();
ServerInstance->XLines->ApplyLines();
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ uint64_t ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
unsigned long bursttime = ts - this->StartBurst;
ServerInstance->SNO->WriteToSnoMask(Parent == Utils->TreeRoot ? 'l' : 'L', "Received end of netburst from \2%s\2 (burst time: %lu %s)",
GetName().c_str(), (bursttime > 10000 ? bursttime / 1000 : bursttime), (bursttime > 10000 ? "secs" : "msecs"));
- AddServerEvent(Utils->Creator, GetName());
+
+ StartBurst = 0;
+ FinishBurstInternal();
+}
+
+void TreeServer::SQuitChild(TreeServer* server, const std::string& reason)
+{
+ stdalgo::erase(Children, server);
+
+ if (IsRoot())
+ {
+ // Server split from us, generate a SQUIT message and broadcast it
+ ServerInstance->SNO->WriteGlobalSno('l', "Server \002" + server->GetName() + "\002 split: " + reason);
+ CmdBuilder("SQUIT").push(server->GetID()).push_last(reason).Broadcast();
+ }
+ else
+ {
+ ServerInstance->SNO->WriteToSnoMask('L', "Server \002" + server->GetName() + "\002 split from server \002" + GetName() + "\002 with reason: " + reason);
+ }
+
+ unsigned int num_lost_servers = 0;
+ server->SQuitInternal(num_lost_servers);
+
+ const std::string quitreason = GetName() + " " + server->GetName();
+ unsigned int num_lost_users = QuitUsers(quitreason);
+
+ ServerInstance->SNO->WriteToSnoMask(IsRoot() ? 'l' : 'L', "Netsplit complete, lost \002%u\002 user%s on \002%u\002 server%s.",
+ num_lost_users, num_lost_users != 1 ? "s" : "", num_lost_servers, num_lost_servers != 1 ? "s" : "");
+
+ // No-op if the socket is already closed (i.e. it called us)
+ if (server->IsLocal())
+ server->GetSocket()->Close();
+
+ // Add the server to the cull list, the servers behind it are handled by cull() and the destructor
+ ServerInstance->GlobalCulls.AddItem(server);
}
-int TreeServer::QuitUsers(const std::string &reason)
+void TreeServer::SQuitInternal(unsigned int& num_lost_servers)
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s lost in split", GetName().c_str());
+
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
+ {
+ TreeServer* server = *i;
+ server->SQuitInternal(num_lost_servers);
+ }
+
+ // Mark server as dead
+ isdead = true;
+ num_lost_servers++;
+ RemoveHash();
+
+ if (!Utils->Creator->dying)
+ FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), SpanningTreeEventListener, OnServerSplit, (this));
+}
+
+unsigned int TreeServer::QuitUsers(const std::string& reason)
{
std::string publicreason = ServerInstance->Config->HideSplits ? "*.net *.split" : reason;
@@ -151,7 +220,8 @@ int TreeServer::QuitUsers(const std::string &reason)
User* user = i->second;
// Increment the iterator now because QuitUser() removes the user from the container
++i;
- if (user->server == this)
+ TreeServer* server = TreeServer::Get(user);
+ if (server->IsDead())
ServerInstance->Users->QuitUser(user, publicreason, &reason);
}
return original_size - users.size();
@@ -181,8 +251,8 @@ void TreeServer::CheckULine()
}
}
-/** This method is used to add the structure to the
- * hash_map for linear searches. It is only called
+/** This method is used to add the server to the
+ * maps for linear searches. It is only called
* by the constructors.
*/
void TreeServer::AddHashEntry()
@@ -191,92 +261,15 @@ void TreeServer::AddHashEntry()
Utils->sidlist[sid] = this;
}
-/** These accessors etc should be pretty self-
- * explanitory.
- */
-TreeServer* TreeServer::GetRoute()
-{
- return Route;
-}
-
-const std::string& TreeServer::GetVersion()
-{
- return VersionString;
-}
-
-void TreeServer::SetNextPingTime(time_t t)
-{
- this->NextPing = t;
- LastPingWasGood = false;
-}
-
-time_t TreeServer::NextPingTime()
-{
- return NextPing;
-}
-
-bool TreeServer::AnsweredLastPing()
-{
- return LastPingWasGood;
-}
-
-void TreeServer::SetPingFlag()
-{
- LastPingWasGood = true;
-}
-
-TreeSocket* TreeServer::GetSocket()
-{
- return Socket;
-}
-
-TreeServer* TreeServer::GetParent()
-{
- return Parent;
-}
-
-void TreeServer::SetVersion(const std::string &Version)
-{
- VersionString = Version;
-}
-
-void TreeServer::AddChild(TreeServer* Child)
-{
- Children.push_back(Child);
-}
-
-bool TreeServer::DelChild(TreeServer* Child)
-{
- std::vector<TreeServer*>::iterator it = std::find(Children.begin(), Children.end(), Child);
- if (it != Children.end())
- {
- Children.erase(it);
- return true;
- }
- return false;
-}
-
-/** Removes child nodes of this node, and of that node, etc etc.
- * This is used during netsplits to automatically tidy up the
- * server tree. It is slow, we don't use it for much else.
- */
-void TreeServer::Tidy()
+CullResult TreeServer::cull()
{
- while (1)
+ // Recursively cull all servers that are under us in the tree
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
{
- std::vector<TreeServer*>::iterator a = Children.begin();
- if (a == Children.end())
- return;
- TreeServer* s = *a;
- s->Tidy();
- s->cull();
- Children.erase(a);
- delete s;
+ TreeServer* server = *i;
+ server->cull();
}
-}
-CullResult TreeServer::cull()
-{
if (!IsRoot())
ServerUser->cull();
return classbase::cull();
@@ -284,10 +277,17 @@ CullResult TreeServer::cull()
TreeServer::~TreeServer()
{
- /* We'd better tidy up after ourselves, eh? */
+ // Recursively delete all servers that are under us in the tree first
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
+ delete *i;
+
+ // Delete server user unless it's us
if (!IsRoot())
delete ServerUser;
+}
+void TreeServer::RemoveHash()
+{
Utils->sidlist.erase(sid);
Utils->serverlist.erase(GetName());
}
diff --git a/src/modules/m_spanningtree/treeserver.h b/src/modules/m_spanningtree/treeserver.h
index ab47012b0..b7e9ee9d9 100644
--- a/src/modules/m_spanningtree/treeserver.h
+++ b/src/modules/m_spanningtree/treeserver.h
@@ -22,6 +22,7 @@
#pragma once
#include "treesocket.h"
+#include "pingtimer.h"
/** Each server in the tree is represented by one class of
* type TreeServer. A locally connected TreeServer can
@@ -43,24 +44,47 @@ class TreeServer : public Server
TreeServer* Route; /* Route entry */
std::vector<TreeServer*> Children; /* List of child objects */
std::string VersionString; /* Version string or empty string */
+
+ /** Full version string including patch version and other info
+ */
+ std::string fullversion;
+
TreeSocket* Socket; /* Socket used to communicate with this server */
- time_t NextPing; /* After this time, the server should be PINGed*/
- bool LastPingWasGood; /* True if the server responded to the last PING with a PONG */
std::string sid; /* Server ID */
+ /** Counter counting how many servers are bursting in front of this server, including
+ * this server. Set to parents' value on construction then it is increased if the
+ * server itself starts bursting. Decreased when a server on the path to this server
+ * finishes burst.
+ */
+ unsigned int behind_bursting;
+
+ /** True if this server has been lost in a split and is awaiting destruction
+ */
+ bool isdead;
+
+ /** Timer handling PINGing the server and killing it on timeout
+ */
+ PingTimer pingtimer;
+
/** This method is used to add this TreeServer to the
* hash maps. It is only called by the constructors.
*/
void AddHashEntry();
+ /** Used by SQuit logic to recursively remove servers
+ */
+ void SQuitInternal(unsigned int& num_lost_servers);
+
+ /** Remove the reference to this server from the hash maps
+ */
+ void RemoveHash();
+
public:
typedef std::vector<TreeServer*> ChildServers;
FakeUser* const ServerUser; /* User representing this server */
const time_t age;
- bool Warned; /* True if we've warned opers about high latency on this server */
- bool bursting; /* whether or not this server is bursting */
-
unsigned int UserCount; /* How many users are on this server? [note: doesn't care about +i] */
unsigned int OperCount; /* How many opers are on this server? */
@@ -76,13 +100,27 @@ class TreeServer : public Server
*/
TreeServer(const std::string& Name, const std::string& Desc, const std::string& id, TreeServer* Above, TreeSocket* Sock, bool Hide);
- int QuitUsers(const std::string &reason);
+ /** SQuit a server connected to this server, removing the given server and all servers behind it
+ * @param server Server to squit, must be directly below this server
+ * @param reason Reason for quitting the server, sent to opers and other servers
+ */
+ void SQuitChild(TreeServer* server, const std::string& reason);
+
+ /** SQuit this server, removing this server and all servers behind it
+ * @param reason Reason for quitting the server, sent to opers and other servers
+ */
+ void SQuit(const std::string& reason)
+ {
+ GetParent()->SQuitChild(this, reason);
+ }
+
+ static unsigned int QuitUsers(const std::string& reason);
/** Get route.
* The 'route' is defined as the locally-
* connected server which can be used to reach this server.
*/
- TreeServer* GetRoute();
+ TreeServer* GetRoute() const { return Route; }
/** Returns true if this server is the tree root (i.e.: us)
*/
@@ -92,21 +130,19 @@ class TreeServer : public Server
*/
bool IsLocal() const { return (this->Route == this); }
- /** Get server version string
- */
- const std::string& GetVersion();
-
- /** Set time we are next due to ping this server
+ /** Returns true if the server is awaiting destruction
+ * @return True if the server is waiting to be culled and deleted, false otherwise
*/
- void SetNextPingTime(time_t t);
+ bool IsDead() const { return isdead; }
- /** Get the time we are next due to ping this server
+ /** Get server version string
*/
- time_t NextPingTime();
+ const std::string& GetVersion() const { return VersionString; }
- /** Last ping time in milliseconds, used to calculate round trip time
+ /** Get the full version string of this server
+ * @return The full version string of this server, including patch version and other info
*/
- unsigned long LastPingMsec;
+ const std::string& GetFullVersion() const { return fullversion; }
/** Round trip time of last ping
*/
@@ -114,55 +150,44 @@ class TreeServer : public Server
/** When we recieved BURST from this server, used to calculate total burst time at ENDBURST.
*/
- unsigned long StartBurst;
+ uint64_t StartBurst;
/** True if this server is hidden
*/
bool Hidden;
- /** True if the server answered their last ping
- */
- bool AnsweredLastPing();
-
- /** Set the server as responding to its last ping
- */
- void SetPingFlag();
-
/** Get the TreeSocket pointer for local servers.
* For remote servers, this returns NULL.
*/
- TreeSocket* GetSocket();
+ TreeSocket* GetSocket() const { return Socket; }
/** Get the parent server.
* For the root node, this returns NULL.
*/
- TreeServer* GetParent();
+ TreeServer* GetParent() const { return Parent; }
/** Set the server version string
*/
- void SetVersion(const std::string &Version);
-
- /** Return all child servers
- */
- const ChildServers& GetChildren() const { return Children; }
+ void SetVersion(const std::string& verstr) { VersionString = verstr; }
- /** Add a child server
+ /** Set the full version string
+ * @param verstr The version string to set
*/
- void AddChild(TreeServer* Child);
+ void SetFullVersion(const std::string& verstr) { fullversion = verstr; }
- /** Delete a child server, return false if it didn't exist.
+ /** Sets the description of this server. Called when the description of a remote server changes
+ * and we are notified about it.
+ * @param descstr The description to set
*/
- bool DelChild(TreeServer* Child);
+ void SetDesc(const std::string& descstr) { description = descstr; }
- /** Removes child nodes of this node, and of that node, etc etc.
- * This is used during netsplits to automatically tidy up the
- * server tree. It is slow, we don't use it for much else.
+ /** Return all child servers
*/
- void Tidy();
+ const ChildServers& GetChildren() const { return Children; }
/** Get server ID
*/
- const std::string& GetID();
+ const std::string& GetID() const { return sid; }
/** Marks a server as having finished bursting and performs appropriate actions.
*/
@@ -174,11 +199,29 @@ class TreeServer : public Server
*/
void CheckULine();
+ /** Get the bursting state of this server
+ * @return True if this server is bursting, false if it isn't
+ */
+ bool IsBursting() const { return (StartBurst != 0); }
+
+ /** Check whether this server is behind a bursting server or is itself bursting.
+ * This can tell whether a user is on a part of the network that is still bursting.
+ * @return True if this server is bursting or is behind a server that is bursting, false if it isn't
+ */
+ bool IsBehindBursting() const { return (behind_bursting != 0); }
+
+ /** Set the bursting state of the server
+ * @param startms Time the server started bursting, if 0 or omitted, use current time
+ */
+ void BeginBurst(uint64_t startms = 0);
+
+ /** Register a PONG from the server
+ */
+ void OnPong() { pingtimer.OnPong(); }
+
CullResult cull();
- /** Destructor
- * Removes the reference to this object from the
- * hash maps.
+ /** Destructor, deletes ServerUser unless IsRoot()
*/
~TreeServer();
diff --git a/src/modules/m_spanningtree/treesocket.h b/src/modules/m_spanningtree/treesocket.h
index 4f72ed006..4887623c1 100644
--- a/src/modules/m_spanningtree/treesocket.h
+++ b/src/modules/m_spanningtree/treesocket.h
@@ -73,7 +73,7 @@ struct CapabData
std::string ourchallenge; /* Challenge sent for challenge/response */
std::string theirchallenge; /* Challenge recv for challenge/response */
int capab_phase; /* Have sent CAPAB already */
- bool auth_fingerprint; /* Did we auth using SSL fingerprint */
+ bool auth_fingerprint; /* Did we auth using SSL certificate fingerprint */
bool auth_challenge; /* Did we auth using challenge/response */
// Data saved from incoming SERVER command, for later use when our credentials have been accepted by the other party
@@ -95,10 +95,13 @@ class TreeSocket : public BufferedSocket
ServerState LinkState; /* Link state */
CapabData* capab; /* Link setup data (held until burst is sent) */
TreeServer* MyRoot; /* The server we are talking to */
- time_t NextPing; /* Time when we are due to ping this server */
- bool LastPingWasGood; /* Responded to last ping we sent? */
int proto_version; /* Remote protocol version */
- bool ConnectionFailureShown; /* Set to true if a connection failure message was shown */
+
+ /** True if we've sent our burst.
+ * This only changes the behavior of message translation for 1202 protocol servers and it can be
+ * removed once 1202 support is dropped.
+ */
+ bool burstsent;
/** Checks if the given servername and sid are both free
*/
@@ -114,6 +117,45 @@ class TreeSocket : public BufferedSocket
/** Send all users and their oper state, away state and metadata */
void SendUsers(BurstState& bs);
+ /** Send all additional info about the given server to this server */
+ void SendServerInfo(TreeServer* from);
+
+ /** Find the User source of a command given a prefix and a command string.
+ * This connection must be fully up when calling this function.
+ * @param prefix Prefix string to find the source User object for. Can be a sid, a uuid or a server name.
+ * @param command The command whose source to find. This is required because certain commands (like mode
+ * changes and kills) must be processed even if their claimed source doesn't exist. If the given command is
+ * such a command and the source does not exist, the function returns a valid FakeUser that can be used to
+ * to process the command with.
+ * @return The command source to use when processing the command or NULL if the source wasn't found.
+ * Note that the direction of the returned source is not verified.
+ */
+ User* FindSource(const std::string& prefix, const std::string& command);
+
+ /** Finish the authentication phase of this connection.
+ * Change the state of the connection to CONNECTED, create a TreeServer object for the server on the
+ * other end of the connection using the details provided in the parameters, and finally send a burst.
+ * @param remotename Name of the remote server
+ * @param remotesid SID of the remote server
+ * @param remotedesc Description of the remote server
+ * @param hidden True if the remote server is hidden according to the configuration
+ */
+ void FinishAuth(const std::string& remotename, const std::string& remotesid, const std::string& remotedesc, bool hidden);
+
+ /** Authenticate the remote server.
+ * Validate the parameters and find the link block that matches the remote server. In case of an error,
+ * an appropriate snotice is generated, an ERROR message is sent and the connection is closed.
+ * Failing to find a matching link block counts as an error.
+ * @param params Parameters they sent in the SERVER command
+ * @return Link block for the remote server, or NULL if an error occurred
+ */
+ Link* AuthRemote(const parameterlist& params);
+
+ /** Write a line on this socket with a new line character appended, skipping all translation for old protocols
+ * @param line Line to write without a new line character at the end
+ */
+ void WriteLineNoCompat(const std::string& line);
+
public:
const time_t age;
@@ -132,7 +174,7 @@ class TreeSocket : public BufferedSocket
/** Get link state
*/
- ServerState GetLinkState();
+ ServerState GetLinkState() const { return LinkState; }
/** Get challenge set in our CAPAB for challenge/response
*/
@@ -206,20 +248,6 @@ class TreeSocket : public BufferedSocket
bool Capab(const parameterlist &params);
- /** This function forces this server to quit, removing this server
- * and any users on it (and servers and users below that, etc etc).
- * It's very slow and pretty clunky, but luckily unless your network
- * is having a REAL bad hair day, this function shouldnt be called
- * too many times a month ;-)
- */
- void SquitServer(std::string &from, TreeServer* Current, int& num_lost_servers, int& num_lost_users);
-
- /** This is a wrapper function for SquitServer above, which
- * does some validation first and passes on the SQUIT to all
- * other remaining servers.
- */
- void Squit(TreeServer* Current, const std::string &reason);
-
/** Send one or more FJOINs for a channel of users.
* If the length of a single line is more than 480-NICKMAX
* in length, it is split over multiple lines.
@@ -276,10 +304,6 @@ class TreeSocket : public BufferedSocket
*/
void Close();
- /** Returns true if this server was introduced to the rest of the network
- */
- bool Introduced();
-
/** Fixes messages coming from old servers so the new command handlers understand them
*/
bool PreProcessOldProtocolMessage(User*& who, std::string& cmd, std::vector<std::string>& params);
diff --git a/src/modules/m_spanningtree/treesocket1.cpp b/src/modules/m_spanningtree/treesocket1.cpp
index 931bd3f9f..370c38d2c 100644
--- a/src/modules/m_spanningtree/treesocket1.cpp
+++ b/src/modules/m_spanningtree/treesocket1.cpp
@@ -31,14 +31,14 @@
#include "treesocket.h"
#include "commands.h"
-/** Because most of the I/O gubbins are encapsulated within
- * BufferedSocket, we just call the superclass constructor for
- * most of the action, and append a few of our own values
- * to it.
+/** Constructor for outgoing connections.
+ * Because most of the I/O gubbins are encapsulated within
+ * BufferedSocket, we just call DoConnect() for most of the action,
+ * and only do minor initialization tasks ourselves.
*/
TreeSocket::TreeSocket(Link* link, Autoconnect* myac, const std::string& ipaddr)
- : linkID(assign(link->Name)), LinkState(CONNECTING), MyRoot(NULL), proto_version(0), ConnectionFailureShown(false)
- , age(ServerInstance->Time())
+ : linkID(link->Name), LinkState(CONNECTING), MyRoot(NULL), proto_version(0)
+ , burstsent(false), age(ServerInstance->Time())
{
capab = new CapabData;
capab->link = link;
@@ -50,30 +50,36 @@ TreeSocket::TreeSocket(Link* link, Autoconnect* myac, const std::string& ipaddr)
SendCapabilities(1);
}
-/** When a listening socket gives us a new file descriptor,
- * we must associate it with a socket without creating a new
- * connection. This constructor is used for this purpose.
+/** Constructor for incoming connections
*/
TreeSocket::TreeSocket(int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
: BufferedSocket(newfd)
, linkID("inbound from " + client->addr()), LinkState(WAIT_AUTH_1), MyRoot(NULL), proto_version(0)
- , ConnectionFailureShown(false), age(ServerInstance->Time())
+ , burstsent(false), age(ServerInstance->Time())
{
capab = new CapabData;
capab->capab_phase = 0;
- if (via->iohookprov)
- via->iohookprov->OnAccept(this, client, server);
+ for (ListenSocket::IOHookProvList::iterator i = via->iohookprovs.begin(); i != via->iohookprovs.end(); ++i)
+ {
+ ListenSocket::IOHookProvRef& iohookprovref = *i;
+ if (!iohookprovref)
+ continue;
+
+ iohookprovref->OnAccept(this, client, server);
+ // IOHook could have encountered a fatal error, e.g. if the TLS ClientHello was already in the queue and there was no common TLS version
+ if (!getError().empty())
+ {
+ TreeSocket::OnError(I_ERR_OTHER);
+ return;
+ }
+ }
+
SendCapabilities(1);
Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, 30);
}
-ServerState TreeSocket::GetLinkState()
-{
- return this->LinkState;
-}
-
void TreeSocket::CleanNegotiationInfo()
{
// connect is good, reset the autoconnect block (if used)
@@ -97,10 +103,10 @@ TreeSocket::~TreeSocket()
}
/** When an outbound connection finishes connecting, we receive
- * this event, and must send our SERVER string to the other
+ * this event, and must do CAPAB negotiation with the other
* side. If the other side is happy, as outlined in the server
* to server docs on the inspircd.org site, the other side
- * will then send back its own server string.
+ * will then send back its own SERVER string eventually.
*/
void TreeSocket::OnConnected()
{
@@ -128,6 +134,7 @@ void TreeSocket::OnError(BufferedSocketError e)
ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\002%s\002' failed with error: %s",
linkID.c_str(), getError().c_str());
LinkState = DYING;
+ Close();
}
void TreeSocket::SendError(const std::string &errormessage)
@@ -138,82 +145,6 @@ void TreeSocket::SendError(const std::string &errormessage)
SetError(errormessage);
}
-/** This function forces this server to quit, removing this server
- * and any users on it (and servers and users below that, etc etc).
- * It's very slow and pretty clunky, but luckily unless your network
- * is having a REAL bad hair day, this function shouldnt be called
- * too many times a month ;-)
- */
-void TreeSocket::SquitServer(std::string &from, TreeServer* Current, int& num_lost_servers, int& num_lost_users)
-{
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "SquitServer for %s from %s", Current->GetName().c_str(), from.c_str());
- /* recursively squit the servers attached to 'Current'.
- * We're going backwards so we don't remove users
- * while we still need them ;)
- */
- const TreeServer::ChildServers& children = Current->GetChildren();
- for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
- {
- TreeServer* recursive_server = *i;
- this->SquitServer(from,recursive_server, num_lost_servers, num_lost_users);
- }
- /* Now we've whacked the kids, whack self */
- num_lost_servers++;
- num_lost_users += Current->QuitUsers(from);
-}
-
-/** This is a wrapper function for SquitServer above, which
- * does some validation first and passes on the SQUIT to all
- * other remaining servers.
- */
-void TreeSocket::Squit(TreeServer* Current, const std::string &reason)
-{
- bool LocalSquit = false;
-
- if (!Current->IsRoot())
- {
- DelServerEvent(Utils->Creator, Current->GetName());
-
- if (Current->IsLocal())
- {
- ServerInstance->SNO->WriteGlobalSno('l', "Server \002"+Current->GetName()+"\002 split: "+reason);
- LocalSquit = true;
- if (Current->GetSocket()->Introduced())
- {
- CmdBuilder params("SQUIT");
- params.push_back(Current->GetID());
- params.push_last(reason);
- params.Broadcast();
- }
- }
- else
- {
- ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
- }
- int num_lost_servers = 0;
- int num_lost_users = 0;
- std::string from = Current->GetParent()->GetName()+" "+Current->GetName();
-
- ModuleSpanningTree* st = Utils->Creator;
- st->SplitInProgress = true;
- SquitServer(from, Current, num_lost_servers, num_lost_users);
- st->SplitInProgress = false;
-
- ServerInstance->SNO->WriteToSnoMask(LocalSquit ? 'l' : 'L', "Netsplit complete, lost \002%d\002 user%s on \002%d\002 server%s.",
- num_lost_users, num_lost_users != 1 ? "s" : "", num_lost_servers, num_lost_servers != 1 ? "s" : "");
- Current->Tidy();
- Current->GetParent()->DelChild(Current);
- Current->cull();
- const bool ismyroot = (Current == MyRoot);
- delete Current;
- if (ismyroot)
- {
- MyRoot = NULL;
- Close();
- }
- }
-}
-
CmdResult CommandSQuit::HandleServer(TreeServer* server, std::vector<std::string>& params)
{
TreeServer* quitting = Utils->FindServer(params[0]);
@@ -223,15 +154,22 @@ CmdResult CommandSQuit::HandleServer(TreeServer* server, std::vector<std::string
return CMD_FAILURE;
}
- TreeSocket* sock = server->GetSocket();
- sock->Squit(quitting, params[1]);
+ CmdResult ret = CMD_SUCCESS;
+ if (quitting == server)
+ {
+ ret = CMD_FAILURE;
+ server = server->GetParent();
+ }
+ else if (quitting->GetParent() != server)
+ throw ProtocolException("Attempted to SQUIT a non-directly connected server or the parent");
+
+ server->SQuitChild(quitting, params[1]);
// XXX: Return CMD_FAILURE when servers SQUIT themselves (i.e. :00S SQUIT 00S :Shutting down)
- // to avoid RouteCommand() being called. RouteCommand() requires a valid command source but we
- // do not have one because the server user is deleted when its TreeServer is destructed.
- // We generate a SQUIT in TreeSocket::Squit(), with our sid as the source and send it to the
+ // to stop this message from being forwarded.
+ // The squit logic generates a SQUIT message with our sid as the source and sends it to the
// remaining servers.
- return ((quitting == server) ? CMD_FAILURE : CMD_SUCCESS);
+ return ret;
}
/** This function is called when we receive data from a remote
@@ -245,7 +183,7 @@ void TreeSocket::OnDataReady()
{
std::string::size_type rline = line.find('\r');
if (rline != std::string::npos)
- line = line.substr(0,rline);
+ line.erase(rline);
if (line.find('\0') != std::string::npos)
{
SendError("Read null character from socket");
@@ -270,8 +208,3 @@ void TreeSocket::OnDataReady()
SendError("RecvQ overrun (line too long)");
Utils->Creator->loopCall = false;
}
-
-bool TreeSocket::Introduced()
-{
- return (capab == NULL);
-}
diff --git a/src/modules/m_spanningtree/treesocket2.cpp b/src/modules/m_spanningtree/treesocket2.cpp
index 8d939d22a..04b850755 100644
--- a/src/modules/m_spanningtree/treesocket2.cpp
+++ b/src/modules/m_spanningtree/treesocket2.cpp
@@ -47,7 +47,7 @@ void TreeSocket::Split(const std::string& line, std::string& prefix, std::string
if (prefix[0] == ':')
{
- prefix = prefix.substr(1);
+ prefix.erase(prefix.begin());
if (prefix.empty())
{
@@ -152,13 +152,13 @@ void TreeSocket::ProcessLine(std::string &line)
time_t delta = them - ServerInstance->Time();
if ((delta < -600) || (delta > 600))
{
- ServerInstance->SNO->WriteGlobalSno('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs((long)delta));
- SendError("Your clocks are out by "+ConvToStr(abs((long)delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!");
+ ServerInstance->SNO->WriteGlobalSno('l',"\2ERROR\2: Your clocks are out by %ld seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",labs((long)delta));
+ SendError("Your clocks are out by "+ConvToStr(labs((long)delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!");
return;
}
else if ((delta < -30) || (delta > 30))
{
- ServerInstance->SNO->WriteGlobalSno('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs((long)delta));
+ ServerInstance->SNO->WriteGlobalSno('l',"\2WARNING\2: Your clocks are out by %ld seconds. Please consider synching your clocks.", labs((long)delta));
}
}
@@ -168,19 +168,7 @@ void TreeSocket::ProcessLine(std::string &line)
if (!CheckDuplicate(capab->name, capab->sid))
return;
- this->LinkState = CONNECTED;
- Utils->timeoutlist.erase(this);
-
- linkID = capab->name;
-
- MyRoot = new TreeServer(capab->name, capab->description, capab->sid, Utils->TreeRoot, this, capab->hidden);
- Utils->TreeRoot->AddChild(MyRoot);
-
- MyRoot->bursting = true;
- this->DoBurst(MyRoot);
-
- CommandServer::Builder(MyRoot).Forward(MyRoot);
- CmdBuilder(MyRoot->GetID(), "BURST").insert(params).Forward(MyRoot);
+ FinishAuth(capab->name, capab->sid, capab->description, capab->hidden);
}
else if (command == "ERROR")
{
@@ -226,46 +214,62 @@ void TreeSocket::ProcessLine(std::string &line)
}
}
-void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params)
+User* TreeSocket::FindSource(const std::string& prefix, const std::string& command)
{
- User* who = ServerInstance->FindUUID(prefix);
+ // Empty prefix means the source is the directly connected server that sent this command
+ if (prefix.empty())
+ return MyRoot->ServerUser;
- if (!who)
+ if (prefix.size() == 3)
{
- TreeServer* ServerSource = Utils->FindServer(prefix);
- if (prefix.empty())
- ServerSource = MyRoot;
+ // Prefix looks like a sid
+ TreeServer* server = Utils->FindServerID(prefix);
+ if (server)
+ return server->ServerUser;
+ }
+ else
+ {
+ // If the prefix string is a uuid FindUUID() returns the appropriate User object
+ User* user = ServerInstance->FindUUID(prefix);
+ if (user)
+ return user;
+ }
- if (ServerSource)
- {
- who = ServerSource->ServerUser;
- }
- else
- {
- /* It is important that we don't close the link here, unknown prefix can occur
- * due to various race conditions such as the KILL message for a user somehow
- * crossing the users QUIT further upstream from the server. Thanks jilles!
- */
+ // Some implementations wrongly send a server name as prefix occasionally, handle that too for now
+ TreeServer* const server = Utils->FindServer(prefix);
+ if (server)
+ return server->ServerUser;
- if ((prefix.length() == UIDGenerator::UUID_LENGTH) && (isdigit(prefix[0])) &&
- ((command == "FMODE") || (command == "MODE") || (command == "KICK") || (command == "TOPIC") || (command == "KILL") || (command == "ADDLINE") || (command == "DELLINE")))
- {
- /* Special case, we cannot drop these commands as they've been committed already on a
- * part of the network by the time we receive them, so in this scenario pretend the
- * command came from a server to avoid desync.
- */
+ /* It is important that we don't close the link here, unknown prefix can occur
+ * due to various race conditions such as the KILL message for a user somehow
+ * crossing the users QUIT further upstream from the server. Thanks jilles!
+ */
- who = ServerInstance->FindUUID(prefix.substr(0, 3));
- if (!who)
- who = this->MyRoot->ServerUser;
- }
- else
- {
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.",
- command.c_str(), prefix.c_str());
- return;
- }
- }
+ if ((prefix.length() == UIDGenerator::UUID_LENGTH) && (isdigit(prefix[0])) &&
+ ((command == "FMODE") || (command == "MODE") || (command == "KICK") || (command == "TOPIC") || (command == "KILL") || (command == "ADDLINE") || (command == "DELLINE")))
+ {
+ /* Special case, we cannot drop these commands as they've been committed already on a
+ * part of the network by the time we receive them, so in this scenario pretend the
+ * command came from a server to avoid desync.
+ */
+
+ TreeServer* const usersserver = Utils->FindServerID(prefix.substr(0, 3));
+ if (usersserver)
+ return usersserver->ServerUser;
+ return this->MyRoot->ServerUser;
+ }
+
+ // Unknown prefix
+ return NULL;
+}
+
+void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params)
+{
+ User* who = FindSource(prefix, command);
+ if (!who)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.", command.c_str(), prefix.c_str());
+ return;
}
/*
@@ -284,8 +288,8 @@ void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command,
* a valid SID or a valid UUID, so that invalid UUID or SID never makes it
* to the higher level functions. -- B
*/
- TreeServer* route_back_again = TreeServer::Get(who)->GetRoute();
- if (route_back_again->GetSocket() != this)
+ TreeServer* const server = TreeServer::Get(who);
+ if (server->GetSocket() != this)
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Protocol violation: Fake direction '%s' from connection '%s'", prefix.c_str(), linkID.c_str());
return;
@@ -304,7 +308,7 @@ void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command,
if (!scmd)
{
// Not a special server-to-server command
- cmd = ServerInstance->Parser->GetHandler(command);
+ cmd = ServerInstance->Parser.GetHandler(command);
if (!cmd)
{
if (command == "ERROR")
@@ -312,6 +316,11 @@ void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command,
this->Error(params);
return;
}
+ else if (command == "BURST")
+ {
+ // This is sent even when there is no need for it, drop it here for now
+ return;
+ }
throw ProtocolException("Unknown command");
}
@@ -340,7 +349,7 @@ void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command,
}
if (res == CMD_SUCCESS)
- Utils->RouteCommand(route_back_again, cmdbase, params, who);
+ Utils->RouteCommand(server->GetRoute(), cmdbase, params, who);
}
void TreeSocket::OnTimeout()
@@ -350,8 +359,10 @@ void TreeSocket::OnTimeout()
void TreeSocket::Close()
{
- if (fd != -1)
- ServerInstance->GlobalCulls.AddItem(this);
+ if (fd < 0)
+ return;
+
+ ServerInstance->GlobalCulls.AddItem(this);
this->BufferedSocket::Close();
SetError("Remote host closed connection");
@@ -359,18 +370,30 @@ void TreeSocket::Close()
// If the connection is fully up (state CONNECTED)
// then propogate a netsplit to all peers.
if (MyRoot)
- Squit(MyRoot,getError());
+ MyRoot->SQuit(getError());
- if (!ConnectionFailureShown)
- {
- ConnectionFailureShown = true;
- ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' failed.",linkID.c_str());
+ ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' failed.",linkID.c_str());
- time_t server_uptime = ServerInstance->Time() - this->age;
- if (server_uptime)
- {
- std::string timestr = ModuleSpanningTree::TimeToStr(server_uptime);
- ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' was established for %s", linkID.c_str(), timestr.c_str());
- }
+ time_t server_uptime = ServerInstance->Time() - this->age;
+ if (server_uptime)
+ {
+ std::string timestr = ModuleSpanningTree::TimeToStr(server_uptime);
+ ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' was established for %s", linkID.c_str(), timestr.c_str());
}
}
+
+void TreeSocket::FinishAuth(const std::string& remotename, const std::string& remotesid, const std::string& remotedesc, bool hidden)
+{
+ this->LinkState = CONNECTED;
+ Utils->timeoutlist.erase(this);
+
+ linkID = remotename;
+
+ MyRoot = new TreeServer(remotename, remotedesc, remotesid, Utils->TreeRoot, this, hidden);
+
+ // Mark the server as bursting
+ MyRoot->BeginBurst();
+ this->DoBurst(MyRoot);
+
+ CommandServer::Builder(MyRoot).Forward(MyRoot);
+}
diff --git a/src/modules/m_spanningtree/uid.cpp b/src/modules/m_spanningtree/uid.cpp
index e9e3e217d..91c9f3ca8 100644
--- a/src/modules/m_spanningtree/uid.cpp
+++ b/src/modules/m_spanningtree/uid.cpp
@@ -24,10 +24,11 @@
#include "utils.h"
#include "treeserver.h"
+#include "remoteuser.h"
CmdResult CommandUID::HandleServer(TreeServer* remoteserver, std::vector<std::string>& params)
{
- /** Do we have enough parameters:
+ /**
* 0 1 2 3 4 5 6 7 8 9 (n-1)
* UID uuid age nick host dhost ident ip.string signon +modes (modepara) :gecos
*/
@@ -36,42 +37,46 @@ CmdResult CommandUID::HandleServer(TreeServer* remoteserver, std::vector<std::st
std::string empty;
const std::string& modestr = params[8];
- /* Is this a valid UID, and not misrouted? */
+ // Check if the length of the uuid is correct and confirm the sid portion of the uuid matches the sid of the server introducing the user
if (params[0].length() != UIDGenerator::UUID_LENGTH || params[0].compare(0, 3, remoteserver->GetID()))
throw ProtocolException("Bogus UUID");
- /* Check parameters for validity before introducing the client, discovered by dmb */
+ // Sanity check on mode string: must begin with '+'
if (modestr[0] != '+')
throw ProtocolException("Invalid mode string");
- /* check for collision */
+ // See if there is a nick collision
User* collideswith = ServerInstance->FindNickOnly(params[2]);
- if (collideswith)
+ if ((collideswith) && (collideswith->registered != REG_ALL))
{
- /*
- * Nick collision.
- */
- int collide = Utils->DoCollision(collideswith, remoteserver, age_t, params[5], params[6], params[0]);
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "*** Collision on %s, collide=%d", params[2].c_str(), collide);
-
- if (collide != 1)
+ // User that the incoming user is colliding with is not fully registered, we force nick change the
+ // unregistered user to their uuid and tell them what happened
+ LocalUser* const localuser = static_cast<LocalUser*>(collideswith);
+ localuser->OverruleNick();
+ }
+ else if (collideswith)
+ {
+ // The user on this side is registered, handle the collision
+ bool they_change = Utils->DoCollision(collideswith, remoteserver, age_t, params[5], params[6], params[0], "UID");
+ if (they_change)
{
- // Remote client lost, make sure we change their nick for the hash too
+ // The client being introduced needs to change nick to uuid, change the nick in the message before
+ // processing/forwarding it. Also change the nick TS to CommandSave::SavedTimestamp.
+ age_t = CommandSave::SavedTimestamp;
+ params[1] = ConvToStr(CommandSave::SavedTimestamp);
params[2] = params[0];
}
}
- /* IMPORTANT NOTE: For remote users, we pass the UUID in the constructor. This automatically
- * sets it up in the UUID hash for us.
- *
+ /* For remote users, we pass the UUID they sent to the constructor.
* If the UUID already exists User::User() throws an exception which causes this connection to be closed.
*/
- RemoteUser* _new = new RemoteUser(params[0], remoteserver);
+ RemoteUser* _new = new SpanningTree::RemoteUser(params[0], remoteserver);
ServerInstance->Users->clientlist[params[2]] = _new;
_new->nick = params[2];
_new->host = params[3];
_new->dhost = params[4];
_new->ident = params[5];
- _new->fullname = params[params.size() - 1];
+ _new->fullname = params.back();
_new->registered = REG_ALL;
_new->signon = signon;
_new->age = age_t;
@@ -89,7 +94,7 @@ CmdResult CommandUID::HandleServer(TreeServer* remoteserver, std::vector<std::st
if (!mh)
throw ProtocolException("Unrecognised mode '" + std::string(1, *v) + "'");
- if (mh->GetNumParams(true))
+ if (mh->NeedsParam(true))
{
if (paramptr >= params.size() - 1)
throw ProtocolException("Out of parameters while processing modes");
@@ -117,7 +122,7 @@ CmdResult CommandUID::HandleServer(TreeServer* remoteserver, std::vector<std::st
bool dosend = true;
- if ((Utils->quiet_bursts && remoteserver->bursting) || _new->server->IsSilentULine())
+ if ((Utils->quiet_bursts && remoteserver->IsBehindBursting()) || _new->server->IsSilentULine())
dosend = false;
if (dosend)
@@ -157,6 +162,6 @@ CommandUID::Builder::Builder(User* user)
push(user->ident);
push(user->GetIPString());
push_int(user->signon);
- push('+').push_raw(user->FormatModes(true));
+ push(user->GetModeLetters(true));
push_last(user->fullname);
}
diff --git a/src/modules/m_spanningtree/utils.cpp b/src/modules/m_spanningtree/utils.cpp
index d6dccba14..c1c32e80a 100644
--- a/src/modules/m_spanningtree/utils.cpp
+++ b/src/modules/m_spanningtree/utils.cpp
@@ -31,7 +31,6 @@
SpanningTreeUtilities* Utils = NULL;
-/* Create server sockets off a listener. */
ModResult ModuleSpanningTree::OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
{
if (from->bind_tag->getString("type") != "servers")
@@ -52,12 +51,6 @@ ModResult ModuleSpanningTree::OnAcceptConnection(int newsock, ListenSocket* from
return MOD_RES_DENY;
}
-/** Yay for fast searches!
- * This is hundreds of times faster than recursion
- * or even scanning a linked list, especially when
- * there are more than a few servers to deal with.
- * (read as: lots).
- */
TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
{
if (InspIRCd::IsSID(ServerName))
@@ -74,37 +67,8 @@ TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
}
}
-/** Returns the locally connected server we must route a
- * message through to reach server 'ServerName'. This
- * only applies to one-to-one and not one-to-many routing.
- * See the comments for the constructor of TreeServer
- * for more details.
- */
-TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
-{
- TreeServer* Found = FindServer(ServerName);
- if (Found)
- {
- return Found->GetRoute();
- }
- else
- {
- // Cheat a bit. This allows for (better) working versions of routing commands with nick based prefixes, without hassle
- User *u = ServerInstance->FindNick(ServerName);
- if (u)
- {
- return TreeServer::Get(u)->GetRoute();
- }
-
- return NULL;
- }
-}
-
/** Find the first server matching a given glob mask.
- * Theres no find-using-glob method of hash_map [awwww :-(]
- * so instead, we iterate over the list using an iterator
- * and match each one until we get a hit. Yes its slow,
- * deal with it.
+ * We iterate over the list and match each one until we get a hit.
*/
TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName)
{
@@ -125,8 +89,22 @@ TreeServer* SpanningTreeUtilities::FindServerID(const std::string &id)
return NULL;
}
+TreeServer* SpanningTreeUtilities::FindRouteTarget(const std::string& target)
+{
+ TreeServer* const server = FindServer(target);
+ if (server)
+ return server;
+
+ User* const user = ServerInstance->FindNick(target);
+ if (user)
+ return TreeServer::Get(user);
+
+ return NULL;
+}
+
SpanningTreeUtilities::SpanningTreeUtilities(ModuleSpanningTree* C)
: Creator(C), TreeRoot(NULL)
+ , PingFreq(60) // XXX: TreeServer constructor reads this and TreeRoot is created before the config is read, so init it to something (value doesn't matter) to avoid a valgrind warning in TimerManager on unload
{
ServerInstance->Timers.AddTimer(&RefreshTimer);
}
@@ -155,7 +133,7 @@ SpanningTreeUtilities::~SpanningTreeUtilities()
delete TreeRoot;
}
-/* returns a list of DIRECT servernames for a specific channel */
+// Returns a list of DIRECT servers for a specific channel
void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeSocketSet& list, char status, const CUList& exempt_list)
{
unsigned int minrank = 0;
@@ -166,9 +144,8 @@ void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeSocketSet
minrank = mh->GetPrefixRank();
}
- const UserMembList *ulist = c->GetUsers();
-
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
if (IS_LOCAL(i->first))
continue;
@@ -201,16 +178,6 @@ void SpanningTreeUtilities::DoOneToAllButSender(const CmdBuilder& params, TreeSe
}
}
-bool SpanningTreeUtilities::DoOneToOne(const CmdBuilder& params, const std::string& target)
-{
- TreeServer* Route = this->BestRouteTo(target);
- if (!Route)
- return false;
-
- DoOneToOne(params, Route);
- return true;
-}
-
void SpanningTreeUtilities::DoOneToOne(const CmdBuilder& params, Server* server)
{
TreeServer* ts = static_cast<TreeServer*>(server);
@@ -300,31 +267,31 @@ void SpanningTreeUtilities::ReadConfiguration()
throw ModuleException("Invalid configuration, found a link tag without a name!" + (!L->IPAddr.empty() ? " IP address: "+L->IPAddr : ""));
if (L->Name.find('.') == std::string::npos)
- throw ModuleException("The link name '"+assign(L->Name)+"' is invalid as it must contain at least one '.' character");
+ throw ModuleException("The link name '"+L->Name+"' is invalid as it must contain at least one '.' character");
if (L->Name.length() > ServerInstance->Config->Limits.MaxHost)
- throw ModuleException("The link name '"+assign(L->Name)+"' is invalid as it is longer than " + ConvToStr(ServerInstance->Config->Limits.MaxHost) + " characters");
+ throw ModuleException("The link name '"+L->Name+"' is invalid as it is longer than " + ConvToStr(ServerInstance->Config->Limits.MaxHost) + " characters");
if (L->RecvPass.empty())
- throw ModuleException("Invalid configuration for server '"+assign(L->Name)+"', recvpass not defined");
+ throw ModuleException("Invalid configuration for server '"+L->Name+"', recvpass not defined");
if (L->SendPass.empty())
- throw ModuleException("Invalid configuration for server '"+assign(L->Name)+"', sendpass not defined");
+ throw ModuleException("Invalid configuration for server '"+L->Name+"', sendpass not defined");
if ((L->SendPass.find(' ') != std::string::npos) || (L->RecvPass.find(' ') != std::string::npos))
- throw ModuleException("Link block '" + assign(L->Name) + "' has a password set that contains a space character which is invalid");
+ throw ModuleException("Link block '" + L->Name + "' has a password set that contains a space character which is invalid");
if ((L->SendPass[0] == ':') || (L->RecvPass[0] == ':'))
- throw ModuleException("Link block '" + assign(L->Name) + "' has a password set that begins with a colon (:) which is invalid");
+ throw ModuleException("Link block '" + L->Name + "' has a password set that begins with a colon (:) which is invalid");
if (L->IPAddr.empty())
{
L->IPAddr = "*";
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Configuration warning: Link block '" + assign(L->Name) + "' has no IP defined! This will allow any IP to connect as this server, and MAY not be what you want.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Configuration warning: Link block '" + L->Name + "' has no IP defined! This will allow any IP to connect as this server, and MAY not be what you want.");
}
if (!L->Port)
- ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Configuration warning: Link block '" + assign(L->Name) + "' has no port defined, you will not be able to /connect it.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Configuration warning: Link block '" + L->Name + "' has no port defined, you will not be able to /connect it.");
L->Fingerprint.erase(std::remove(L->Fingerprint.begin(), L->Fingerprint.end(), ':'), L->Fingerprint.end());
LinkBlocks.push_back(L);
@@ -364,7 +331,7 @@ Link* SpanningTreeUtilities::FindLink(const std::string& name)
for (std::vector<reference<Link> >::iterator i = LinkBlocks.begin(); i != LinkBlocks.end(); ++i)
{
Link* x = *i;
- if (InspIRCd::Match(x->Name.c_str(), name.c_str(), rfc_case_insensitive_map))
+ if (InspIRCd::Match(x->Name, name, ascii_case_insensitive_map))
{
return x;
}
diff --git a/src/modules/m_spanningtree/utils.h b/src/modules/m_spanningtree/utils.h
index bc2ea24a1..a2f7212f6 100644
--- a/src/modules/m_spanningtree/utils.h
+++ b/src/modules/m_spanningtree/utils.h
@@ -25,7 +25,6 @@
#include "inspircd.h"
#include "cachetimer.h"
-/* Foward declarations */
class TreeServer;
class TreeSocket;
class Link;
@@ -36,8 +35,7 @@ class CmdBuilder;
extern SpanningTreeUtilities* Utils;
-/* This hash_map holds the hash equivalent of the server
- * tree, used for rapid linear lookups.
+/** Associative container type, mapping server names/ids to TreeServers
*/
typedef TR1NS::unordered_map<std::string, TreeServer*, irc::insensitive, irc::StrHashComp> server_hash;
@@ -120,7 +118,6 @@ class SpanningTreeUtilities : public classbase
/** Send a message from this server to one other local or remote
*/
- bool DoOneToOne(const CmdBuilder& params, const std::string& target);
void DoOneToOne(const CmdBuilder& params, Server* target);
/** Send a message from this server to all but one other, local or remote
@@ -137,13 +134,13 @@ class SpanningTreeUtilities : public classbase
/** Handle nick collision
*/
- int DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid);
+ bool DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid, const char* collidecmd);
/** Compile a list of servers which contain members of channel c
*/
void GetListOfServersForChannel(Channel* c, TreeSocketSet& list, char status, const CUList& exempt_list);
- /** Find a server by name
+ /** Find a server by name or SID
*/
TreeServer* FindServer(const std::string &ServerName);
@@ -151,9 +148,10 @@ class SpanningTreeUtilities : public classbase
*/
TreeServer* FindServerID(const std::string &id);
- /** Find a route to a server by name
+ /** Find a server based on a target string.
+ * @param target Target string where a command should be routed to. May be a server name, a sid, a nickname or a uuid.
*/
- TreeServer* BestRouteTo(const std::string &ServerName);
+ TreeServer* FindRouteTarget(const std::string& target);
/** Find a server by glob mask
*/
diff --git a/src/modules/m_sqlauth.cpp b/src/modules/m_sqlauth.cpp
index 1ffb3305a..1a5b68dd9 100644
--- a/src/modules/m_sqlauth.cpp
+++ b/src/modules/m_sqlauth.cpp
@@ -78,7 +78,9 @@ class ModuleSQLAuth : public Module
bool verbose;
public:
- ModuleSQLAuth() : pendingExt("sqlauth-wait", this), SQL(this, "SQL")
+ ModuleSQLAuth()
+ : pendingExt("sqlauth-wait", ExtensionItem::EXT_USER, this)
+ , SQL(this, "SQL")
{
}
@@ -124,11 +126,11 @@ class ModuleSQLAuth : public Module
HashProvider* md5 = ServerInstance->Modules->FindDataService<HashProvider>("hash/md5");
if (md5)
- userinfo["md5pass"] = md5->hexsum(user->password);
+ userinfo["md5pass"] = md5->Generate(user->password);
HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256");
if (sha256)
- userinfo["sha256pass"] = sha256->hexsum(user->password);
+ userinfo["sha256pass"] = sha256->Generate(user->password);
const std::string certfp = SSLClientCert::GetFingerprint(&user->eh);
userinfo["certfp"] = certfp;
diff --git a/src/modules/m_sqloper.cpp b/src/modules/m_sqloper.cpp
index fb5b65e56..b5f0d6c47 100644
--- a/src/modules/m_sqloper.cpp
+++ b/src/modules/m_sqloper.cpp
@@ -61,7 +61,7 @@ class OpMeQuery : public SQLQuery
if (!user)
return;
- Command* oper_command = ServerInstance->Parser->GetHandler("OPER");
+ Command* oper_command = ServerInstance->Parser.GetHandler("OPER");
if (oper_command)
{
@@ -78,7 +78,7 @@ class OpMeQuery : public SQLQuery
bool OperUser(User* user, const std::string &pattern, const std::string &type)
{
- OperIndex::iterator iter = ServerInstance->Config->OperTypes.find(type);
+ ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->OperTypes.find(type);
if (iter == ServerInstance->Config->OperTypes.end())
{
ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "bad type '%s' in returned row for oper %s", type.c_str(), username.c_str());
@@ -122,7 +122,7 @@ public:
SQL.SetProvider("SQL/" + dbid);
hashtype = tag->getString("hash");
- query = tag->getString("query", "SELECT hostname as host, type FROM ircd_opers WHERE username='$username' AND password='$password'");
+ query = tag->getString("query", "SELECT hostname as host, type FROM ircd_opers WHERE username='$username' AND password='$password' AND active=1;");
}
ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
@@ -147,7 +147,7 @@ public:
ParamM userinfo;
SQL->PopulateUserInfo(user, userinfo);
userinfo["username"] = username;
- userinfo["password"] = hash ? hash->hexsum(password) : password;
+ userinfo["password"] = hash ? hash->Generate(password) : password;
SQL->submit(new OpMeQuery(this, user->uuid, username, password), query, userinfo);
}
diff --git a/src/modules/m_sslinfo.cpp b/src/modules/m_sslinfo.cpp
index 656a9a432..9682e92cf 100644
--- a/src/modules/m_sslinfo.cpp
+++ b/src/modules/m_sslinfo.cpp
@@ -22,7 +22,11 @@
class SSLCertExt : public ExtensionItem {
public:
- SSLCertExt(Module* parent) : ExtensionItem("ssl_cert", parent) {}
+ SSLCertExt(Module* parent)
+ : ExtensionItem("ssl_cert", ExtensionItem::EXT_USER, parent)
+ {
+ }
+
ssl_cert* get(const Extensible* item) const
{
return static_cast<ssl_cert*>(get_raw(item));
@@ -91,7 +95,7 @@ class CommandSSLInfo : public Command
if ((!target) || (target->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nickname", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
@@ -135,14 +139,16 @@ class UserCertificateAPIImpl : public UserCertificateAPIBase
}
};
-class ModuleSSLInfo : public Module
+class ModuleSSLInfo : public Module, public Whois::EventListener
{
CommandSSLInfo cmd;
UserCertificateAPIImpl APIImpl;
public:
ModuleSSLInfo()
- : cmd(this), APIImpl(this, cmd.CertExt)
+ : Whois::EventListener(this)
+ , cmd(this)
+ , APIImpl(this, cmd.CertExt)
{
}
@@ -151,16 +157,15 @@ class ModuleSSLInfo : public Module
return Version("SSL Certificate Utilities", VF_VENDOR);
}
- void OnWhois(User* source, User* dest) CXX11_OVERRIDE
+ void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
{
- ssl_cert* cert = cmd.CertExt.get(dest);
+ ssl_cert* cert = cmd.CertExt.get(whois.GetTarget());
if (cert)
{
- ServerInstance->SendWhoisLine(source, dest, 671, "%s :is using a secure connection", dest->nick.c_str());
+ whois.SendLine(671, "is using a secure connection");
bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
- if ((!operonlyfp || source == dest || source->IsOper()) && !cert->fingerprint.empty())
- ServerInstance->SendWhoisLine(source, dest, 276, "%s :has client certificate fingerprint %s",
- dest->nick.c_str(), cert->fingerprint.c_str());
+ if ((!operonlyfp || whois.IsSelfWhois() || whois.GetSource()->IsOper()) && !cert->fingerprint.empty())
+ whois.SendLine(276, InspIRCd::Format("has client certificate fingerprint %s", cert->fingerprint.c_str()));
}
}
@@ -168,7 +173,7 @@ class ModuleSSLInfo : public Module
{
if ((command == "OPER") && (validated))
{
- OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
+ ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
if (i != ServerInstance->Config->oper_blocks.end())
{
OperInfo* ifo = i->second;
@@ -176,7 +181,7 @@ class ModuleSSLInfo : public Module
if (ifo->oper_block->getBool("sslonly") && !cert)
{
- user->WriteNumeric(491, ":This oper login requires an SSL connection.");
+ user->WriteNumeric(491, "This oper login requires an SSL connection.");
user->CommandFloodPenalty += 10000;
return MOD_RES_DENY;
}
@@ -184,7 +189,7 @@ class ModuleSSLInfo : public Module
std::string fingerprint;
if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || cert->GetFingerprint() != fingerprint))
{
- user->WriteNumeric(491, ":This oper login requires a matching SSL fingerprint.");
+ user->WriteNumeric(491, "This oper login requires a matching SSL certificate fingerprint.");
user->CommandFloodPenalty += 10000;
return MOD_RES_DENY;
}
@@ -204,11 +209,29 @@ class ModuleSSLInfo : public Module
void OnPostConnect(User* user) CXX11_OVERRIDE
{
- ssl_cert *cert = cmd.CertExt.get(user);
- if (!cert || cert->fingerprint.empty())
+ LocalUser* const localuser = IS_LOCAL(user);
+ if (!localuser)
+ return;
+
+ const SSLIOHook* const ssliohook = SSLIOHook::IsSSL(&localuser->eh);
+ if (!ssliohook)
+ return;
+
+ ssl_cert* const cert = ssliohook->GetCertificate();
+
+ {
+ std::string text = "*** You are connected using SSL cipher '";
+ ssliohook->GetCiphersuite(text);
+ text.push_back('\'');
+ if ((cert) && (!cert->GetFingerprint().empty()))
+ text.append(" and your SSL certificate fingerprint is ").append(cert->GetFingerprint());
+ user->WriteNotice(text);
+ }
+
+ if (!cert)
return;
// find an auto-oper block for this user
- for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
+ for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); ++i)
{
OperInfo* ifo = i->second;
std::string fp = ifo->oper_block->getString("fingerprint");
diff --git a/src/modules/m_sslmodes.cpp b/src/modules/m_sslmodes.cpp
index 6ac07434f..e499082ff 100644
--- a/src/modules/m_sslmodes.cpp
+++ b/src/modules/m_sslmodes.cpp
@@ -48,13 +48,13 @@ class SSLMode : public ModeHandler
if (!API)
return MODEACTION_DENY;
- const UserMembList* userlist = channel->GetUsers();
- for(UserMembCIter i = userlist->begin(); i != userlist->end(); i++)
+ const Channel::MemberMap& userlist = channel->GetUsers();
+ for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
{
ssl_cert* cert = API->GetCertificate(i->first);
if (!cert && !i->first->server->IsULine())
{
- source->WriteNumeric(ERR_ALLMUSTSSL, "%s :all members of the channel must be connected via SSL", channel->name.c_str());
+ source->WriteNumeric(ERR_ALLMUSTSSL, channel->name, "all members of the channel must be connected via SSL");
return MODEACTION_DENY;
}
}
@@ -107,7 +107,7 @@ class ModuleSSLModes : public Module
else
{
// Deny
- user->WriteNumeric(489, "%s :Cannot join channel; SSL users only (+z)", cname.c_str());
+ user->WriteNumeric(489, cname, "Cannot join channel; SSL users only (+z)");
return MOD_RES_DENY;
}
}
diff --git a/src/modules/m_starttls.cpp b/src/modules/m_starttls.cpp
index d591eed55..b3cf5a263 100644
--- a/src/modules/m_starttls.cpp
+++ b/src/modules/m_starttls.cpp
@@ -44,23 +44,23 @@ class CommandStartTLS : public SplitCommand
{
if (!ssl)
{
- user->WriteNumeric(ERR_STARTTLS, ":STARTTLS is not enabled");
+ user->WriteNumeric(ERR_STARTTLS, "STARTTLS is not enabled");
return CMD_FAILURE;
}
if (user->registered == REG_ALL)
{
- user->WriteNumeric(ERR_STARTTLS, ":STARTTLS is not permitted after client registration is complete");
+ user->WriteNumeric(ERR_STARTTLS, "STARTTLS is not permitted after client registration is complete");
return CMD_FAILURE;
}
if (user->eh.GetIOHook())
{
- user->WriteNumeric(ERR_STARTTLS, ":STARTTLS failure");
+ user->WriteNumeric(ERR_STARTTLS, "STARTTLS failure");
return CMD_FAILURE;
}
- user->WriteNumeric(RPL_STARTTLS, ":STARTTLS successful, go ahead with TLS handshake");
+ user->WriteNumeric(RPL_STARTTLS, "STARTTLS successful, go ahead with TLS handshake");
/* We need to flush the write buffer prior to adding the IOHook,
* otherwise we'll be sending this line inside the SSL session - which
* won't start its handshake until the client gets this line. Currently,
@@ -80,7 +80,7 @@ class CommandStartTLS : public SplitCommand
class ModuleStartTLS : public Module
{
CommandStartTLS starttls;
- GenericCap tls;
+ Cap::Capability tls;
dynamic_reference_nocheck<IOHookProvider> ssl;
public:
@@ -102,16 +102,6 @@ class ModuleStartTLS : public Module
ssl.SetProvider("ssl/" + newprovider);
}
- void OnEvent(Event& ev) CXX11_OVERRIDE
- {
- tls.HandleEvent(ev);
- }
-
- void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
- {
- tokens["STARTTLS"];
- }
-
Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the STARTTLS command", VF_VENDOR);
diff --git a/src/modules/m_stripcolor.cpp b/src/modules/m_stripcolor.cpp
index 0d4bdb877..592aeda90 100644
--- a/src/modules/m_stripcolor.cpp
+++ b/src/modules/m_stripcolor.cpp
@@ -83,6 +83,23 @@ class ModuleStripColor : public Module
return MOD_RES_PASSTHRU;
}
+ void OnUserPart(Membership* memb, std::string& partmessage, CUList& except_list) CXX11_OVERRIDE
+ {
+ User* user = memb->user;
+ Channel* channel = memb->chan;
+
+ if (!IS_LOCAL(user))
+ return;
+
+ bool active = channel->GetExtBanStatus(user, 'S').check(!user->IsModeSet(csc))
+ && ServerInstance->OnCheckExemption(user, channel, "stripcolor") != MOD_RES_ALLOW;
+
+ if (active)
+ {
+ InspIRCd::StripColor(partmessage);
+ }
+ }
+
Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel +S mode (strip ansi color)", VF_VENDOR);
diff --git a/src/modules/m_svshold.cpp b/src/modules/m_svshold.cpp
index af306ce62..ad6a4d1aa 100644
--- a/src/modules/m_svshold.cpp
+++ b/src/modules/m_svshold.cpp
@@ -183,22 +183,22 @@ class ModuleSVSHold : public Module
silent = tag->getBool("silent", true);
}
- ModResult OnStats(char symbol, User* user, string_list &out) CXX11_OVERRIDE
+ ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
{
- if(symbol != 'S')
+ if (stats.GetSymbol() != 'S')
return MOD_RES_PASSTHRU;
- ServerInstance->XLines->InvokeStats("SVSHOLD", 210, user, out);
+ ServerInstance->XLines->InvokeStats("SVSHOLD", 210, stats);
return MOD_RES_DENY;
}
- ModResult OnUserPreNick(User *user, const std::string &newnick) CXX11_OVERRIDE
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
XLine *rl = ServerInstance->XLines->MatchesLine("SVSHOLD", newnick);
if (rl)
{
- user->WriteNumeric(ERR_ERRONEUSNICKNAME, "%s :Services reserved nickname: %s", newnick.c_str(), rl->reason.c_str());
+ user->WriteNumeric(ERR_ERRONEUSNICKNAME, newnick, InspIRCd::Format("Services reserved nickname: %s", rl->reason.c_str()));
return MOD_RES_DENY;
}
diff --git a/src/modules/m_swhois.cpp b/src/modules/m_swhois.cpp
index 4eb2a9cda..9a433e154 100644
--- a/src/modules/m_swhois.cpp
+++ b/src/modules/m_swhois.cpp
@@ -31,7 +31,9 @@ class CommandSwhois : public Command
{
public:
StringExtItem swhois;
- CommandSwhois(Module* Creator) : Command(Creator,"SWHOIS", 2,2), swhois("swhois", Creator)
+ CommandSwhois(Module* Creator)
+ : Command(Creator, "SWHOIS", 2, 2)
+ , swhois("swhois", ExtensionItem::EXT_USER, Creator)
{
flags_needed = 'o'; syntax = "<nick> :<swhois>";
TRANSLATE2(TR_NICK, TR_TEXT);
@@ -41,9 +43,9 @@ class CommandSwhois : public Command
{
User* dest = ServerInstance->FindNick(parameters[0]);
- if ((!dest) || (IS_SERVER(dest))) // allow setting swhois using SWHOIS before reg
+ if (!dest) // allow setting swhois using SWHOIS before reg
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
@@ -79,26 +81,28 @@ class CommandSwhois : public Command
};
-class ModuleSWhois : public Module
+class ModuleSWhois : public Module, public Whois::LineEventListener
{
CommandSwhois cmd;
public:
- ModuleSWhois() : cmd(this)
+ ModuleSWhois()
+ : Whois::LineEventListener(this)
+ , cmd(this)
{
}
// :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games.
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text) CXX11_OVERRIDE
+ ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
{
/* We use this and not OnWhois because this triggers for remote, too */
- if (numeric == 312)
+ if (numeric.GetNumeric() == 312)
{
/* Insert our numeric before 312 */
- std::string* swhois = cmd.swhois.get(dest);
+ std::string* swhois = cmd.swhois.get(whois.GetTarget());
if (swhois)
{
- ServerInstance->SendWhoisLine(user, dest, 320, "%s :%s", dest->nick.c_str(), swhois->c_str());
+ whois.SendLine(320, *swhois);
}
}
diff --git a/src/modules/m_testnet.cpp b/src/modules/m_testnet.cpp
deleted file mode 100644
index 6e05ed681..000000000
--- a/src/modules/m_testnet.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-class CommandTest : public Command
-{
- public:
- CommandTest(Module* parent) : Command(parent, "TEST", 1)
- {
- syntax = "<action> <parameters>";
- }
-
- CmdResult Handle(const std::vector<std::string> &parameters, User *user)
- {
- if (parameters[0] == "flood")
- {
- unsigned int count = parameters.size() > 1 ? atoi(parameters[1].c_str()) : 100;
- std::string line = parameters.size() > 2 ? parameters[2] : ":z.z NOTICE !flood :Flood text";
- for(unsigned int i=0; i < count; i++)
- user->Write(line);
- }
- else if (parameters[0] == "freeze" && IS_LOCAL(user) && parameters.size() > 1)
- {
- IS_LOCAL(user)->CommandFloodPenalty += atoi(parameters[1].c_str());
- }
- return CMD_SUCCESS;
- }
-};
-
-class ModuleTest : public Module
-{
- CommandTest cmd;
- public:
- ModuleTest() : cmd(this)
- {
- }
-
- void init() CXX11_OVERRIDE
- {
- if (!strstr(ServerInstance->Config->ServerName.c_str(), ".test"))
- throw ModuleException("Don't load modules without reading their descriptions!");
- }
-
- Version GetVersion() CXX11_OVERRIDE
- {
- return Version("Provides a module for testing the server while linked in a network", VF_VENDOR|VF_OPTCOMMON);
- }
-};
-
-MODULE_INIT(ModuleTest)
diff --git a/src/modules/m_timedbans.cpp b/src/modules/m_timedbans.cpp
index e3a938336..874e6440f 100644
--- a/src/modules/m_timedbans.cpp
+++ b/src/modules/m_timedbans.cpp
@@ -21,15 +21,16 @@
#include "inspircd.h"
+#include "listmode.h"
/** Holds a timed ban
*/
class TimedBan
{
public:
- std::string channel;
std::string mask;
time_t expire;
+ Channel* chan;
};
typedef std::vector<TimedBan> timedbans;
@@ -39,8 +40,28 @@ timedbans TimedBanList;
*/
class CommandTban : public Command
{
+ ChanModeReference banmode;
+
+ bool IsBanSet(Channel* chan, const std::string& mask)
+ {
+ ListModeBase* banlm = static_cast<ListModeBase*>(*banmode);
+ const ListModeBase::ModeList* bans = banlm->GetList(chan);
+ if (bans)
+ {
+ for (ListModeBase::ModeList::const_iterator i = bans->begin(); i != bans->end(); ++i)
+ {
+ const ListModeBase::ListItem& ban = *i;
+ if (!strcasecmp(ban.mask.c_str(), mask.c_str()))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public:
CommandTban(Module* Creator) : Command(Creator,"TBAN", 3)
+ , banmode(Creator, "ban")
{
syntax = "<channel> <duration> <banmask>";
}
@@ -50,19 +71,17 @@ class CommandTban : public Command
Channel* channel = ServerInstance->FindChan(parameters[0]);
if (!channel)
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
return CMD_FAILURE;
}
int cm = channel->GetPrefixValue(user);
if (cm < HALFOP_VALUE)
{
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You do not have permission to set bans on this channel",
- channel->name.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You do not have permission to set bans on this channel");
return CMD_FAILURE;
}
TimedBan T;
- std::string channelname = parameters[0];
unsigned long duration = InspIRCd::Duration(parameters[1]);
unsigned long expire = duration + ServerInstance->Time();
if (duration < 1)
@@ -71,17 +90,21 @@ class CommandTban : public Command
return CMD_FAILURE;
}
std::string mask = parameters[2];
- std::vector<std::string> setban;
- setban.push_back(parameters[0]);
- setban.push_back("+b");
bool isextban = ((mask.size() > 2) && (mask[1] == ':'));
if (!isextban && !InspIRCd::IsValidMask(mask))
mask.append("!*@*");
- setban.push_back(mask);
+ if (IsBanSet(channel, mask))
+ {
+ user->WriteNotice("Ban already set");
+ return CMD_FAILURE;
+ }
+
+ Modes::ChangeList setban;
+ setban.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
// Pass the user (instead of ServerInstance->FakeClient) to ModeHandler::Process() to
// make it so that the user sets the mode themselves
- ServerInstance->Modes->Process(setban, user);
+ ServerInstance->Modes->Process(user, channel, NULL, setban);
if (ServerInstance->Modes->GetLastParse().empty())
{
user->WriteNotice("Invalid ban mask");
@@ -89,9 +112,9 @@ class CommandTban : public Command
}
CUList tmp;
- T.channel = channelname;
T.mask = mask;
T.expire = expire + (IS_REMOTE(user) ? 5 : 0);
+ T.chan = channel;
TimedBanList.push_back(T);
// If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
@@ -121,13 +144,13 @@ class BanWatcher : public ModeWatcher
if (adding)
return;
- irc::string listitem = banmask.c_str();
- irc::string thischan = chan->name.c_str();
for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); ++i)
{
- irc::string target = i->mask.c_str();
- irc::string tchan = i->channel.c_str();
- if ((listitem == target) && (tchan == thischan))
+ if (i->chan != chan)
+ continue;
+
+ const std::string& target = i->mask;
+ if (irc::equals(banmask, target))
{
TimedBanList.erase(i);
break;
@@ -136,6 +159,22 @@ class BanWatcher : public ModeWatcher
}
};
+class ChannelMatcher
+{
+ Channel* const chan;
+
+ public:
+ ChannelMatcher(Channel* ch)
+ : chan(ch)
+ {
+ }
+
+ bool operator()(const TimedBan& tb) const
+ {
+ return (tb.chan == chan);
+ }
+};
+
class ModuleTimedBans : public Module
{
CommandTban cmd;
@@ -164,26 +203,27 @@ class ModuleTimedBans : public Module
for (timedbans::iterator i = expired.begin(); i != expired.end(); i++)
{
- std::string chan = i->channel;
std::string mask = i->mask;
- Channel* cr = ServerInstance->FindChan(chan);
- if (cr)
+ Channel* cr = i->chan;
{
- std::vector<std::string> setban;
- setban.push_back(chan);
- setban.push_back("-b");
- setban.push_back(mask);
-
CUList empty;
- std::string expiry = "*** Timed ban on " + chan + " expired.";
+ std::string expiry = "*** Timed ban on " + cr->name + " expired.";
cr->WriteAllExcept(ServerInstance->FakeClient, true, '@', empty, "NOTICE %s :%s", cr->name.c_str(), expiry.c_str());
ServerInstance->PI->SendChannelNotice(cr, '@', expiry);
- ServerInstance->Modes->Process(setban, ServerInstance->FakeClient);
+ Modes::ChangeList setban;
+ setban.push_remove(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, cr, NULL, setban);
}
}
}
+ void OnChannelDelete(Channel* chan)
+ {
+ // Remove all timed bans affecting the channel from internal bookkeeping
+ TimedBanList.erase(std::remove_if(TimedBanList.begin(), TimedBanList.end(), ChannelMatcher(chan)), TimedBanList.end());
+ }
+
Version GetVersion() CXX11_OVERRIDE
{
return Version("Adds timed bans", VF_COMMON | VF_VENDOR);
diff --git a/src/modules/m_topiclock.cpp b/src/modules/m_topiclock.cpp
index 42ed6e4c1..340fbfdec 100644
--- a/src/modules/m_topiclock.cpp
+++ b/src/modules/m_topiclock.cpp
@@ -49,32 +49,13 @@ class CommandSVSTOPIC : public Command
return CMD_INVALID;
}
- std::string newtopic;
- newtopic.assign(parameters[3], 0, ServerInstance->Config->Limits.MaxTopic);
- bool topics_differ = (chan->topic != newtopic);
- if ((topics_differ) || (chan->topicset != topicts) || (chan->setby != parameters[2]))
- {
- // Update when any parameter differs
- chan->topicset = topicts;
- chan->setby.assign(parameters[2], 0, 127);
- chan->topic = newtopic;
- // Send TOPIC to clients only if the actual topic has changed, be silent otherwise
- if (topics_differ)
- chan->WriteChannel(user, "TOPIC %s :%s", chan->name.c_str(), chan->topic.c_str());
- }
+ chan->SetTopic(user, parameters[3], topicts, &parameters[2]);
}
else
{
// 1 parameter version, nuke the topic
- bool topic_empty = chan->topic.empty();
- if (!topic_empty || !chan->setby.empty())
- {
- chan->topicset = 0;
- chan->setby.clear();
- chan->topic.clear();
- if (!topic_empty)
- chan->WriteChannel(user, "TOPIC %s :", chan->name.c_str());
- }
+ chan->SetTopic(user, std::string(), 0);
+ chan->setby.clear();
}
return CMD_SUCCESS;
@@ -90,7 +71,7 @@ class FlagExtItem : public ExtensionItem
{
public:
FlagExtItem(const std::string& key, Module* owner)
- : ExtensionItem(key, owner)
+ : ExtensionItem(key, ExtensionItem::EXT_CHANNEL, owner)
{
}
@@ -150,7 +131,7 @@ class ModuleTopicLock : public Module
// Only fired for local users currently, but added a check anyway
if ((IS_LOCAL(user)) && (topiclock.get(chan)))
{
- user->WriteNumeric(744, "%s :TOPIC cannot be changed due to topic lock being active on the channel", chan->name.c_str());
+ user->WriteNumeric(744, chan->name, "TOPIC cannot be changed due to topic lock being active on the channel");
return MOD_RES_DENY;
}
diff --git a/src/modules/m_uhnames.cpp b/src/modules/m_uhnames.cpp
index 0a171c4dc..ce9c517f4 100644
--- a/src/modules/m_uhnames.cpp
+++ b/src/modules/m_uhnames.cpp
@@ -24,9 +24,9 @@
class ModuleUHNames : public Module
{
- public:
- GenericCap cap;
+ Cap::Capability cap;
+ public:
ModuleUHNames() : cap(this, "userhost-in-names")
{
}
@@ -52,7 +52,7 @@ class ModuleUHNames : public Module
{
if ((parameters.size()) && (!strcasecmp(parameters[0].c_str(),"UHNAMES")))
{
- cap.ext.set(user, 1);
+ cap.set(user, true);
return MOD_RES_DENY;
}
}
@@ -61,16 +61,11 @@ class ModuleUHNames : public Module
ModResult OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
{
- if (cap.ext.get(issuer))
+ if (cap.get(issuer))
nick = memb->user->GetFullHost();
return MOD_RES_PASSTHRU;
}
-
- void OnEvent(Event& ev) CXX11_OVERRIDE
- {
- cap.HandleEvent(ev);
- }
};
MODULE_INIT(ModuleUHNames)
diff --git a/src/modules/m_uninvite.cpp b/src/modules/m_uninvite.cpp
index 97ad841f1..d3a424dff 100644
--- a/src/modules/m_uninvite.cpp
+++ b/src/modules/m_uninvite.cpp
@@ -21,13 +21,17 @@
#include "inspircd.h"
+#include "modules/invite.h"
/** Handle /UNINVITE
*/
class CommandUninvite : public Command
{
+ Invite::API invapi;
public:
- CommandUninvite(Module* Creator) : Command(Creator,"UNINVITE", 2)
+ CommandUninvite(Module* Creator)
+ : Command(Creator, "UNINVITE", 2)
+ , invapi(Creator)
{
syntax = "<nick> <channel>";
TRANSLATE2(TR_NICK, TR_TEXT);
@@ -47,11 +51,11 @@ class CommandUninvite : public Command
{
if (!c)
{
- user->WriteNumeric(401, "%s :No such nick/channel", parameters[1].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[1]));
}
else
{
- user->WriteNumeric(401, "%s :No such nick/channel", parameters[0].c_str());
+ user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
}
return CMD_FAILURE;
@@ -61,7 +65,7 @@ class CommandUninvite : public Command
{
if (c->GetPrefixValue(user) < HALFOP_VALUE)
{
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be a channel %soperator", c->name.c_str(), c->GetPrefixValue(u) == HALFOP_VALUE ? "" : "half-");
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, InspIRCd::Format("You must be a channel %soperator", c->GetPrefixValue(u) == HALFOP_VALUE ? "" : "half-"));
return CMD_FAILURE;
}
}
@@ -73,17 +77,26 @@ class CommandUninvite : public Command
LocalUser* lu = IS_LOCAL(u);
if (lu)
{
- if (!lu->RemoveInvite(c))
+ // XXX: The source of the numeric we send must be the server of the user doing the /UNINVITE,
+ // so they don't see where the target user is connected to
+ if (!invapi->Remove(lu, c))
{
- user->SendText(":%s 505 %s %s %s :Is not invited to channel %s", user->server->GetName().c_str(), user->nick.c_str(), u->nick.c_str(), c->name.c_str(), c->name.c_str());
+ Numeric::Numeric n(505);
+ n.SetServer(user->server);
+ n.push(u->nick).push(c->name).push(InspIRCd::Format("Is not invited to channel %s", c->name.c_str()));
+ user->WriteRemoteNumeric(n);
return CMD_FAILURE;
}
- user->SendText(":%s 494 %s %s %s :Uninvited", user->server->GetName().c_str(), user->nick.c_str(), c->name.c_str(), u->nick.c_str());
- lu->WriteNumeric(493, ":You were uninvited from %s by %s", c->name.c_str(), user->nick.c_str());
+ Numeric::Numeric n(494);
+ n.SetServer(user->server);
+ n.push(c->name).push(u->nick).push("Uninvited");
+ user->WriteRemoteNumeric(n);
+
+ lu->WriteNumeric(493, InspIRCd::Format("You were uninvited from %s by %s", c->name.c_str(), user->nick.c_str()));
std::string msg = "*** " + user->nick + " uninvited " + u->nick + ".";
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE " + c->name + " :" + msg);
+ c->WriteNotice(msg);
ServerInstance->PI->SendChannelNotice(c, 0, msg);
}
@@ -92,8 +105,7 @@ class CommandUninvite : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- User* u = ServerInstance->FindNick(parameters[0]);
- return u ? ROUTE_OPT_UCAST(u->server) : ROUTE_LOCALONLY;
+ return ROUTE_OPT_UCAST(parameters[0]);
}
};
diff --git a/src/modules/m_userip.cpp b/src/modules/m_userip.cpp
index 96505a047..6fa367bff 100644
--- a/src/modules/m_userip.cpp
+++ b/src/modules/m_userip.cpp
@@ -28,12 +28,12 @@ class CommandUserip : public Command
public:
CommandUserip(Module* Creator) : Command(Creator,"USERIP", 1)
{
- syntax = "<nick>{,<nick>}";
+ syntax = "<nick> [<nick> ...]";
}
CmdResult Handle (const std::vector<std::string> &parameters, User *user)
{
- std::string retbuf = "340 " + user->nick + " :";
+ std::string retbuf;
int nicks = 0;
bool checked_privs = false;
bool has_privs = false;
@@ -52,7 +52,7 @@ class CommandUserip : public Command
checked_privs = true;
has_privs = user->HasPrivPermission("users/auspex");
if (!has_privs)
- user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - You do not have the required operator privileges");
+ user->WriteNumeric(ERR_NOPRIVILEGES, "Permission Denied - You do not have the required operator privileges");
}
if (!has_privs)
@@ -70,7 +70,7 @@ class CommandUserip : public Command
}
if (nicks != 0)
- user->WriteServ(retbuf);
+ user->WriteNumeric(RPL_USERIP, retbuf);
return CMD_SUCCESS;
}
diff --git a/src/modules/m_watch.cpp b/src/modules/m_watch.cpp
index 57ca18a8f..6e9a6f9ed 100644
--- a/src/modules/m_watch.cpp
+++ b/src/modules/m_watch.cpp
@@ -1,10 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2005-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
@@ -22,507 +19,250 @@
#include "inspircd.h"
+#define INSPIRCD_MONITOR_MANAGER_ONLY
+#include "m_monitor.cpp"
-/*
- * Okay, it's nice that this was documented and all, but I at least understood very little
- * of it, so I'm going to attempt to explain the data structures in here a bit more.
- *
- * For efficiency, many data structures are kept.
- *
- * The first is a global list `watchentries':
- * hash_map<irc::string, std::deque<User*> >
- *
- * That is, if nick 'w00t' is being watched by user pointer 'Brain' and 'Om', <w00t, (Brain, Om)>
- * will be in the watchentries list.
- *
- * The second is that each user has a per-user data structure attached to their user record via Extensible:
- * std::map<irc::string, std::string> watchlist;
- * So, in the above example with w00t watched by Brain and Om, we'd have:
- * Brain-
- * `- w00t
- * Om-
- * `- w00t
- *
- * Hopefully this helps any brave soul that ventures into this file other than me. :-)
- * -- w00t (mar 30, 2008)
- */
-
-
-/* This module has been refactored to provide a very efficient (in terms of cpu time)
- * implementation of /WATCH.
- *
- * To improve the efficiency of watch, many lists are kept. The first primary list is
- * a hash_map of who's being watched by who. For example:
- *
- * KEY: Brain ---> Watched by: Boo, w00t, Om
- * KEY: Boo ---> Watched by: Brain, w00t
- *
- * This is used when we want to tell all the users that are watching someone that
- * they are now available or no longer available. For example, if the hash was
- * populated as shown above, then when Brain signs on, messages are sent to Boo, w00t
- * and Om by reading their 'watched by' list. When this occurs, their online status
- * in each of these users lists (see below) is also updated.
- *
- * Each user also has a seperate (smaller) map attached to their User whilst they
- * have any watch entries, which is managed by class Extensible. When they add or remove
- * a watch entry from their list, it is inserted here, as well as the main list being
- * maintained. This map also contains the user's online status. For users that are
- * offline, the key points at an empty string, and for users that are online, the key
- * points at a string containing "users-ident users-host users-signon-time". This is
- * stored in this manner so that we don't have to FindUser() to fetch this info, the
- * users signon can populate the field for us.
- *
- * For example, going again on the example above, this would be w00t's watchlist:
- *
- * KEY: Boo ---> Status: "Boo brains.sexy.babe 535342348"
- * KEY: Brain ---> Status: ""
- *
- * In this list we can see that Boo is online, and Brain is offline. We can then
- * use this list for 'WATCH L', and 'WATCH S' can be implemented as a combination
- * of the above two data structures, with minimum CPU penalty for doing so.
- *
- * In short, the least efficient this ever gets is O(n), and thats only because
- * there are parts that *must* loop (e.g. telling all users that are watching a
- * nick that the user online), however this is a *major* improvement over the
- * 1.0 implementation, which in places had O(n^n) and worse in it, because this
- * implementation scales based upon the sizes of the watch entries, whereas the
- * old system would scale (or not as the case may be) according to the total number
- * of users using WATCH.
- */
+enum
+{
+ RPL_GONEAWAY = 598,
+ RPL_NOTAWAY = 599,
+ RPL_LOGON = 600,
+ RPL_LOGOFF = 601,
+ RPL_WATCHOFF = 602,
+ RPL_WATCHSTAT = 603,
+ RPL_NOWON = 604,
+ RPL_NOWOFF = 605,
+ RPL_WATCHLIST = 606,
+ RPL_ENDOFWATCHLIST = 607,
+ // RPL_CLEARWATCH = 608, // unused
+ RPL_NOWISAWAY = 609,
+ ERR_TOOMANYWATCH = 512
+};
-typedef TR1NS::unordered_map<irc::string, std::deque<User*>, irc::hash> watchentries;
-typedef std::map<irc::string, std::string> watchlist;
+class CommandWatch : public SplitCommand
+{
+ // Additional penalty for /WATCH commands that request a list from the server
+ static const unsigned int ListPenalty = 4000;
-/* Who's watching each nickname.
- * NOTE: We do NOT iterate this to display a user's WATCH list!
- * See the comments above!
- */
-watchentries* whos_watching_me;
+ IRCv3::Monitor::Manager& manager;
-class CommandSVSWatch : public Command
-{
- public:
- CommandSVSWatch(Module* Creator) : Command(Creator,"SVSWATCH", 2)
+ static void SendOnlineOffline(LocalUser* user, const std::string& nick, bool show_offline = true)
{
- syntax = "<target> [C|L|S]|[+|-<nick>]";
- TRANSLATE2(TR_NICK, TR_TEXT); /* we watch for a nick. not a UID. */
+ User* target = IRCv3::Monitor::Manager::FindNick(nick);
+ if (target)
+ {
+ // The away state should only be sent if the client requests away notifications for a nick but 2.0 always sends them so we do that too
+ if (target->IsAway())
+ user->WriteNumeric(RPL_NOWISAWAY, target->nick, target->ident, target->dhost, (unsigned long)target->awaytime, "is away");
+ else
+ user->WriteNumeric(RPL_NOWON, target->nick, target->ident, target->dhost, (unsigned long)target->age, "is online");
+ }
+ else if (show_offline)
+ user->WriteNumeric(RPL_NOWOFF, nick, "*", "*", "0", "is offline");
}
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+ void HandlePlus(LocalUser* user, const std::string& nick)
{
- if (!user->server->IsULine())
- return CMD_FAILURE;
-
- User *u = ServerInstance->FindNick(parameters[0]);
- if (!u)
- return CMD_FAILURE;
-
- if (IS_LOCAL(u))
+ IRCv3::Monitor::Manager::WatchResult result = manager.Watch(user, nick, maxwatch);
+ if (result == IRCv3::Monitor::Manager::WR_TOOMANY)
+ {
+ // List is full, send error numeric
+ user->WriteNumeric(ERR_TOOMANYWATCH, nick, "Too many WATCH entries");
+ return;
+ }
+ else if (result == IRCv3::Monitor::Manager::WR_INVALIDNICK)
{
- ServerInstance->Parser->CallHandler("WATCH", parameters, u);
+ user->WriteNumeric(942, nick, "Invalid nickname");
+ return;
}
+ else if (result != IRCv3::Monitor::Manager::WR_OK)
+ return;
- return CMD_SUCCESS;
+ SendOnlineOffline(user, nick);
}
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ void HandleMinus(LocalUser* user, const std::string& nick)
{
- User* target = ServerInstance->FindNick(parameters[0]);
+ if (!manager.Unwatch(user, nick))
+ return;
+
+ User* target = IRCv3::Monitor::Manager::FindNick(nick);
if (target)
- return ROUTE_OPT_UCAST(target->server);
- return ROUTE_LOCALONLY;
+ user->WriteNumeric(RPL_WATCHOFF, target->nick, target->ident, target->dhost, (unsigned long)target->age, "stopped watching");
+ else
+ user->WriteNumeric(RPL_WATCHOFF, nick, "*", "*", "0", "stopped watching");
}
-};
-/** Handle /WATCH
- */
-class CommandWatch : public Command
-{
- unsigned int& MAX_WATCH;
- public:
- SimpleExtItem<watchlist> ext;
- CmdResult remove_watch(User* user, const char* nick)
+ void HandleList(LocalUser* user, bool show_offline)
{
- // removing an item from the list
- if (!ServerInstance->IsNick(nick))
+ user->CommandFloodPenalty += ListPenalty;
+ const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+ for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
{
- user->WriteNumeric(942, "%s :Invalid nickname", nick);
- return CMD_FAILURE;
+ const IRCv3::Monitor::Entry* entry = *i;
+ SendOnlineOffline(user, entry->GetNick(), show_offline);
}
+ user->WriteNumeric(RPL_ENDOFWATCHLIST, "End of WATCH list");
+ }
- watchlist* wl = ext.get(user);
- if (wl)
- {
- /* Yup, is on my list */
- watchlist::iterator n = wl->find(nick);
-
- if (!wl)
- return CMD_FAILURE;
-
- if (n != wl->end())
- {
- if (!n->second.empty())
- user->WriteNumeric(602, "%s %s :stopped watching", n->first.c_str(), n->second.c_str());
- else
- user->WriteNumeric(602, "%s * * 0 :stopped watching", nick);
-
- wl->erase(n);
- }
+ void HandleStats(LocalUser* user)
+ {
+ user->CommandFloodPenalty += ListPenalty;
- if (wl->empty())
- {
- ext.unset(user);
- }
+ // Do not show how many clients are watching this nick, it's pointless
+ const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
+ user->WriteNumeric(RPL_WATCHSTAT, InspIRCd::Format("You have %lu and are on 0 WATCH entries", (unsigned long)list.size()));
- watchentries::iterator x = whos_watching_me->find(nick);
- if (x != whos_watching_me->end())
- {
- /* People are watching this user, am i one of them? */
- std::deque<User*>::iterator n2 = std::find(x->second.begin(), x->second.end(), user);
- if (n2 != x->second.end())
- /* I'm no longer watching you... */
- x->second.erase(n2);
-
- if (x->second.empty())
- /* nobody else is, either. */
- whos_watching_me->erase(nick);
- }
+ Numeric::Builder<' '> out(user, RPL_WATCHLIST);
+ for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ const IRCv3::Monitor::Entry* entry = *i;
+ out.Add(entry->GetNick());
}
-
- return CMD_SUCCESS;
+ out.Flush();
+ user->WriteNumeric(RPL_ENDOFWATCHLIST, "End of WATCH S");
}
- CmdResult add_watch(User* user, const char* nick)
+ public:
+ unsigned int maxwatch;
+
+ CommandWatch(Module* mod, IRCv3::Monitor::Manager& managerref)
+ : SplitCommand(mod, "WATCH")
+ , manager(managerref)
{
- if (!ServerInstance->IsNick(nick))
- {
- user->WriteNumeric(942, "%s :Invalid nickname", nick);
- return CMD_FAILURE;
- }
+ allow_empty_last_param = false;
+ syntax = "[<C|L|S|l|+<nick1>|-<nick>>]";
+ }
- watchlist* wl = ext.get(user);
- if (!wl)
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+ {
+ if (parameters.empty())
{
- wl = new watchlist();
- ext.set(user, wl);
+ HandleList(user, false);
+ return CMD_SUCCESS;
}
- if (wl->size() == MAX_WATCH)
- {
- user->WriteNumeric(512, "%s :Too many WATCH entries", nick);
- return CMD_FAILURE;
- }
+ bool watch_l_done = false;
+ bool watch_s_done = false;
- watchlist::iterator n = wl->find(nick);
- if (n == wl->end())
+ for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end(); ++i)
{
- /* Don't already have the user on my watch list, proceed */
- watchentries::iterator x = whos_watching_me->find(nick);
- if (x != whos_watching_me->end())
+ const std::string& token = *i;
+ char subcmd = toupper(token[0]);
+ if (subcmd == '+')
{
- /* People are watching this user, add myself */
- x->second.push_back(user);
+ HandlePlus(user, token.substr(1));
}
- else
+ else if (subcmd == '-')
{
- std::deque<User*> newlist;
- newlist.push_back(user);
- (*(whos_watching_me))[nick] = newlist;
+ HandleMinus(user, token.substr(1));
}
-
- User* target = ServerInstance->FindNick(nick);
- if ((target) && (target->registered == REG_ALL))
+ else if (subcmd == 'C')
{
- (*wl)[nick] = std::string(target->ident).append(" ").append(target->dhost).append(" ").append(ConvToStr(target->age));
- user->WriteNumeric(604, "%s %s :is online", nick, (*wl)[nick].c_str());
- if (target->IsAway())
- {
- user->WriteNumeric(609, "%s %s %s %lu :is away", target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long) target->awaytime);
- }
+ manager.UnwatchAll(user);
}
- else
+ else if ((subcmd == 'L') && (!watch_l_done))
{
- (*wl)[nick].clear();
- user->WriteNumeric(605, "%s * * 0 :is offline", nick);
+ watch_l_done = true;
+ // WATCH L requests a full list with online and offline nicks
+ // WATCH l requests a list with only online nicks
+ HandleList(user, (token[0] == 'L'));
}
- }
-
- return CMD_SUCCESS;
- }
-
- CommandWatch(Module* parent, unsigned int &maxwatch) : Command(parent,"WATCH", 0), MAX_WATCH(maxwatch), ext("watchlist", parent)
- {
- syntax = "[C|L|S]|[+|-<nick>]";
- }
-
- CmdResult Handle (const std::vector<std::string> &parameters, User *user)
- {
- if (parameters.empty())
- {
- watchlist* wl = ext.get(user);
- if (wl)
+ else if ((subcmd == 'S') && (!watch_s_done))
{
- for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
- {
- if (!q->second.empty())
- user->WriteNumeric(604, "%s %s :is online", q->first.c_str(), q->second.c_str());
- }
- }
- user->WriteNumeric(607, ":End of WATCH list");
- }
- else if (parameters.size() > 0)
- {
- for (int x = 0; x < (int)parameters.size(); x++)
- {
- const char *nick = parameters[x].c_str();
- if (!strcasecmp(nick,"C"))
- {
- // watch clear
- watchlist* wl = ext.get(user);
- if (wl)
- {
- for (watchlist::iterator i = wl->begin(); i != wl->end(); i++)
- {
- watchentries::iterator i2 = whos_watching_me->find(i->first);
- if (i2 != whos_watching_me->end())
- {
- /* People are watching this user, am i one of them? */
- std::deque<User*>::iterator n = std::find(i2->second.begin(), i2->second.end(), user);
- if (n != i2->second.end())
- /* I'm no longer watching you... */
- i2->second.erase(n);
-
- if (i2->second.empty())
- /* nobody else is, either. */
- whos_watching_me->erase(i2);
- }
- }
-
- ext.unset(user);
- }
- }
- else if (!strcasecmp(nick,"L"))
- {
- watchlist* wl = ext.get(user);
- if (wl)
- {
- for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
- {
- User* targ = ServerInstance->FindNick(q->first.c_str());
- if (targ && !q->second.empty())
- {
- user->WriteNumeric(604, "%s %s :is online", q->first.c_str(), q->second.c_str());
- if (targ->IsAway())
- {
- user->WriteNumeric(609, "%s %s %s %lu :is away", targ->nick.c_str(), targ->ident.c_str(), targ->dhost.c_str(), (unsigned long) targ->awaytime);
- }
- }
- else
- user->WriteNumeric(605, "%s * * 0 :is offline", q->first.c_str());
- }
- }
- user->WriteNumeric(607, ":End of WATCH list");
- }
- else if (!strcasecmp(nick,"S"))
- {
- watchlist* wl = ext.get(user);
- int you_have = 0;
- int youre_on = 0;
- std::string list;
-
- if (wl)
- {
- for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
- list.append(q->first.c_str()).append(" ");
- you_have = wl->size();
- }
-
- watchentries::iterator i2 = whos_watching_me->find(user->nick.c_str());
- if (i2 != whos_watching_me->end())
- youre_on = i2->second.size();
-
- user->WriteNumeric(603, ":You have %d and are on %d WATCH entries", you_have, youre_on);
- user->WriteNumeric(606, ":%s", list.c_str());
- user->WriteNumeric(607, ":End of WATCH S");
- }
- else if (nick[0] == '-')
- {
- nick++;
- remove_watch(user, nick);
- }
- else if (nick[0] == '+')
- {
- nick++;
- add_watch(user, nick);
- }
+ watch_s_done = true;
+ HandleStats(user);
}
}
return CMD_SUCCESS;
}
};
-class Modulewatch : public Module
+class ModuleWatch : public Module
{
- unsigned int maxwatch;
- CommandWatch cmdw;
- CommandSVSWatch sw;
+ IRCv3::Monitor::Manager manager;
+ CommandWatch cmd;
- public:
- Modulewatch()
- : maxwatch(32), cmdw(this, maxwatch), sw(this)
+ void SendAlert(User* user, const std::string& nick, unsigned int numeric, const char* numerictext, time_t shownts)
{
- whos_watching_me = new watchentries();
+ const IRCv3::Monitor::WatcherList* list = manager.GetWatcherList(nick);
+ if (!list)
+ return;
+
+ Numeric::Numeric num(numeric);
+ num.push(nick).push(user->ident).push(user->dhost).push(ConvToStr(shownts)).push(numerictext);
+ for (IRCv3::Monitor::WatcherList::const_iterator i = list->begin(); i != list->end(); ++i)
+ {
+ LocalUser* curr = *i;
+ curr->WriteNumeric(num);
+ }
}
- void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ void Online(User* user)
{
- maxwatch = ServerInstance->Config->ConfValue("watch")->getInt("maxentries", 32);
- if (!maxwatch)
- maxwatch = 32;
+ SendAlert(user, user->nick, RPL_LOGON, "arrived online", user->age);
}
- ModResult OnSetAway(User *user, const std::string &awaymsg) CXX11_OVERRIDE
+ void Offline(User* user, const std::string& nick)
{
- std::string numeric;
- int inum;
-
- if (awaymsg.empty())
- {
- numeric = user->nick + " " + user->ident + " " + user->dhost + " " + ConvToStr(ServerInstance->Time()) + " :is no longer away";
- inum = 599;
- }
- else
- {
- numeric = user->nick + " " + user->ident + " " + user->dhost + " " + ConvToStr(ServerInstance->Time()) + " :" + awaymsg;
- inum = 598;
- }
-
- watchentries::iterator x = whos_watching_me->find(user->nick.c_str());
- if (x != whos_watching_me->end())
- {
- for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
- {
- (*n)->WriteNumeric(inum, numeric);
- }
- }
-
- return MOD_RES_PASSTHRU;
+ SendAlert(user, nick, RPL_LOGOFF, "went offline", user->age);
}
- void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
+ public:
+ ModuleWatch()
+ : manager(this, "watch")
+ , cmd(this, manager)
{
- watchentries::iterator x = whos_watching_me->find(user->nick.c_str());
- if (x != whos_watching_me->end())
- {
- for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
- {
- (*n)->WriteNumeric(601, "%s %s %s %lu :went offline", user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) ServerInstance->Time());
-
- watchlist* wl = cmdw.ext.get(*n);
- if (wl)
- /* We were on somebody's notify list, set ourselves offline */
- (*wl)[user->nick.c_str()].clear();
- }
- }
-
- /* Now im quitting, if i have a notify list, im no longer watching anyone */
- watchlist* wl = cmdw.ext.get(user);
- if (wl)
- {
- /* Iterate every user on my watch list, and take me out of the whos_watching_me map for each one we're watching */
- for (watchlist::iterator i = wl->begin(); i != wl->end(); i++)
- {
- watchentries::iterator i2 = whos_watching_me->find(i->first);
- if (i2 != whos_watching_me->end())
- {
- /* People are watching this user, am i one of them? */
- std::deque<User*>::iterator n = std::find(i2->second.begin(), i2->second.end(), user);
- if (n != i2->second.end())
- /* I'm no longer watching you... */
- i2->second.erase(n);
-
- if (i2->second.empty())
- /* and nobody else is, either. */
- whos_watching_me->erase(i2);
- }
- }
- }
}
- void OnGarbageCollect()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- watchentries* old_watch = whos_watching_me;
- whos_watching_me = new watchentries();
-
- for (watchentries::const_iterator n = old_watch->begin(); n != old_watch->end(); n++)
- whos_watching_me->insert(*n);
-
- delete old_watch;
+ ConfigTag* tag = ServerInstance->Config->ConfValue("watch");
+ cmd.maxwatch = tag->getInt("maxwatch", 30, 1);
}
void OnPostConnect(User* user) CXX11_OVERRIDE
{
- watchentries::iterator x = whos_watching_me->find(user->nick.c_str());
- if (x != whos_watching_me->end())
- {
- for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
- {
- (*n)->WriteNumeric(600, "%s %s %s %lu :arrived online", user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
-
- watchlist* wl = cmdw.ext.get(*n);
- if (wl)
- /* We were on somebody's notify list, set ourselves online */
- (*wl)[user->nick.c_str()] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age));
- }
- }
+ Online(user);
}
- void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
+ void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE
{
- watchentries::iterator new_offline = whos_watching_me->find(oldnick.c_str());
- watchentries::iterator new_online = whos_watching_me->find(user->nick.c_str());
+ // Detect and ignore nickname case change
+ if (ServerInstance->FindNickOnly(oldnick) == user)
+ return;
- if (new_offline != whos_watching_me->end())
- {
- for (std::deque<User*>::iterator n = new_offline->second.begin(); n != new_offline->second.end(); n++)
- {
- watchlist* wl = cmdw.ext.get(*n);
- if (wl)
- {
- (*n)->WriteNumeric(601, "%s %s %s %lu :went offline", oldnick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
- (*wl)[oldnick.c_str()].clear();
- }
- }
- }
+ Offline(user, oldnick);
+ Online(user);
+ }
- if (new_online != whos_watching_me->end())
- {
- for (std::deque<User*>::iterator n = new_online->second.begin(); n != new_online->second.end(); n++)
- {
- watchlist* wl = cmdw.ext.get(*n);
- if (wl)
- {
- (*wl)[user->nick.c_str()] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age));
- (*n)->WriteNumeric(600, "%s %s :arrived online", user->nick.c_str(), (*wl)[user->nick.c_str()].c_str());
- }
- }
- }
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
+ {
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ manager.UnwatchAll(localuser);
+ Offline(user, user->nick);
}
- void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ ModResult OnSetAway(User* user, const std::string& awaymsg) CXX11_OVERRIDE
{
- tokens["WATCH"] = ConvToStr(maxwatch);
+ if (awaymsg.empty())
+ SendAlert(user, user->nick, RPL_NOTAWAY, "is no longer away", ServerInstance->Time());
+ else
+ SendAlert(user, user->nick, RPL_GONEAWAY, awaymsg.c_str(), user->awaytime);
+
+ return MOD_RES_PASSTHRU;
}
- ~Modulewatch()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- delete whos_watching_me;
+ tokens["WATCH"] = ConvToStr(cmd.maxwatch);
}
Version GetVersion() CXX11_OVERRIDE
{
- return Version("Provides support for the /WATCH command", VF_OPTCOMMON | VF_VENDOR);
+ return Version("Provides WATCH support", VF_VENDOR);
}
};
-MODULE_INIT(Modulewatch)
+MODULE_INIT(ModuleWatch)
diff --git a/src/modules/m_websocket.cpp b/src/modules/m_websocket.cpp
new file mode 100644
index 000000000..399b0b017
--- /dev/null
+++ b/src/modules/m_websocket.cpp
@@ -0,0 +1,405 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "iohook.h"
+#include "modules/hash.h"
+
+static const char MagicGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+static const char whitespace[] = " \t\r\n";
+static dynamic_reference_nocheck<HashProvider>* sha1;
+
+class WebSocketHookProvider : public IOHookProvider
+{
+ public:
+ WebSocketHookProvider(Module* mod)
+ : IOHookProvider(mod, "websocket", IOHookProvider::IOH_UNKNOWN, true)
+ {
+ }
+
+ void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
+
+ void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ }
+};
+
+class WebSocketHook : public IOHookMiddle
+{
+ class HTTPHeaderFinder
+ {
+ std::string::size_type bpos;
+ std::string::size_type len;
+
+ public:
+ bool Find(const std::string& req, const char* header, std::string::size_type headerlen, std::string::size_type maxpos)
+ {
+ std::string::size_type keybegin = req.find(header);
+ if ((keybegin == std::string::npos) || (keybegin > maxpos) || (keybegin == 0) || (req[keybegin-1] != '\n'))
+ return false;
+
+ keybegin += headerlen;
+
+ bpos = req.find_first_not_of(whitespace, keybegin, sizeof(whitespace)-1);
+ if ((bpos == std::string::npos) || (bpos > maxpos))
+ return false;
+
+ const std::string::size_type epos = req.find_first_of(whitespace, bpos, sizeof(whitespace)-1);
+ len = epos - bpos;
+
+ return true;
+ }
+
+ std::string ExtractValue(const std::string& req) const
+ {
+ return std::string(req, bpos, len);
+ }
+ };
+
+ enum OpCode
+ {
+ OP_CONTINUATION = 0x00,
+ OP_TEXT = 0x01,
+ OP_BINARY = 0x02,
+ OP_CLOSE = 0x08,
+ OP_PING = 0x09,
+ OP_PONG = 0x0a
+ };
+
+ enum State
+ {
+ STATE_HTTPREQ,
+ STATE_ESTABLISHED
+ };
+
+ static const unsigned char WS_MASKBIT = (1 << 7);
+ static const unsigned char WS_FINBIT = (1 << 7);
+ static const unsigned char WS_PAYLOAD_LENGTH_MAGIC_LARGE = 126;
+ static const unsigned char WS_PAYLOAD_LENGTH_MAGIC_HUGE = 127;
+ static const size_t WS_MAX_PAYLOAD_LENGTH_SMALL = 125;
+ static const size_t WS_MAX_PAYLOAD_LENGTH_LARGE = 65535;
+ static const size_t MAXHEADERSIZE = sizeof(uint64_t) + 2;
+
+ // Clients sending ping or pong frames faster than this are killed
+ static const time_t MINPINGPONGDELAY = 10;
+
+ State state;
+ time_t lastpingpong;
+
+ static size_t FillHeader(unsigned char* outbuf, size_t sendlength, OpCode opcode)
+ {
+ size_t pos = 0;
+ outbuf[pos++] = WS_FINBIT | opcode;
+
+ if (sendlength <= WS_MAX_PAYLOAD_LENGTH_SMALL)
+ {
+ outbuf[pos++] = sendlength;
+ }
+ else if (sendlength <= WS_MAX_PAYLOAD_LENGTH_LARGE)
+ {
+ outbuf[pos++] = WS_PAYLOAD_LENGTH_MAGIC_LARGE;
+ outbuf[pos++] = (sendlength >> 8) & 0xff;
+ outbuf[pos++] = sendlength & 0xff;
+ }
+ else
+ {
+ outbuf[pos++] = WS_PAYLOAD_LENGTH_MAGIC_HUGE;
+ const uint64_t len = sendlength;
+ for (int i = sizeof(uint64_t)-1; i >= 0; i--)
+ outbuf[pos++] = ((len >> i*8) & 0xff);
+ }
+
+ return pos;
+ }
+
+ static StreamSocket::SendQueue::Element PrepareSendQElem(size_t size, OpCode opcode)
+ {
+ unsigned char header[MAXHEADERSIZE];
+ const size_t n = FillHeader(header, size, opcode);
+
+ return StreamSocket::SendQueue::Element(reinterpret_cast<const char*>(header), n);
+ }
+
+ int HandleAppData(StreamSocket* sock, std::string& appdataout, bool allowlarge)
+ {
+ std::string& myrecvq = GetRecvQ();
+ // Need 1 byte opcode, minimum 1 byte len, 4 bytes masking key
+ if (myrecvq.length() < 6)
+ return 0;
+
+ const std::string& cmyrecvq = myrecvq;
+ unsigned char len1 = (unsigned char)cmyrecvq[1];
+ if (!(len1 & WS_MASKBIT))
+ {
+ sock->SetError("WebSocket protocol violation: unmasked client frame");
+ return -1;
+ }
+
+ len1 &= ~WS_MASKBIT;
+
+ // Assume the length is a single byte, if not, update values later
+ unsigned int len = len1;
+ unsigned int payloadstartoffset = 6;
+ const unsigned char* maskkey = reinterpret_cast<const unsigned char*>(&cmyrecvq[2]);
+
+ if (len1 == WS_PAYLOAD_LENGTH_MAGIC_LARGE)
+ {
+ // allowlarge is false for control frames according to the RFC meaning large pings, etc. are not allowed
+ if (!allowlarge)
+ {
+ sock->SetError("WebSocket protocol violation: large control frame");
+ return -1;
+ }
+
+ // Large frame, has 2 bytes len after the magic byte indicating the length
+ // Need 1 byte opcode, 3 bytes len, 4 bytes masking key
+ if (myrecvq.length() < 8)
+ return 0;
+
+ unsigned char len2 = (unsigned char)cmyrecvq[2];
+ unsigned char len3 = (unsigned char)cmyrecvq[3];
+ len = (len2 << 8) | len3;
+
+ if (len <= WS_MAX_PAYLOAD_LENGTH_SMALL)
+ {
+ sock->SetError("WebSocket protocol violation: non-minimal length encoding used");
+ return -1;
+ }
+
+ maskkey += 2;
+ payloadstartoffset += 2;
+ }
+ else if (len1 == WS_PAYLOAD_LENGTH_MAGIC_HUGE)
+ {
+ sock->SetError("WebSocket: Huge frames are not supported");
+ return -1;
+ }
+
+ if (myrecvq.length() < payloadstartoffset + len)
+ return 0;
+
+ unsigned int maskkeypos = 0;
+ const std::string::iterator endit = myrecvq.begin() + payloadstartoffset + len;
+ for (std::string::const_iterator i = myrecvq.begin() + payloadstartoffset; i != endit; ++i)
+ {
+ const unsigned char c = (unsigned char)*i;
+ appdataout.push_back(c ^ maskkey[maskkeypos++]);
+ maskkeypos %= 4;
+ }
+
+ myrecvq.erase(myrecvq.begin(), endit);
+ return 1;
+ }
+
+ int HandlePingPongFrame(StreamSocket* sock, bool isping)
+ {
+ if (lastpingpong + MINPINGPONGDELAY >= ServerInstance->Time())
+ {
+ sock->SetError("WebSocket: Ping/pong flood");
+ return -1;
+ }
+
+ lastpingpong = ServerInstance->Time();
+
+ std::string appdata;
+ const int result = HandleAppData(sock, appdata, false);
+ // If it's a pong stop here regardless of the result so we won't generate a reply
+ if ((result <= 0) || (!isping))
+ return result;
+
+ StreamSocket::SendQueue::Element elem = PrepareSendQElem(appdata.length(), OP_PONG);
+ elem.append(appdata);
+ GetSendQ().push_back(elem);
+
+ SocketEngine::ChangeEventMask(sock, FD_ADD_TRIAL_WRITE);
+ return 1;
+ }
+
+ int HandleWS(StreamSocket* sock, std::string& destrecvq)
+ {
+ if (GetRecvQ().empty())
+ return 0;
+
+ unsigned char opcode = (unsigned char)GetRecvQ().c_str()[0];
+ opcode &= ~WS_FINBIT;
+
+ switch (opcode)
+ {
+ case OP_CONTINUATION:
+ case OP_TEXT:
+ case OP_BINARY:
+ {
+ return HandleAppData(sock, destrecvq, true);
+ }
+
+ case OP_PING:
+ {
+ return HandlePingPongFrame(sock, true);
+ }
+
+ case OP_PONG:
+ {
+ // A pong frame may be sent unsolicited, so we have to handle it.
+ // It may carry application data which we need to remove from the recvq as well.
+ return HandlePingPongFrame(sock, false);
+ }
+
+ case OP_CLOSE:
+ {
+ sock->SetError("Connection closed");
+ return -1;
+ }
+
+ default:
+ {
+ sock->SetError("WebSocket: Invalid opcode");
+ return -1;
+ }
+ }
+ }
+
+ void FailHandshake(StreamSocket* sock, const char* httpreply, const char* sockerror)
+ {
+ GetSendQ().push_back(StreamSocket::SendQueue::Element(httpreply));
+ sock->DoWrite();
+ sock->SetError(sockerror);
+ }
+
+ int HandleHTTPReq(StreamSocket* sock)
+ {
+ std::string& recvq = GetRecvQ();
+ const std::string::size_type reqend = recvq.find("\r\n\r\n");
+ if (reqend == std::string::npos)
+ return 0;
+
+ HTTPHeaderFinder keyheader;
+ if (!keyheader.Find(recvq, "Sec-WebSocket-Key:", 18, reqend))
+ {
+ FailHandshake(sock, "HTTP/1.1 501 Not Implemented\r\nConnection: close\r\n\r\n", "WebSocket: Received HTTP request which is not a websocket upgrade");
+ return -1;
+ }
+
+ if (!*sha1)
+ {
+ FailHandshake(sock, "HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\n\r\n", "WebSocket: SHA-1 provider missing");
+ return -1;
+ }
+
+ state = STATE_ESTABLISHED;
+
+ std::string key = keyheader.ExtractValue(recvq);
+ key.append(MagicGUID);
+
+ std::string reply = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ";
+ reply.append(BinToBase64((*sha1)->GenerateRaw(key), NULL, '=')).append("\r\n\r\n");
+ GetSendQ().push_back(StreamSocket::SendQueue::Element(reply));
+
+ SocketEngine::ChangeEventMask(sock, FD_ADD_TRIAL_WRITE);
+
+ recvq.erase(0, reqend + 4);
+
+ return 1;
+ }
+
+ public:
+ WebSocketHook(IOHookProvider* Prov, StreamSocket* sock)
+ : IOHookMiddle(Prov)
+ , state(STATE_HTTPREQ)
+ , lastpingpong(0)
+ {
+ sock->AddIOHook(this);
+ }
+
+ int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
+ {
+ StreamSocket::SendQueue& mysendq = GetSendQ();
+
+ // Return 1 to allow sending back an error HTTP response
+ if (state != STATE_ESTABLISHED)
+ return (mysendq.empty() ? 0 : 1);
+
+ if (!uppersendq.empty())
+ {
+ StreamSocket::SendQueue::Element elem = PrepareSendQElem(uppersendq.bytes(), OP_BINARY);
+ mysendq.push_back(elem);
+ mysendq.moveall(uppersendq);
+ }
+
+ return 1;
+ }
+
+ int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
+ {
+ if (state == STATE_HTTPREQ)
+ {
+ int httpret = HandleHTTPReq(sock);
+ if (httpret <= 0)
+ return httpret;
+ }
+
+ int wsret;
+ do
+ {
+ wsret = HandleWS(sock, destrecvq);
+ }
+ while ((!GetRecvQ().empty()) && (wsret > 0));
+
+ return wsret;
+ }
+
+ void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ }
+};
+
+void WebSocketHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+{
+ new WebSocketHook(this, sock);
+}
+
+class ModuleWebSocket : public Module
+{
+ dynamic_reference_nocheck<HashProvider> hash;
+ WebSocketHookProvider hookprov;
+
+ public:
+ ModuleWebSocket()
+ : hash(this, "hash/sha1")
+ , hookprov(this)
+ {
+ sha1 = &hash;
+ }
+
+ void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
+ {
+ if (target_type != TYPE_USER)
+ return;
+
+ LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+ if ((user) && (user->eh.GetModHook(this)))
+ ServerInstance->Users.QuitUser(user, "WebSocket module unloading");
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides RFC 6455 WebSocket support", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleWebSocket)
diff --git a/src/modules/m_xline_db.cpp b/src/modules/m_xline_db.cpp
index 1a7fd8cc5..d220027fe 100644
--- a/src/modules/m_xline_db.cpp
+++ b/src/modules/m_xline_db.cpp
@@ -64,11 +64,6 @@ class ModuleXLineDB : public Module
dirty = true;
}
- void OnExpireLine(XLine *line) CXX11_OVERRIDE
- {
- dirty = true;
- }
-
void OnBackgroundTimer(time_t now) CXX11_OVERRIDE
{
if (dirty)
@@ -91,8 +86,8 @@ class ModuleXLineDB : public Module
std::ofstream stream(xlinenewdbpath.c_str());
if (!stream.is_open())
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot create database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot create database \"%s\"! %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new xline db \"%s\": %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
return false;
}
@@ -120,7 +115,7 @@ class ModuleXLineDB : public Module
XLine* line = i->second;
stream << "LINE " << line->type << " " << line->Displayable() << " "
<< ServerInstance->Config->ServerName << " " << line->set_time << " "
- << line->duration << " " << line->reason << std::endl;
+ << line->duration << " :" << line->reason << std::endl;
}
}
@@ -128,25 +123,20 @@ class ModuleXLineDB : public Module
if (stream.fail())
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot write to new database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot write to new database \"%s\"! %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new xline db \"%s\": %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
return false;
}
stream.close();
#ifdef _WIN32
- if (remove(xlinedbpath.c_str()))
- {
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot remove old database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot remove old database: %s (%d)", strerror(errno), errno);
- return false;
- }
+ remove(xlinedbpath.c_str());
#endif
// Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
if (rename(xlinenewdbpath.c_str(), xlinedbpath.c_str()) < 0)
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot move new to old database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot replace old database \"%s\" with new database \"%s\"! %s (%d)", xlinedbpath.c_str(), xlinenewdbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old xline db \"%s\" with new db \"%s\": %s (%d)", xlinedbpath.c_str(), xlinenewdbpath.c_str(), strerror(errno), errno);
return false;
}
@@ -162,8 +152,8 @@ class ModuleXLineDB : public Module
std::ifstream stream(xlinedbpath.c_str());
if (!stream.is_open())
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot read database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot read db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot read database \"%s\"! %s (%d)", xlinedbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot read xline db \"%s\": %s (%d)", xlinedbpath.c_str(), strerror(errno), errno);
return false;
}
diff --git a/src/server.cpp b/src/server.cpp
index ac638a08c..2feb08f96 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -46,13 +46,9 @@ void InspIRCd::Exit(int status)
#ifdef _WIN32
SetServiceStopped(status);
#endif
- if (this)
- {
- this->SendError("Exiting with status " + ConvToStr(status) + " (" + std::string(ExitCodes[status]) + ")");
- this->Cleanup();
- delete this;
- ServerInstance = NULL;
- }
+ this->Cleanup();
+ ServerInstance = NULL;
+ delete this;
exit (status);
}
@@ -61,14 +57,14 @@ void InspIRCd::Rehash(const std::string& uuid)
if (!ServerInstance->ConfigThread)
{
ServerInstance->ConfigThread = new ConfigReaderThread(uuid);
- ServerInstance->Threads->Start(ServerInstance->ConfigThread);
+ ServerInstance->Threads.Start(ServerInstance->ConfigThread);
}
}
std::string InspIRCd::GetVersionString(bool getFullVersion)
{
if (getFullVersion)
- return INSPIRCD_VERSION " " + Config->ServerName + " :" INSPIRCD_SYSTEM " [" INSPIRCD_REVISION "," INSPIRCD_SOCKETENGINE_NAME "," + Config->sid + "]";
+ return INSPIRCD_VERSION " " + Config->ServerName + " :" INSPIRCD_SYSTEM " [" INSPIRCD_SOCKETENGINE_NAME "," + Config->sid + "]";
return INSPIRCD_BRANCH " " + Config->ServerName + " :" + Config->CustomVersion;
}
@@ -169,13 +165,13 @@ void ISupportManager::Build()
tokens["AWAYLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxAway);
tokens["CASEMAPPING"] = "rfc1459";
+ tokens["CHANLIMIT"] = InspIRCd::Format("#:%u", ServerInstance->Config->MaxChans);
tokens["CHANMODES"] = ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL);
tokens["CHANNELLEN"] = ConvToStr(ServerInstance->Config->Limits.ChanMax);
tokens["CHANTYPES"] = "#";
tokens["ELIST"] = "MU";
tokens["KICKLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxKick);
tokens["MAXBANS"] = "64"; // TODO: make this a config setting.
- tokens["MAXCHANNELS"] = ConvToStr(ServerInstance->Config->MaxChans);
tokens["MAXTARGETS"] = ConvToStr(ServerInstance->Config->MaxTargets);
tokens["MODES"] = ConvToStr(ServerInstance->Config->Limits.MaxModes);
tokens["NETWORK"] = ServerInstance->Config->Network;
@@ -183,8 +179,7 @@ void ISupportManager::Build()
tokens["PREFIX"] = ServerInstance->Modes->BuildPrefixes();
tokens["STATUSMSG"] = ServerInstance->Modes->BuildPrefixes(false);
tokens["TOPICLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxTopic);
-
- tokens["FNC"] = tokens["VBANLIST"];
+ tokens["VBANLIST"];
// Modules can add new tokens and also edit or remove existing tokens
FOREACH_MOD(On005Numeric, (tokens));
@@ -193,41 +188,39 @@ void ISupportManager::Build()
std::map<std::string, std::string>::iterator extban = tokens.find("EXTBAN");
if (extban != tokens.end())
{
- sort(extban->second.begin(), extban->second.end());
+ std::sort(extban->second.begin(), extban->second.end());
extban->second.insert(0, ",");
}
// Transform the map into a list of lines, ready to be sent to clients
- std::vector<std::string>& lines = this->Lines;
- std::string line;
+ Numeric::Numeric numeric(RPL_ISUPPORT);
unsigned int token_count = 0;
- lines.clear();
+ cachedlines.clear();
for (std::map<std::string, std::string>::const_iterator it = tokens.begin(); it != tokens.end(); ++it)
{
- line.append(it->first);
+ numeric.push(it->first);
+ std::string& token = numeric.GetParams().back();
// If this token has a value then append a '=' char after the name and then the value itself
if (!it->second.empty())
- line.append(1, '=').append(it->second);
+ token.append(1, '=').append(it->second);
- // Always append a space, even if it's the last token because all lines will be suffixed
- line.push_back(' ');
token_count++;
if (token_count % 13 == 12 || it == --tokens.end())
{
// Reached maximum number of tokens for this line or the current token
// is the last one; finalize the line and store it for later use
- line.append(":are supported by this server");
- lines.push_back(line);
- line.clear();
+ numeric.push("are supported by this server");
+ cachedlines.push_back(numeric);
+ numeric.GetParams().clear();
}
}
}
void ISupportManager::SendTo(LocalUser* user)
{
- for (std::vector<std::string>::const_iterator i = this->Lines.begin(); i != this->Lines.end(); ++i)
- user->WriteNumeric(RPL_ISUPPORT, *i);
+ for (std::vector<Numeric::Numeric>::const_iterator i = cachedlines.begin(); i != cachedlines.end(); ++i)
+ user->WriteNumeric(*i);
}
diff --git a/src/snomasks.cpp b/src/snomasks.cpp
index 738d0970d..fd6a2709a 100644
--- a/src/snomasks.cpp
+++ b/src/snomasks.cpp
@@ -21,7 +21,6 @@
#include "inspircd.h"
-#include <stdarg.h>
void SnomaskManager::FlushSnotices()
{
diff --git a/src/socket.cpp b/src/socket.cpp
index 4ff43cde7..17f13bb8a 100644
--- a/src/socket.cpp
+++ b/src/socket.cpp
@@ -22,59 +22,6 @@
#include "inspircd.h"
-#include "socket.h"
-#include "socketengine.h"
-using irc::sockets::sockaddrs;
-
-/** This will bind a socket to a port. It works for UDP/TCP.
- * It can only bind to IP addresses, if you wish to bind to hostnames
- * you should first resolve them using class 'Resolver'.
- */
-bool InspIRCd::BindSocket(int sockfd, int port, const char* addr, bool dolisten)
-{
- sockaddrs servaddr;
- int ret;
-
- if ((*addr == '*' || *addr == '\0') && port == -1)
- {
- /* Port -1: Means UDP IPV4 port binding - Special case
- * used by DNS engine.
- */
- memset(&servaddr, 0, sizeof(servaddr));
- servaddr.in4.sin_family = AF_INET;
- }
- else if (!irc::sockets::aptosa(addr, port, servaddr))
- return false;
-
- ret = SocketEngine::Bind(sockfd, servaddr);
-
- if (ret < 0)
- {
- return false;
- }
- else
- {
- if (dolisten)
- {
- if (SocketEngine::Listen(sockfd, Config->MaxConn) == -1)
- {
- this->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR in listen(): %s",strerror(errno));
- return false;
- }
- else
- {
- this->Logs->Log("SOCKET", LOG_DEBUG, "New socket binding for %d with listen: %s:%d", sockfd, addr, port);
- SocketEngine::NonBlocking(sockfd);
- return true;
- }
- }
- else
- {
- this->Logs->Log("SOCKET", LOG_DEBUG, "New socket binding for %d without listen: %s:%d", sockfd, addr, port);
- return true;
- }
- }
-}
int InspIRCd::BindPorts(FailedPortList &failed_ports)
{
diff --git a/src/socketengine.cpp b/src/socketengine.cpp
index c6c520efc..3735e7530 100644
--- a/src/socketengine.cpp
+++ b/src/socketengine.cpp
@@ -53,6 +53,14 @@ void EventHandler::SetFd(int FD)
this->fd = FD;
}
+void EventHandler::OnEventHandlerWrite()
+{
+}
+
+void EventHandler::OnEventHandlerError(int errornum)
+{
+}
+
void SocketEngine::ChangeEventMask(EventHandler* eh, int change)
{
int old_m = eh->event_mask;
@@ -91,9 +99,9 @@ void SocketEngine::DispatchTrialWrites()
int mask = eh->event_mask;
eh->event_mask &= ~(FD_ADD_TRIAL_READ | FD_ADD_TRIAL_WRITE);
if ((mask & (FD_ADD_TRIAL_READ | FD_READ_WILL_BLOCK)) == FD_ADD_TRIAL_READ)
- eh->HandleEvent(EVENT_READ, 0);
+ eh->OnEventHandlerRead();
if ((mask & (FD_ADD_TRIAL_WRITE | FD_WRITE_WILL_BLOCK)) == FD_ADD_TRIAL_WRITE)
- eh->HandleEvent(EVENT_WRITE, 0);
+ eh->OnEventHandlerWrite();
}
}
@@ -195,35 +203,57 @@ void SocketEngine::SetReuse(int fd)
int SocketEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, sockaddr *from, socklen_t *fromlen)
{
int nbRecvd = recvfrom(fd->GetFd(), (char*)buf, len, flags, from, fromlen);
- if (nbRecvd > 0)
- stats.Update(nbRecvd, 0);
+ stats.UpdateReadCounters(nbRecvd);
return nbRecvd;
}
int SocketEngine::Send(EventHandler* fd, const void *buf, size_t len, int flags)
{
int nbSent = send(fd->GetFd(), (const char*)buf, len, flags);
- if (nbSent > 0)
- stats.Update(0, nbSent);
+ stats.UpdateWriteCounters(nbSent);
return nbSent;
}
int SocketEngine::Recv(EventHandler* fd, void *buf, size_t len, int flags)
{
int nbRecvd = recv(fd->GetFd(), (char*)buf, len, flags);
- if (nbRecvd > 0)
- stats.Update(nbRecvd, 0);
+ stats.UpdateReadCounters(nbRecvd);
return nbRecvd;
}
int SocketEngine::SendTo(EventHandler* fd, const void *buf, size_t len, int flags, const sockaddr *to, socklen_t tolen)
{
int nbSent = sendto(fd->GetFd(), (const char*)buf, len, flags, to, tolen);
- if (nbSent > 0)
- stats.Update(0, nbSent);
+ stats.UpdateWriteCounters(nbSent);
return nbSent;
}
+int SocketEngine::WriteV(EventHandler* fd, const IOVector* iovec, int count)
+{
+ int sent = writev(fd->GetFd(), iovec, count);
+ stats.UpdateWriteCounters(sent);
+ return sent;
+}
+
+#ifdef _WIN32
+int SocketEngine::WriteV(EventHandler* fd, const iovec* iovec, int count)
+{
+ // On Windows the fields in iovec are not in the order required by the Winsock API; IOVector has
+ // the fields in the correct order.
+ // Create temporary IOVectors from the iovecs and pass them to the WriteV() method that accepts the
+ // platform's native struct.
+ IOVector wiovec[128];
+ count = std::min(count, static_cast<int>(sizeof(wiovec) / sizeof(IOVector)));
+
+ for (int i = 0; i < count; i++)
+ {
+ wiovec[i].iov_len = iovec[i].iov_len;
+ wiovec[i].iov_base = reinterpret_cast<char*>(iovec[i].iov_base);
+ }
+ return WriteV(fd, wiovec, count);
+}
+#endif
+
int SocketEngine::Connect(EventHandler* fd, const sockaddr *serv_addr, socklen_t addrlen)
{
int ret = connect(fd->GetFd(), serv_addr, addrlen);
@@ -254,11 +284,26 @@ int SocketEngine::Shutdown(int fd, int how)
return shutdown(fd, how);
}
-void SocketEngine::Statistics::Update(size_t len_in, size_t len_out)
+void SocketEngine::Statistics::UpdateReadCounters(int len_in)
+{
+ CheckFlush();
+
+ ReadEvents++;
+ if (len_in > 0)
+ indata += len_in;
+ else if (len_in < 0)
+ ErrorEvents++;
+}
+
+void SocketEngine::Statistics::UpdateWriteCounters(int len_out)
{
CheckFlush();
- indata += len_in;
- outdata += len_out;
+
+ WriteEvents++;
+ if (len_out > 0)
+ outdata += len_out;
+ else if (len_out < 0)
+ ErrorEvents++;
}
void SocketEngine::Statistics::CheckFlush() const
@@ -291,7 +336,13 @@ std::string SocketEngine::LastError()
DWORD dwErrorCode = WSAGetLastError();
if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)szErrorString, _countof(szErrorString), NULL) == 0)
sprintf_s(szErrorString, _countof(szErrorString), "Error code: %u", dwErrorCode);
- return szErrorString;
+
+ std::string::size_type p;
+ std::string ret = szErrorString;
+ while ((p = ret.find_last_of("\r\n")) != std::string::npos)
+ ret.erase(p, 1);
+
+ return ret;
#endif
}
diff --git a/src/socketengines/socketengine_epoll.cpp b/src/socketengines/socketengine_epoll.cpp
index 7d919ec9f..c442e340d 100644
--- a/src/socketengines/socketengine_epoll.cpp
+++ b/src/socketengines/socketengine_epoll.cpp
@@ -18,16 +18,12 @@
*/
-#include <vector>
-#include <string>
-#include <map>
#include "inspircd.h"
#include "exitcodes.h"
-#include "socketengine.h"
+
#include <sys/epoll.h>
-#include <ulimit.h>
+#include <sys/resource.h>
#include <iostream>
-#define EP_DELAY 5
/** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
*/
@@ -42,8 +38,12 @@ namespace
void SocketEngine::Init()
{
- // MAX_DESCRIPTORS is mainly used for display purposes, no problem if ulimit() fails and returns a negative number
- MAX_DESCRIPTORS = ulimit(4, 0);
+ // MAX_DESCRIPTORS is mainly used for display purposes, no problem if getrlimit() fails
+ struct rlimit limit;
+ if (!getrlimit(RLIMIT_NOFILE, &limit))
+ {
+ MAX_DESCRIPTORS = limit.rlim_cur;
+ }
// 128 is not a maximum, just a hint at the eventual number of sockets that may be polled,
// and it is completely ignored by 2.6.8 and later kernels, except it must be larger than zero.
@@ -185,7 +185,7 @@ int SocketEngine::DispatchEvents()
if (ev.events & EPOLLHUP)
{
stats.ErrorEvents++;
- eh->HandleEvent(EVENT_ERROR, 0);
+ eh->OnEventHandlerError(0);
continue;
}
@@ -197,7 +197,7 @@ int SocketEngine::DispatchEvents()
int errcode;
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
errcode = errno;
- eh->HandleEvent(EVENT_ERROR, errcode);
+ eh->OnEventHandlerError(errcode);
continue;
}
@@ -217,16 +217,14 @@ int SocketEngine::DispatchEvents()
eh->SetEventMask(mask);
if (ev.events & EPOLLIN)
{
- stats.ReadEvents++;
- eh->HandleEvent(EVENT_READ);
+ eh->OnEventHandlerRead();
if (eh != GetRef(fd))
// whoa! we got deleted, better not give out the write event
continue;
}
if (ev.events & EPOLLOUT)
{
- stats.WriteEvents++;
- eh->HandleEvent(EVENT_WRITE);
+ eh->OnEventHandlerWrite();
}
}
diff --git a/src/socketengines/socketengine_kqueue.cpp b/src/socketengines/socketengine_kqueue.cpp
index d5a3bb793..9db902314 100644
--- a/src/socketengines/socketengine_kqueue.cpp
+++ b/src/socketengines/socketengine_kqueue.cpp
@@ -24,7 +24,6 @@
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
-#include "socketengine.h"
#include <iostream>
#include <sys/sysctl.h>
@@ -195,25 +194,23 @@ int SocketEngine::DispatchEvents()
if (kev.flags & EV_EOF)
{
stats.ErrorEvents++;
- eh->HandleEvent(EVENT_ERROR, kev.fflags);
+ eh->OnEventHandlerError(kev.fflags);
continue;
}
if (filter == EVFILT_WRITE)
{
- stats.WriteEvents++;
/* When mask is FD_WANT_FAST_WRITE or FD_WANT_SINGLE_WRITE,
* we set a one-shot write, so we need to clear that bit
* to detect when it set again.
*/
const int bits_to_clr = FD_WANT_SINGLE_WRITE | FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
eh->SetEventMask(eh->GetEventMask() & ~bits_to_clr);
- eh->HandleEvent(EVENT_WRITE);
+ eh->OnEventHandlerWrite();
}
else if (filter == EVFILT_READ)
{
- stats.ReadEvents++;
eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
- eh->HandleEvent(EVENT_READ);
+ eh->OnEventHandlerRead();
}
}
diff --git a/src/socketengines/socketengine_poll.cpp b/src/socketengines/socketengine_poll.cpp
index 4e6d0b9f5..59991d80d 100644
--- a/src/socketengines/socketengine_poll.cpp
+++ b/src/socketengines/socketengine_poll.cpp
@@ -21,12 +21,8 @@
*/
-#include <vector>
-#include <string>
-#include <map>
#include "exitcodes.h"
#include "inspircd.h"
-#include "socketengine.h"
#include <sys/poll.h>
#include <sys/resource.h>
@@ -172,7 +168,7 @@ int SocketEngine::DispatchEvents()
int processed = 0;
ServerInstance->UpdateTime();
- for (int index = 0; index < CurrentSetSize && processed < i; index++)
+ for (size_t index = 0; index < CurrentSetSize && processed < i; index++)
{
struct pollfd& pfd = events[index];
@@ -189,7 +185,7 @@ int SocketEngine::DispatchEvents()
if (revents & POLLHUP)
{
- eh->HandleEvent(EVENT_ERROR, 0);
+ eh->OnEventHandlerError(0);
continue;
}
@@ -200,14 +196,14 @@ int SocketEngine::DispatchEvents()
int errcode;
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
errcode = errno;
- eh->HandleEvent(EVENT_ERROR, errcode);
+ eh->OnEventHandlerError(errcode);
continue;
}
if (revents & POLLIN)
{
eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
- eh->HandleEvent(EVENT_READ);
+ eh->OnEventHandlerRead();
if (eh != GetRef(fd))
// whoops, deleted out from under us
continue;
@@ -221,7 +217,7 @@ int SocketEngine::DispatchEvents()
// The vector could've been resized, reference can be invalid by now; don't use it
events[index].events = mask_to_poll(mask);
- eh->HandleEvent(EVENT_WRITE);
+ eh->OnEventHandlerWrite();
}
}
diff --git a/src/socketengines/socketengine_ports.cpp b/src/socketengines/socketengine_ports.cpp
index c6ddb041c..68fa70e3b 100644
--- a/src/socketengines/socketengine_ports.cpp
+++ b/src/socketengines/socketengine_ports.cpp
@@ -25,11 +25,7 @@
# error You need Solaris 10 or later to make use of this code.
#endif
-#include <vector>
-#include <string>
-#include <map>
#include "inspircd.h"
-#include "socketengine.h"
#include <port.h>
#include <iostream>
#include <ulimit.h>
@@ -163,15 +159,13 @@ int SocketEngine::DispatchEvents()
port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(mask), eh);
if (portev_events & POLLRDNORM)
{
- stats.ReadEvents++;
- eh->HandleEvent(EVENT_READ);
+ eh->OnEventHandlerRead();
if (eh != GetRef(fd))
continue;
}
if (portev_events & POLLWRNORM)
{
- stats.WriteEvents++;
- eh->HandleEvent(EVENT_WRITE);
+ eh->OnEventHandlerWrite();
}
}
diff --git a/src/socketengines/socketengine_select.cpp b/src/socketengines/socketengine_select.cpp
index be4a7d186..42f634db1 100644
--- a/src/socketengines/socketengine_select.cpp
+++ b/src/socketengines/socketengine_select.cpp
@@ -20,7 +20,6 @@
#include "inspircd.h"
-#include "socketengine.h"
#ifndef _WIN32
#include <sys/select.h>
@@ -142,26 +141,24 @@ int SocketEngine::DispatchEvents()
if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
errcode = errno;
- ev->HandleEvent(EVENT_ERROR, errcode);
+ ev->OnEventHandlerError(errcode);
continue;
}
if (has_read)
{
- stats.ReadEvents++;
ev->SetEventMask(ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
- ev->HandleEvent(EVENT_READ);
+ ev->OnEventHandlerRead();
if (ev != GetRef(i))
continue;
}
if (has_write)
{
- stats.WriteEvents++;
int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
SocketEngine::OnSetEvent(ev, ev->GetEventMask(), newmask);
ev->SetEventMask(newmask);
- ev->HandleEvent(EVENT_WRITE);
+ ev->OnEventHandlerWrite();
}
}
diff --git a/src/testsuite.cpp b/src/testsuite.cpp
index b57a21ab8..a7a9ec99b 100644
--- a/src/testsuite.cpp
+++ b/src/testsuite.cpp
@@ -23,7 +23,6 @@
#include "inspircd.h"
#include "testsuite.h"
-#include "threadengine.h"
#include <iostream>
class TestSuiteThread : public Thread
diff --git a/src/threadengine.cpp b/src/threadengine.cpp
index 8e1bac470..f757aa56c 100644
--- a/src/threadengine.cpp
+++ b/src/threadengine.cpp
@@ -18,7 +18,6 @@
#include "inspircd.h"
-#include "threadengine.h"
void Thread::SetExitFlag()
{
@@ -27,14 +26,5 @@ void Thread::SetExitFlag()
void Thread::join()
{
- state->FreeThread(this);
- delete state;
- state = 0;
-}
-
-/** If this thread has a Creator set, call it to
- * free the thread
- */
-Thread::~Thread()
-{
+ ServerInstance->Threads.Stop(this);
}
diff --git a/src/threadengines/threadengine_pthread.cpp b/src/threadengines/threadengine_pthread.cpp
index 8527907c4..3249f442b 100644
--- a/src/threadengines/threadengine_pthread.cpp
+++ b/src/threadengines/threadengine_pthread.cpp
@@ -21,13 +21,8 @@
#include "inspircd.h"
#include "threadengines/threadengine_pthread.h"
#include <pthread.h>
-#include <signal.h>
#include <fcntl.h>
-ThreadEngine::ThreadEngine()
-{
-}
-
static void* entry_point(void* parameter)
{
/* Recommended by nenolod, signal safety on a per-thread basis */
@@ -44,25 +39,14 @@ static void* entry_point(void* parameter)
void ThreadEngine::Start(Thread* thread)
{
- ThreadData* data = new ThreadData;
- thread->state = data;
-
- if (pthread_create(&data->pthread_id, NULL, entry_point, thread) != 0)
- {
- thread->state = NULL;
- delete data;
+ if (pthread_create(&thread->state.pthread_id, NULL, entry_point, thread) != 0)
throw CoreException("Unable to create new thread: " + std::string(strerror(errno)));
- }
}
-ThreadEngine::~ThreadEngine()
-{
-}
-
-void ThreadData::FreeThread(Thread* thread)
+void ThreadEngine::Stop(Thread* thread)
{
thread->SetExitFlag();
- pthread_join(pthread_id, NULL);
+ pthread_join(thread->state.pthread_id, NULL);
}
#ifdef HAS_EVENTFD
@@ -88,18 +72,21 @@ class ThreadSignalSocket : public EventHandler
eventfd_write(fd, 1);
}
- void HandleEvent(EventType et, int errornum)
+ void OnEventHandlerRead() CXX11_OVERRIDE
{
- if (et == EVENT_READ)
- {
- eventfd_t dummy;
- eventfd_read(fd, &dummy);
- parent->OnNotify();
- }
- else
- {
- ServerInstance->GlobalCulls.AddItem(this);
- }
+ eventfd_t dummy;
+ eventfd_read(fd, &dummy);
+ parent->OnNotify();
+ }
+
+ void OnEventHandlerWrite() CXX11_OVERRIDE
+ {
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+
+ void OnEventHandlerError(int errcode) CXX11_OVERRIDE
+ {
+ ThreadSignalSocket::OnEventHandlerWrite();
}
};
@@ -108,7 +95,7 @@ SocketThread::SocketThread()
signal.sock = NULL;
int fd = eventfd(0, EFD_NONBLOCK);
if (fd < 0)
- throw new CoreException("Could not create pipe " + std::string(strerror(errno)));
+ throw CoreException("Could not create pipe " + std::string(strerror(errno)));
signal.sock = new ThreadSignalSocket(this, fd);
}
#else
@@ -138,18 +125,21 @@ class ThreadSignalSocket : public EventHandler
write(send_fd, &dummy, 1);
}
- void HandleEvent(EventType et, int errornum)
+ void OnEventHandlerRead() CXX11_OVERRIDE
+ {
+ char dummy[128];
+ read(fd, dummy, 128);
+ parent->OnNotify();
+ }
+
+ void OnEventHandlerWrite() CXX11_OVERRIDE
+ {
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+
+ void OnEventHandlerError(int errcode) CXX11_OVERRIDE
{
- if (et == EVENT_READ)
- {
- char dummy[128];
- read(fd, dummy, 128);
- parent->OnNotify();
- }
- else
- {
- ServerInstance->GlobalCulls.AddItem(this);
- }
+ ThreadSignalSocket::OnEventHandlerWrite();
}
};
@@ -158,7 +148,7 @@ SocketThread::SocketThread()
signal.sock = NULL;
int fds[2];
if (pipe(fds))
- throw new CoreException("Could not create pipe " + std::string(strerror(errno)));
+ throw CoreException("Could not create pipe " + std::string(strerror(errno)));
signal.sock = new ThreadSignalSocket(this, fds[0], fds[1]);
}
#endif
diff --git a/src/threadengines/threadengine_win32.cpp b/src/threadengines/threadengine_win32.cpp
index c3a844a74..0f0d1f277 100644
--- a/src/threadengines/threadengine_win32.cpp
+++ b/src/threadengines/threadengine_win32.cpp
@@ -21,33 +21,19 @@
#include "inspircd.h"
#include "threadengines/threadengine_win32.h"
-ThreadEngine::ThreadEngine()
-{
-}
-
void ThreadEngine::Start(Thread* thread)
{
- ThreadData* data = new ThreadData;
- thread->state = data;
-
- DWORD ThreadId = 0;
- data->handle = CreateThread(NULL,0,ThreadEngine::Entry,thread,0,&ThreadId);
+ thread->state.handle = CreateThread(NULL, 0, ThreadEngine::Entry, thread, 0, NULL);
- if (data->handle == NULL)
+ if (thread->state.handle == NULL)
{
DWORD lasterr = GetLastError();
- thread->state = NULL;
- delete data;
std::string err = "Unable to create new thread: " + ConvToStr(lasterr);
SetLastError(ERROR_SUCCESS);
throw CoreException(err);
}
}
-ThreadEngine::~ThreadEngine()
-{
-}
-
DWORD WINAPI ThreadEngine::Entry(void* parameter)
{
Thread* pt = static_cast<Thread*>(parameter);
@@ -55,9 +41,10 @@ DWORD WINAPI ThreadEngine::Entry(void* parameter)
return 0;
}
-void ThreadData::FreeThread(Thread* thread)
+void ThreadEngine::Stop(Thread* thread)
{
thread->SetExitFlag();
+ HANDLE handle = thread->state.handle;
WaitForSingleObject(handle,INFINITE);
CloseHandle(handle);
}
@@ -83,6 +70,24 @@ class ThreadSignalSocket : public BufferedSocket
}
};
+static bool BindAndListen(int sockfd, int port, const char* addr)
+{
+ irc::sockets::sockaddrs servaddr;
+ if (!irc::sockets::aptosa(addr, port, servaddr))
+ return false;
+
+ if (SocketEngine::Bind(sockfd, servaddr) != 0)
+ return false;
+
+ if (SocketEngine::Listen(sockfd, ServerInstance->Config->MaxConn) != 0)
+ {
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR in listen(): %s", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
SocketThread::SocketThread()
{
int listenFD = socket(AF_INET, SOCK_STREAM, 0);
@@ -92,7 +97,7 @@ SocketThread::SocketThread()
if (connFD == -1)
throw CoreException("Could not create ITC pipe");
- if (!ServerInstance->BindSocket(listenFD, 0, "127.0.0.1", true))
+ if (!BindAndListen(listenFD, 0, "127.0.0.1"))
throw CoreException("Could not create ITC pipe");
SocketEngine::NonBlocking(connFD);
diff --git a/src/timer.cpp b/src/timer.cpp
index 8e11ee4a7..0b0d8bac3 100644
--- a/src/timer.cpp
+++ b/src/timer.cpp
@@ -21,7 +21,6 @@
#include "inspircd.h"
-#include "timer.h"
void Timer::SetInterval(time_t newinterval)
{
@@ -31,6 +30,13 @@ void Timer::SetInterval(time_t newinterval)
ServerInstance->Timers.AddTimer(this);
}
+Timer::Timer(unsigned int secs_from_now, bool repeating)
+ : trigger(ServerInstance->Time() + secs_from_now)
+ , secs(secs_from_now)
+ , repeat(repeating)
+{
+}
+
Timer::~Timer()
{
ServerInstance->Timers.DelTimer(this);
diff --git a/src/usermanager.cpp b/src/usermanager.cpp
index 3c234f13f..12ec36ec7 100644
--- a/src/usermanager.cpp
+++ b/src/usermanager.cpp
@@ -22,11 +22,35 @@
#include "inspircd.h"
#include "xline.h"
-#include "bancache.h"
#include "iohook.h"
+namespace
+{
+ class WriteCommonQuit : public User::ForEachNeighborHandler
+ {
+ std::string line;
+ std::string operline;
+
+ void Execute(LocalUser* user) CXX11_OVERRIDE
+ {
+ user->Write(user->IsOper() ? operline : line);
+ }
+
+ public:
+ WriteCommonQuit(User* user, const std::string& msg, const std::string& opermsg)
+ : line(":" + user->GetFullHost() + " QUIT :")
+ , operline(line)
+ {
+ line += msg;
+ operline += opermsg;
+ user->ForEachNeighbor(*this, false);
+ }
+ };
+}
+
UserManager::UserManager()
- : unregistered_count(0)
+ : already_sent_id(0)
+ , unregistered_count(0)
{
}
@@ -38,44 +62,41 @@ UserManager::~UserManager()
}
}
-/* add a client connection to the sockets list */
void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
{
- /* NOTE: Calling this one parameter constructor for User automatically
- * allocates a new UUID and places it in the hash_map.
- */
- LocalUser* New = NULL;
- try
- {
- New = new LocalUser(socket, client, server);
- }
- catch (...)
- {
- ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "*** WTF *** Duplicated UUID! -- Crack smoking monkeys have been unleashed.");
- ServerInstance->SNO->WriteToSnoMask('a', "WARNING *** Duplicate UUID allocated!");
- return;
- }
+ // User constructor allocates a new UUID for the user and inserts it into the uuidlist
+ LocalUser* const New = new LocalUser(socket, client, server);
UserIOHandler* eh = &New->eh;
- // If this listener has an IO hook provider set then tell it about the connection
- if (via->iohookprov)
- via->iohookprov->OnAccept(eh, client, server);
-
ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New user fd: %d", socket);
this->unregistered_count++;
-
- /* The users default nick is their UUID */
- New->nick = New->uuid;
this->clientlist[New->nick] = New;
+ this->AddClone(New);
+ this->local_users.push_front(New);
- New->registered = REG_NONE;
- New->signon = ServerInstance->Time() + ServerInstance->Config->dns_timeout;
- New->lastping = 1;
+ if (!SocketEngine::AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
+ {
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
+ this->QuitUser(New, "Internal error handling connection");
+ return;
+ }
- this->AddClone(New);
+ // If this listener has an IO hook provider set then tell it about the connection
+ for (ListenSocket::IOHookProvList::iterator i = via->iohookprovs.begin(); i != via->iohookprovs.end(); ++i)
+ {
+ ListenSocket::IOHookProvRef& iohookprovref = *i;
+ if (!iohookprovref)
+ continue;
- this->local_users.push_front(New);
+ iohookprovref->OnAccept(eh, client, server);
+ // IOHook could have encountered a fatal error, e.g. if the TLS ClientHello was already in the queue and there was no common TLS version
+ if (!eh->getError().empty())
+ {
+ QuitUser(New, eh->getError());
+ return;
+ }
+ }
if (this->local_users.size() > ServerInstance->Config->SoftLimit)
{
@@ -84,16 +105,9 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs
return;
}
- /*
- * First class check. We do this again in FullConnect after DNS is done, and NICK/USER is recieved.
- * See my note down there for why this is required. DO NOT REMOVE. :) -- w00t
- */
+ // First class check. We do this again in LocalUser::FullConnect() after DNS is done, and NICK/USER is received.
New->SetClass();
-
- /*
- * Check connect class settings and initialise settings into User.
- * This will be done again after DNS resolution. -- w00t
- */
+ // If the user doesn't have an acceptable connect class CheckClass() quits them
New->CheckClass(ServerInstance->Config->CCOnConnect);
if (New->quitting)
return;
@@ -105,14 +119,15 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs
*/
New->exempt = (ServerInstance->XLines->MatchesLine("E",New) != NULL);
- if (BanCacheHit *b = ServerInstance->BanCache->GetHit(New->GetIPString()))
+ BanCacheHit* const b = ServerInstance->BanCache.GetHit(New->GetIPString());
+ if (b)
{
if (!b->Type.empty() && !New->exempt)
{
/* user banned */
ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Positive hit for " + New->GetIPString());
if (!ServerInstance->Config->XLineMessage.empty())
- New->WriteNotice("*** " + ServerInstance->Config->XLineMessage);
+ New->WriteNumeric(ERR_YOUREBANNEDCREEP, ServerInstance->Config->XLineMessage);
this->QuitUser(New, b->Reason);
return;
}
@@ -135,12 +150,6 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs
}
}
- if (!SocketEngine::AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
- {
- ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
- this->QuitUser(New, "Internal error handling connection");
- }
-
if (ServerInstance->Config->RawLog)
New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
@@ -180,7 +189,7 @@ void UserManager::QuitUser(User* user, const std::string& quitreason, const std:
if (user->registered == REG_ALL)
{
FOREACH_MOD(OnUserQuit, (user, reason, *operreason));
- user->WriteCommonQuit(reason, *operreason);
+ WriteCommonQuit(user, reason, *operreason);
}
else
unregistered_count--;
@@ -193,6 +202,7 @@ void UserManager::QuitUser(User* user, const std::string& quitreason, const std:
if (lu->registered == REG_ALL)
ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]", user->GetFullRealHost().c_str(), user->GetIPString().c_str(), operreason->c_str());
+ local_users.erase(lu);
}
if (!clientlist.erase(user->nick))
@@ -229,6 +239,18 @@ void UserManager::RemoveCloneCounts(User *user)
}
}
+void UserManager::RehashCloneCounts()
+{
+ clonemap.clear();
+
+ const user_hash& hash = ServerInstance->Users.GetUsers();
+ for (user_hash::const_iterator i = hash.begin(); i != hash.end(); ++i)
+ {
+ User* u = i->second;
+ AddClone(u);
+ }
+}
+
const UserManager::CloneCounts& UserManager::GetCloneCounts(User* user) const
{
CloneMap::const_iterator it = clonemap.find(user->GetCIDRMask());
@@ -244,24 +266,13 @@ void UserManager::ServerNoticeAll(const char* text, ...)
VAFORMAT(message, text, text);
message = "NOTICE $" + ServerInstance->Config->ServerName + " :" + message;
- for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
+ for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
{
User* t = *i;
t->WriteServ(message);
}
}
-void UserManager::GarbageCollect()
-{
- // Reset the already_sent IDs so we don't wrap it around and drop a message
- LocalUser::already_sent_id = 0;
- for (LocalUserList::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++)
- {
- (**i).already_sent = 0;
- (**i).RemoveExpiredInvites();
- }
-}
-
/* this returns true when all modules are satisfied that the user should be allowed onto the irc server
* (until this returns true, a user will block in the waiting state, waiting to connect up to the
* registration timeout maximum seconds)
@@ -275,20 +286,16 @@ bool UserManager::AllModulesReportReady(LocalUser* user)
/**
* This function is called once a second from the mainloop.
- * It is intended to do background checking on all the user structs, e.g.
- * stuff like ping checks, registration timeouts, etc.
+ * It is intended to do background checking on all the users, e.g. do
+ * ping checks, registration timeouts, etc.
*/
void UserManager::DoBackgroundUserStuff()
{
- /*
- * loop over all local users..
- */
- for (LocalUserList::iterator i = local_users.begin(); i != local_users.end(); ++i)
+ for (LocalList::iterator i = local_users.begin(); i != local_users.end(); )
{
+ // It's possible that we quit the user below due to ping timeout etc. and QuitUser() removes it from the list
LocalUser* curr = *i;
-
- if (curr->quitting)
- continue;
+ ++i;
if (curr->CommandFloodPenalty || curr->eh.getSendQSize())
{
@@ -303,7 +310,7 @@ void UserManager::DoBackgroundUserStuff()
switch (curr->registered)
{
case REG_ALL:
- if (ServerInstance->Time() > curr->nping)
+ if (ServerInstance->Time() >= curr->nping)
{
// This user didn't answer the last ping, remove them
if (!curr->lastping)
@@ -326,10 +333,15 @@ void UserManager::DoBackgroundUserStuff()
curr->FullConnect();
continue;
}
+
+ // If the user has been quit in OnCheckReady then we shouldn't
+ // quit them again for having a registration timeout.
+ if (curr->quitting)
+ continue;
break;
}
- if (curr->registered != REG_ALL && (ServerInstance->Time() > (curr->age + curr->MyClass->GetRegTimeout())))
+ if (curr->registered != REG_ALL && curr->MyClass && (ServerInstance->Time() > (curr->signon + curr->MyClass->GetRegTimeout())))
{
/*
* registration timeout -- didnt send USER/NICK/HOST
@@ -340,3 +352,18 @@ void UserManager::DoBackgroundUserStuff()
}
}
}
+
+already_sent_t UserManager::NextAlreadySentId()
+{
+ if (++already_sent_id == 0)
+ {
+ // Wrapped around, reset the already_sent ids of all users
+ already_sent_id = 1;
+ for (LocalList::iterator i = local_users.begin(); i != local_users.end(); ++i)
+ {
+ LocalUser* user = *i;
+ user->already_sent = 0;
+ }
+ }
+ return already_sent_id;
+}
diff --git a/src/users.cpp b/src/users.cpp
index 6e9e8202e..d54931644 100644
--- a/src/users.cpp
+++ b/src/users.cpp
@@ -24,12 +24,7 @@
#include "inspircd.h"
-#include <stdarg.h>
-#include "socketengine.h"
#include "xline.h"
-#include "bancache.h"
-
-already_sent_t LocalUser::already_sent_id = 0;
bool User::IsNoticeMaskSet(unsigned char sm)
{
@@ -38,60 +33,76 @@ bool User::IsNoticeMaskSet(unsigned char sm)
return (snomasks[sm-65]);
}
-bool User::IsModeSet(unsigned char m)
+bool User::IsModeSet(unsigned char m) const
{
ModeHandler* mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
return (mh && modes[mh->GetId()]);
}
-const char* User::FormatModes(bool showparameters)
+std::string User::GetModeLetters(bool includeparams) const
{
- static std::string data;
+ std::string ret(1, '+');
std::string params;
- data.clear();
- for (unsigned char n = 0; n < 64; n++)
+ for (unsigned char i = 'A'; i < 'z'; i++)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_USER);
- if (mh && IsModeSet(mh))
+ const ModeHandler* const mh = ServerInstance->Modes.FindMode(i, MODETYPE_USER);
+ if ((!mh) || (!IsModeSet(mh)))
+ continue;
+
+ ret.push_back(mh->GetModeChar());
+ if ((includeparams) && (mh->NeedsParam(true)))
{
- data.push_back(n + 65);
- if (showparameters && mh->GetNumParams(true))
- {
- std::string p = mh->GetUserParameter(this);
- if (p.length())
- params.append(" ").append(p);
- }
+ const std::string val = mh->GetUserParameter(this);
+ if (!val.empty())
+ params.append(1, ' ').append(val);
}
}
- data += params;
- return data.c_str();
+
+ ret += params;
+ return ret;
}
User::User(const std::string& uid, Server* srv, int type)
- : uuid(uid), server(srv), usertype(type)
+ : age(ServerInstance->Time())
+ , signon(0)
+ , uuid(uid)
+ , server(srv)
+ , registered(REG_NONE)
+ , quitting(false)
+ , usertype(type)
{
- age = ServerInstance->Time();
- signon = 0;
- registered = 0;
- quitting = false;
client_sa.sa.sa_family = AF_UNSPEC;
ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New UUID for user: %s", uuid.c_str());
- if (!ServerInstance->Users->uuidlist.insert(std::make_pair(uuid, this)).second)
- throw CoreException("Duplicate UUID "+std::string(uuid)+" in User constructor");
+ // Do not insert FakeUsers into the uuidlist so FindUUID() won't return them which is the desired behavior
+ if (type != USERTYPE_SERVER)
+ {
+ if (!ServerInstance->Users.uuidlist.insert(std::make_pair(uuid, this)).second)
+ throw CoreException("Duplicate UUID in User constructor: " + uuid);
+ }
}
LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* servaddr)
- : User(ServerInstance->UIDGen.GetUID(), ServerInstance->FakeClient->server, USERTYPE_LOCAL), eh(this),
- bytes_in(0), bytes_out(0), cmds_in(0), cmds_out(0), nping(0), CommandFloodPenalty(0),
- already_sent(0)
-{
- exempt = quitting_sendq = false;
- idle_lastmsg = 0;
+ : User(ServerInstance->UIDGen.GetUID(), ServerInstance->FakeClient->server, USERTYPE_LOCAL)
+ , eh(this)
+ , bytes_in(0)
+ , bytes_out(0)
+ , cmds_in(0)
+ , cmds_out(0)
+ , quitting_sendq(false)
+ , lastping(true)
+ , exempt(false)
+ , nping(0)
+ , idle_lastmsg(0)
+ , CommandFloodPenalty(0)
+ , already_sent(0)
+{
+ signon = ServerInstance->Time();
+ // The user's default nick is their UUID
+ nick = uuid;
ident = "unknown";
- lastping = 0;
eh.SetFd(myfd);
memcpy(&client_sa, client, sizeof(irc::sockets::sockaddrs));
memcpy(&server_sa, servaddr, sizeof(irc::sockets::sockaddrs));
@@ -100,8 +111,6 @@ LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::so
User::~User()
{
- if (ServerInstance->FindUUID(uuid))
- ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "User destructor for %s called without cull", uuid.c_str());
}
const std::string& User::MakeHost()
@@ -144,41 +153,20 @@ const std::string& User::GetFullRealHost()
return this->cached_fullrealhost;
}
-InviteList& LocalUser::GetInviteList()
-{
- RemoveExpiredInvites();
- return invites;
-}
-
-bool LocalUser::RemoveInvite(Channel* chan)
-{
- Invitation* inv = Invitation::Find(chan, this);
- if (inv)
- {
- delete inv;
- return true;
- }
- return false;
-}
-
-void LocalUser::RemoveExpiredInvites()
-{
- Invitation::Find(NULL, this);
-}
-
-bool User::HasModePermission(unsigned char, ModeType)
+bool User::HasModePermission(const ModeHandler* mh) const
{
return true;
}
-bool LocalUser::HasModePermission(unsigned char mode, ModeType type)
+bool LocalUser::HasModePermission(const ModeHandler* mh) const
{
if (!this->IsOper())
return false;
+ const unsigned char mode = mh->GetModeChar();
if (mode < 'A' || mode > ('A' + 64)) return false;
- return ((type == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
+ return ((mh->GetModeType() == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
}
/*
@@ -282,14 +270,14 @@ void UserIOHandler::OnDataReady()
return;
eol_found:
// just found a newline. Terminate the string, and pull it out of recvq
- recvq = recvq.substr(qpos);
+ recvq.erase(0, qpos);
// TODO should this be moved to when it was inserted in recvq?
- ServerInstance->stats->statsRecv += qpos;
+ ServerInstance->stats.Recv += qpos;
user->bytes_in += qpos;
user->cmds_in++;
- ServerInstance->Parser->ProcessBuffer(line, user);
+ ServerInstance->Parser.ProcessBuffer(line, user);
if (user->quitting)
return;
}
@@ -333,8 +321,6 @@ CullResult User::cull()
CullResult LocalUser::cull()
{
- ServerInstance->Users->local_users.erase(this);
- ClearInvites();
eh.cull();
return User::cull();
}
@@ -343,8 +329,7 @@ CullResult FakeUser::cull()
{
// Fake users don't quit, they just get culled.
quitting = true;
- // Fake users are not inserted into UserManager::clientlist, they're only in the uuidlist
- ServerInstance->Users->uuidlist.erase(uuid);
+ // Fake users are not inserted into UserManager::clientlist or uuidlist, so we don't need to modify those here
return User::cull();
}
@@ -368,7 +353,7 @@ void User::Oper(OperInfo* info)
LocalUser* l = IS_LOCAL(this);
std::string vhost = oper->getConfig("vhost");
if (!vhost.empty())
- l->ChangeDisplayedHost(vhost.c_str());
+ l->ChangeDisplayedHost(vhost);
std::string opClass = oper->getConfig("class");
if (!opClass.empty())
l->SetClass(opClass);
@@ -376,7 +361,7 @@ void User::Oper(OperInfo* info)
ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",
nick.c_str(), ident.c_str(), host.c_str(), oper->name.c_str(), opername.c_str());
- this->WriteNumeric(RPL_YOUAREOPER, ":You are now %s %s", strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->name.c_str());
+ this->WriteNumeric(RPL_YOUAREOPER, InspIRCd::Format("You are now %s %s", strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->name.c_str()));
ServerInstance->Logs->Log("OPER", LOG_DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->name.c_str());
ServerInstance->Users->all_opers.push_back(this);
@@ -420,7 +405,7 @@ void OperInfo::init()
{
this->AllowedUserModes.set();
}
- else if (*c >= 'A' && *c < 'z')
+ else if (*c >= 'A' && *c <= 'z')
{
this->AllowedUserModes[*c - 'A'] = true;
}
@@ -433,7 +418,7 @@ void OperInfo::init()
{
this->AllowedChanModes.set();
}
- else if (*c >= 'A' && *c < 'z')
+ else if (*c >= 'A' && *c <= 'z')
{
this->AllowedChanModes[*c - 'A'] = true;
}
@@ -455,21 +440,16 @@ void User::UnOper()
/* Remove all oper only modes from the user when the deoper - Bug #466*/
- std::string moderemove("-");
-
- for (unsigned char letter = 'A'; letter <= 'z'; letter++)
+ Modes::ChangeList changelist;
+ const ModeParser::ModeHandlerMap& usermodes = ServerInstance->Modes->GetModes(MODETYPE_USER);
+ for (ModeParser::ModeHandlerMap::const_iterator i = usermodes.begin(); i != usermodes.end(); ++i)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_USER);
- if (mh && mh->NeedsOper())
- moderemove += letter;
+ ModeHandler* mh = i->second;
+ if (mh->NeedsOper())
+ changelist.push_remove(mh);
}
-
- std::vector<std::string> parameters;
- parameters.push_back(this->nick);
- parameters.push_back(moderemove);
-
- ServerInstance->Modes->Process(parameters, this);
+ ServerInstance->Modes->Process(this, NULL, this, changelist);
// Remove the user from the oper list
stdalgo::vector::swaperase(ServerInstance->Users->all_opers, this);
@@ -540,7 +520,7 @@ bool LocalUser::CheckLines(bool doZline)
void LocalUser::FullConnect()
{
- ServerInstance->stats->statsConnects++;
+ ServerInstance->stats.Connects++;
this->idle_lastmsg = ServerInstance->Time();
/*
@@ -557,12 +537,12 @@ void LocalUser::FullConnect()
if (quitting)
return;
- this->WriteNumeric(RPL_WELCOME, ":Welcome to the %s IRC Network %s", ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str());
- this->WriteNumeric(RPL_YOURHOSTIS, ":Your host is %s, running version %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH);
- this->WriteNumeric(RPL_SERVERCREATED, ":This server was created %s %s", __TIME__, __DATE__);
+ this->WriteNumeric(RPL_WELCOME, InspIRCd::Format("Welcome to the %s IRC Network %s", ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str()));
+ this->WriteNumeric(RPL_YOURHOSTIS, InspIRCd::Format("Your host is %s, running version %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH));
+ this->WriteNumeric(RPL_SERVERCREATED, InspIRCd::Format("This server was created %s %s", __TIME__, __DATE__));
const std::string& modelist = ServerInstance->Modes->GetModeListFor004Numeric();
- this->WriteNumeric(RPL_SERVERVERSION, "%s %s %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH, modelist.c_str());
+ this->WriteNumeric(RPL_SERVERVERSION, ServerInstance->Config->ServerName, INSPIRCD_BRANCH, modelist);
ServerInstance->ISupport.SendTo(this);
@@ -576,13 +556,13 @@ void LocalUser::FullConnect()
std::vector<std::string> parameters;
FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
if (!MOD_RESULT)
- ServerInstance->Parser->CallHandler(command, parameters, this);
+ ServerInstance->Parser.CallHandler(command, parameters, this);
MOD_RESULT = MOD_RES_PASSTHRU;
command = "MOTD";
FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
if (!MOD_RESULT)
- ServerInstance->Parser->CallHandler(command, parameters, this);
+ ServerInstance->Parser.CallHandler(command, parameters, this);
if (ServerInstance->Config->RawLog)
WriteServ("PRIVMSG %s :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.", nick.c_str());
@@ -600,7 +580,7 @@ void LocalUser::FullConnect()
ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d (class %s): %s (%s) [%s]",
this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->fullname.c_str());
ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding NEGATIVE hit for " + this->GetIPString());
- ServerInstance->BanCache->AddHit(this->GetIPString(), "", "");
+ ServerInstance->BanCache.AddHit(this->GetIPString(), "", "");
// reset the flood penalty (which could have been raised due to things like auto +x)
CommandFloodPenalty = 0;
}
@@ -608,13 +588,14 @@ void LocalUser::FullConnect()
void User::InvalidateCache()
{
/* Invalidate cache */
+ cachedip.clear();
cached_fullhost.clear();
cached_hostip.clear();
cached_makehost.clear();
cached_fullrealhost.clear();
}
-bool User::ChangeNick(const std::string& newnick, bool force, time_t newts)
+bool User::ChangeNick(const std::string& newnick, time_t newts)
{
if (quitting)
{
@@ -622,21 +603,10 @@ bool User::ChangeNick(const std::string& newnick, bool force, time_t newts)
return false;
}
- if (!force)
+ User* const InUse = ServerInstance->FindNickOnly(newnick);
+ if (InUse == this)
{
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (this, newnick));
-
- if (MOD_RESULT == MOD_RES_DENY)
- {
- ServerInstance->stats->statsCollisions++;
- return false;
- }
- }
-
- if (assign(newnick) == assign(nick))
- {
- // case change, don't need to check Q:lines and such
+ // case change, don't need to check campers
// and, if it's identical including case, we can leave right now
// We also don't update the nick TS if it's a case change, either
if (newnick == nick)
@@ -645,42 +615,6 @@ bool User::ChangeNick(const std::string& newnick, bool force, time_t newts)
else
{
/*
- * Don't check Q:Lines if it's a server-enforced change, just on the off-chance some fucking *moron*
- * tries to Q:Line SIDs, also, this means we just get our way period, as it really should be.
- * Thanks Kein for finding this. -- w00t
- *
- * Also don't check Q:Lines for remote nickchanges, they should have our Q:Lines anyway to enforce themselves.
- * -- w00t
- */
- if (IS_LOCAL(this) && !force)
- {
- XLine* mq = ServerInstance->XLines->MatchesLine("Q",newnick);
- if (mq)
- {
- if (this->registered == REG_ALL)
- {
- ServerInstance->SNO->WriteGlobalSno('a', "Q-Lined nickname %s from %s: %s",
- newnick.c_str(), GetFullRealHost().c_str(), mq->reason.c_str());
- }
- this->WriteNumeric(ERR_ERRONEUSNICKNAME, "%s :Invalid nickname: %s", newnick.c_str(), mq->reason.c_str());
- return false;
- }
-
- if (ServerInstance->Config->RestrictBannedUsers)
- {
- for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
- {
- Channel* chan = (*i)->chan;
- if (chan->GetPrefixValue(this) < VOICE_VALUE && chan->IsBanned(this))
- {
- this->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (you're banned)", chan->name.c_str());
- return false;
- }
- }
- }
- }
-
- /*
* Uh oh.. if the nickname is in use, and it's not in use by the person using it (doh) --
* then we have a potential collide. Check whether someone else is camping on the nick
* (i.e. connect -> send NICK, don't send USER.) If they are camping, force-change the
@@ -689,26 +623,18 @@ bool User::ChangeNick(const std::string& newnick, bool force, time_t newts)
* If the guy using the nick is already using it, tell the incoming nick change to gtfo,
* because the nick is already (rightfully) in use. -- w00t
*/
- User* InUse = ServerInstance->FindNickOnly(newnick);
- if (InUse && (InUse != this))
+ if (InUse)
{
if (InUse->registered != REG_ALL)
{
/* force the camper to their UUID, and ask them to re-send a NICK. */
- InUse->WriteFrom(InUse, "NICK %s", InUse->uuid.c_str());
- InUse->WriteNumeric(ERR_NICKNAMEINUSE, "%s :Nickname overruled.", InUse->nick.c_str());
-
- ServerInstance->Users->clientlist.erase(InUse->nick);
- ServerInstance->Users->clientlist[InUse->uuid] = InUse;
-
- InUse->nick = InUse->uuid;
- InUse->InvalidateCache();
- InUse->registered &= ~REG_NICK;
+ LocalUser* const localuser = static_cast<LocalUser*>(InUse);
+ localuser->OverruleNick();
}
else
{
/* No camping, tell the incoming user to stop trying to change nick ;p */
- this->WriteNumeric(ERR_NICKNAMEINUSE, "%s :Nickname is already in use.", newnick.c_str());
+ this->WriteNumeric(ERR_NICKNAMEINUSE, newnick, "Nickname is already in use.");
return false;
}
}
@@ -731,6 +657,16 @@ bool User::ChangeNick(const std::string& newnick, bool force, time_t newts)
return true;
}
+void LocalUser::OverruleNick()
+{
+ this->WriteFrom(this, "NICK %s", this->uuid.c_str());
+ this->WriteNumeric(ERR_NICKNAMEINUSE, this->nick, "Nickname overruled.");
+
+ // Clear the bit before calling ChangeNick() to make it NOT run the OnUserPostNick() hook
+ this->registered &= ~REG_NICK;
+ this->ChangeNick(this->uuid);
+}
+
int LocalUser::GetServerPort()
{
switch (this->server_sa.sa.sa_family)
@@ -774,8 +710,7 @@ irc::sockets::cidr_mask User::GetCIDRMask()
bool User::SetClientIP(const char* sip, bool recheck_eline)
{
- cachedip.clear();
- cached_hostip.clear();
+ this->InvalidateCache();
return irc::sockets::aptosa(sip, 0, client_sa);
}
@@ -827,7 +762,7 @@ void LocalUser::Write(const std::string& text)
if (text.length() > ServerInstance->Config->Limits.MaxLine - 2)
{
// this should happen rarely or never. Crop the string at 512 and try again.
- std::string try_again = text.substr(0, ServerInstance->Config->Limits.MaxLine - 2);
+ std::string try_again(text, 0, ServerInstance->Config->Limits.MaxLine - 2);
Write(try_again);
return;
}
@@ -837,7 +772,7 @@ void LocalUser::Write(const std::string& text)
eh.AddWriteBuf(text);
eh.AddWriteBuf(wide_newline);
- ServerInstance->stats->statsSent += text.length() + 2;
+ ServerInstance->stats.Sent += text.length() + 2;
this->bytes_out += text.length() + 2;
this->cmds_out++;
}
@@ -871,25 +806,33 @@ void User::WriteCommand(const char* command, const std::string& text)
this->WriteServ(command + (this->registered & REG_NICK ? " " + this->nick : " *") + " " + text);
}
-void User::WriteNumeric(unsigned int numeric, const char* text, ...)
+namespace
{
- std::string textbuffer;
- VAFORMAT(textbuffer, text, text);
- this->WriteNumeric(numeric, textbuffer);
+ std::string BuildNumeric(const std::string& source, User* targetuser, unsigned int num, const std::vector<std::string>& params)
+ {
+ const char* const target = (targetuser->registered & REG_NICK ? targetuser->nick.c_str() : "*");
+ std::string raw = InspIRCd::Format(":%s %03u %s", source.c_str(), num, target);
+ if (!params.empty())
+ {
+ for (std::vector<std::string>::const_iterator i = params.begin(); i != params.end()-1; ++i)
+ raw.append(1, ' ').append(*i);
+ raw.append(" :").append(params.back());
+ }
+ return raw;
+ }
}
-void User::WriteNumeric(unsigned int numeric, const std::string &text)
+void User::WriteNumeric(const Numeric::Numeric& numeric)
{
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric, text));
+ FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric));
if (MOD_RESULT == MOD_RES_DENY)
return;
- const std::string message = InspIRCd::Format(":%s %03u %s %s", ServerInstance->Config->ServerName.c_str(),
- numeric, this->registered & REG_NICK ? this->nick.c_str() : "*", text.c_str());
- this->Write(message);
+ const std::string& servername = (numeric.GetServer() ? numeric.GetServer()->GetName() : ServerInstance->Config->ServerName);
+ this->Write(BuildNumeric(servername, this, numeric.GetNumeric(), numeric.GetParams()));
}
void User::WriteFrom(User *user, const std::string &text)
@@ -908,133 +851,105 @@ void User::WriteFrom(User *user, const char* text, ...)
this->WriteFrom(user, textbuffer);
}
-void User::WriteCommon(const char* text, ...)
+void User::WriteRemoteNotice(const std::string& text)
{
- if (this->registered != REG_ALL || quitting)
- return;
-
- std::string textbuffer;
- VAFORMAT(textbuffer, text, text);
- textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
- this->WriteCommonRaw(textbuffer, true);
+ ServerInstance->PI->SendUserNotice(this, text);
}
-void User::WriteCommonRaw(const std::string &line, bool include_self)
+void LocalUser::WriteRemoteNotice(const std::string& text)
{
- if (this->registered != REG_ALL || quitting)
- return;
-
- LocalUser::already_sent_id++;
-
- IncludeChanList include_c(chans.begin(), chans.end());
- std::map<User*,bool> exceptions;
-
- exceptions[this] = include_self;
-
- FOREACH_MOD(OnBuildNeighborList, (this, include_c, exceptions));
+ WriteNotice(text);
+}
- for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
+namespace
+{
+ class WriteCommonRawHandler : public User::ForEachNeighborHandler
{
- LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting)
+ const std::string& msg;
+
+ void Execute(LocalUser* user) CXX11_OVERRIDE
{
- u->already_sent = LocalUser::already_sent_id;
- if (i->second)
- u->Write(line);
+ user->Write(msg);
}
- }
- for (IncludeChanList::const_iterator v = include_c.begin(); v != include_c.end(); ++v)
- {
- Channel* c = (*v)->chan;
- const UserMembList* ulist = c->GetUsers();
- for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
+
+ public:
+ WriteCommonRawHandler(const std::string& message)
+ : msg(message)
{
- LocalUser* u = IS_LOCAL(i->first);
- if (u && u->already_sent != LocalUser::already_sent_id)
- {
- u->already_sent = LocalUser::already_sent_id;
- u->Write(line);
- }
}
- }
+ };
}
-void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
+void User::WriteCommon(const char* text, ...)
{
- if (this->registered != REG_ALL)
- return;
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
+ this->WriteCommonRaw(textbuffer, true);
+}
- already_sent_t uniq_id = ++LocalUser::already_sent_id;
+void User::WriteCommonRaw(const std::string &line, bool include_self)
+{
+ WriteCommonRawHandler handler(line);
+ ForEachNeighbor(handler, include_self);
+}
- const std::string normalMessage = ":" + this->GetFullHost() + " QUIT :" + normal_text;
- const std::string operMessage = ":" + this->GetFullHost() + " QUIT :" + oper_text;
+void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self)
+{
+ // The basic logic for visiting the neighbors of a user is to iterate the channel list of the user
+ // and visit all users on those channels. Because two users may share more than one common channel,
+ // we must skip users that we have already visited.
+ // To do this, we make use of a global counter and an integral 'already_sent' field in LocalUser.
+ // The global counter is incremented every time we do something for each neighbor of a user. Then,
+ // before visiting a member we examine user->already_sent. If it's equal to the current counter, we
+ // skip the member. Otherwise, we set it to the current counter and visit the member.
- IncludeChanList include_c(chans.begin(), chans.end());
- std::map<User*,bool> exceptions;
+ // Ask modules to build a list of exceptions.
+ // Mods may also exclude entire channels by erasing them from include_chans.
+ IncludeChanList include_chans(chans.begin(), chans.end());
+ std::map<User*, bool> exceptions;
+ exceptions[this] = include_self;
+ FOREACH_MOD(OnBuildNeighborList, (this, include_chans, exceptions));
- FOREACH_MOD(OnBuildNeighborList, (this, include_c, exceptions));
+ // Get next id, guaranteed to differ from the already_sent field of all users
+ const already_sent_t newid = ServerInstance->Users.NextAlreadySentId();
- for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
+ // Handle exceptions first
+ for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
{
- LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting)
+ LocalUser* curr = IS_LOCAL(i->first);
+ if (curr)
{
- u->already_sent = uniq_id;
- if (i->second)
- u->Write(u->IsOper() ? operMessage : normalMessage);
+ // Mark as visited to ensure we won't visit again if there is a common channel
+ curr->already_sent = newid;
+ // Always treat quitting users as excluded
+ if ((i->second) && (!curr->quitting))
+ handler.Execute(curr);
}
}
- for (IncludeChanList::const_iterator v = include_c.begin(); v != include_c.end(); ++v)
+
+ // Now consider the real neighbors
+ for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
{
- const UserMembList* ulist = (*v)->chan->GetUsers();
- for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
+ Channel* chan = (*i)->chan;
+ const Channel::MemberMap& userlist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator j = userlist.begin(); j != userlist.end(); ++j)
{
- LocalUser* u = IS_LOCAL(i->first);
- if (u && (u->already_sent != uniq_id))
+ LocalUser* curr = IS_LOCAL(j->first);
+ // User not yet visited?
+ if ((curr) && (curr->already_sent != newid))
{
- u->already_sent = uniq_id;
- u->Write(u->IsOper() ? operMessage : normalMessage);
+ // Mark as visited and execute function
+ curr->already_sent = newid;
+ handler.Execute(curr);
}
}
}
}
-void LocalUser::SendText(const std::string& line)
-{
- Write(line);
-}
-
-void RemoteUser::SendText(const std::string& line)
-{
- ServerInstance->PI->PushToClient(this, line);
-}
-
-void FakeUser::SendText(const std::string& line)
-{
-}
-
-void User::SendText(const char *text, ...)
+void User::WriteRemoteNumeric(const Numeric::Numeric& numeric)
{
- std::string line;
- VAFORMAT(line, text, text);
- SendText(line);
-}
-
-void User::SendText(const std::string& linePrefix, std::stringstream& textStream)
-{
- std::string line;
- std::string word;
- while (textStream >> word)
- {
- size_t lineLength = linePrefix.length() + line.length() + word.length() + 3; // "\s\n\r"
- if (lineLength > ServerInstance->Config->Limits.MaxLine)
- {
- SendText(linePrefix + line);
- line.clear();
- }
- line += " " + word;
- }
- SendText(linePrefix + line);
+ WriteNumeric(numeric);
}
/* return 0 or 1 depending if users u and u2 share one or more common channels
@@ -1052,7 +967,7 @@ void User::SendText(const std::string& linePrefix, std::stringstream& textStream
bool User::SharesChannelWith(User *other)
{
/* Outer loop */
- for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
+ for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); ++i)
{
/* Eliminate the inner loop (which used to be ~equal in size to the outer loop)
* by replacing it with a map::find which *should* be more efficient
@@ -1100,7 +1015,7 @@ bool User::ChangeDisplayedHost(const std::string& shost)
this->InvalidateCache();
if (IS_LOCAL(this))
- this->WriteNumeric(RPL_YOURDISPLAYEDHOST, "%s :is now your displayed host", this->dhost.c_str());
+ this->WriteNumeric(RPL_YOURDISPLAYEDHOST, this->dhost, "is now your displayed host");
return true;
}
@@ -1133,7 +1048,7 @@ void LocalUser::SetClass(const std::string &explicit_name)
if (!explicit_name.empty())
{
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
{
ConnectClass* c = *i;
@@ -1146,7 +1061,7 @@ void LocalUser::SetClass(const std::string &explicit_name)
}
else
{
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
{
ConnectClass* c = *i;
ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Checking %s", c->GetName().c_str());
@@ -1188,14 +1103,14 @@ void LocalUser::SetClass(const std::string &explicit_name)
}
/* if it requires a port ... */
- int port = c->config->getInt("port");
- if (port)
+ if (!c->ports.empty())
{
- ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Requires port (%d)", port);
-
/* and our port doesn't match, fail. */
- if (this->GetServerPort() != port)
+ if (!c->ports.count(this->GetServerPort()))
+ {
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Requires a different port, skipping");
continue;
+ }
}
if (regdone && !c->config->getString("password").empty())
@@ -1225,7 +1140,7 @@ void LocalUser::SetClass(const std::string &explicit_name)
void User::PurgeEmptyChannels()
{
// firstly decrement the count on each channel
- for (UCListIter i = this->chans.begin(); i != this->chans.end(); )
+ for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); )
{
Channel* c = (*i)->chan;
++i;
@@ -1263,7 +1178,7 @@ ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask, cons
softsendqmax(parent.softsendqmax), hardsendqmax(parent.hardsendqmax), recvqmax(parent.recvqmax),
penaltythreshold(parent.penaltythreshold), commandrate(parent.commandrate),
maxlocal(parent.maxlocal), maxglobal(parent.maxglobal), maxconnwarn(parent.maxconnwarn), maxchans(parent.maxchans),
- limit(parent.limit), resolvehostnames(parent.resolvehostnames)
+ limit(parent.limit), resolvehostnames(parent.resolvehostnames), ports(parent.ports)
{
}
@@ -1287,4 +1202,5 @@ void ConnectClass::Update(const ConnectClass* src)
maxchans = src->maxchans;
limit = src->limit;
resolvehostnames = src->resolvehostnames;
+ ports = src->ports;
}
diff --git a/src/version.sh b/src/version.sh
index d307082f4..830f6d3dd 100755
--- a/src/version.sh
+++ b/src/version.sh
@@ -1,2 +1,2 @@
#!/bin/sh
-echo "InspIRCd-2.2.0+pre"
+echo "InspIRCd-3.0.0-a1"
diff --git a/src/wildcard.cpp b/src/wildcard.cpp
index b62fd8a61..6711f953a 100644
--- a/src/wildcard.cpp
+++ b/src/wildcard.cpp
@@ -20,8 +20,6 @@
#include "inspircd.h"
-#include "hashcomp.h"
-#include "inspstring.h"
static bool MatchInternal(const unsigned char* str, const unsigned char* mask, unsigned const char* map)
{
diff --git a/src/xline.cpp b/src/xline.cpp
index 20693b599..b116d2e1f 100644
--- a/src/xline.cpp
+++ b/src/xline.cpp
@@ -23,7 +23,6 @@
#include "inspircd.h"
#include "xline.h"
-#include "bancache.h"
/** An XLineFactory specialized to generate GLine* pointers
*/
@@ -156,7 +155,8 @@ void XLineManager::CheckELines()
if (ELines.empty())
return;
- for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
{
LocalUser* u = *u2;
@@ -259,7 +259,7 @@ bool XLineManager::AddLine(XLine* line, User* user)
ContainerIter x = lookup_lines.find(line->type);
if (x != lookup_lines.end())
{
- LookupIter i = x->second.find(line->Displayable().c_str());
+ LookupIter i = x->second.find(line->Displayable());
if (i != x->second.end())
{
// XLine propagation bug was here, if the line to be added already exists and
@@ -276,12 +276,12 @@ bool XLineManager::AddLine(XLine* line, User* user)
if (!xlf)
return false;
- ServerInstance->BanCache->RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
+ ServerInstance->BanCache.RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
if (xlf->AutoApplyToUserList(line))
pending_lines.push_back(line);
- lookup_lines[line->type][line->Displayable().c_str()] = line;
+ lookup_lines[line->type][line->Displayable()] = line;
line->OnAdd();
FOREACH_MOD(OnAddLine, (user, line));
@@ -306,15 +306,13 @@ bool XLineManager::DelLine(const char* hostmask, const std::string &type, User*
if (simulate)
return true;
- ServerInstance->BanCache->RemoveEntries(y->second->type, true);
+ ServerInstance->BanCache.RemoveEntries(y->second->type, true);
FOREACH_MOD(OnDelLine, (user, y->second));
y->second->Unset();
- std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), y->second);
- if (pptr != pending_lines.end())
- pending_lines.erase(pptr);
+ stdalgo::erase(pending_lines, y->second);
delete y->second;
x->second.erase(y);
@@ -326,7 +324,8 @@ bool XLineManager::DelLine(const char* hostmask, const std::string &type, User*
void ELine::Unset()
{
/* remove exempt from everyone and force recheck after deleting eline */
- for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
{
LocalUser* u = *u2;
u->exempt = false;
@@ -418,9 +417,7 @@ void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
* is pending, cleared when it is no longer pending, so we skip over this loop if its not pending?
* -- Brain
*/
- std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), item->second);
- if (pptr != pending_lines.end())
- pending_lines.erase(pptr);
+ stdalgo::erase(pending_lines, item->second);
delete item->second;
container->second.erase(item);
@@ -430,8 +427,8 @@ void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
// applies lines, removing clients and changing nicks etc as applicable
void XLineManager::ApplyLines()
{
- LocalUserList& list = ServerInstance->Users->local_users;
- for (LocalUserList::iterator j = list.begin(); j != list.end(); ++j)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator j = list.begin(); j != list.end(); ++j)
{
LocalUser* u = *j;
@@ -450,7 +447,7 @@ void XLineManager::ApplyLines()
pending_lines.clear();
}
-void XLineManager::InvokeStats(const std::string &type, int numeric, User* user, string_list &results)
+void XLineManager::InvokeStats(const std::string& type, unsigned int numeric, Stats::Context& stats)
{
ContainerIter n = lookup_lines.find(type);
@@ -471,7 +468,7 @@ void XLineManager::InvokeStats(const std::string &type, int numeric, User* user,
ExpireLine(n, i);
}
else
- results.push_back(ConvToStr(numeric)+" "+user->nick+" :"+i->second->Displayable()+" "+
+ stats.AddRow(numeric, i->second->Displayable()+" "+
ConvToStr(i->second->set_time)+" "+ConvToStr(i->second->duration)+" "+i->second->source+" :"+i->second->reason);
i = safei;
}
@@ -534,7 +531,7 @@ void XLine::DefaultApply(User* u, const std::string &line, bool bancache)
const std::string banReason = line + "-Lined: " + reason;
if (!ServerInstance->Config->XLineMessage.empty())
- u->WriteNotice("*** " + ServerInstance->Config->XLineMessage);
+ u->WriteNumeric(ERR_YOUREBANNEDCREEP, ServerInstance->Config->XLineMessage);
if (ServerInstance->Config->HideBans)
ServerInstance->Users->QuitUser(u, line + "-Lined", &banReason);
@@ -545,7 +542,7 @@ void XLine::DefaultApply(User* u, const std::string &line, bool bancache)
if (bancache)
{
ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding positive hit (" + line + ") for " + u->GetIPString());
- ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, banReason, this->duration);
+ ServerInstance->BanCache.AddHit(u->GetIPString(), this->type, banReason, this->duration);
}
}
@@ -642,7 +639,7 @@ bool QLine::Matches(User *u)
void QLine::Apply(User* u)
{
/* Force to uuid on apply of qline, no need to disconnect any more :) */
- u->ForceNickChange(u->uuid);
+ u->ChangeNick(u->uuid);
}
@@ -680,7 +677,8 @@ bool GLine::Matches(const std::string &str)
void ELine::OnAdd()
{
/* When adding one eline, only check the one eline */
- for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
{
LocalUser* u = *u2;
if (this->Matches(u))