summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--.github/ISSUE_TEMPLATE.md40
-rw-r--r--.gitignore16
-rw-r--r--.travis.yml13
-rw-r--r--README.md2
-rwxr-xr-xconfigure576
-rw-r--r--docs/Doxyfile1644
-rw-r--r--docs/conf/aliases/anope.conf.example27
-rw-r--r--docs/conf/aliases/atheme.conf.example25
-rw-r--r--docs/conf/aliases/ircservices.conf.example21
-rw-r--r--docs/conf/aliases/neostats.conf.example35
-rw-r--r--docs/conf/censor.conf.example2
-rw-r--r--docs/conf/filter.conf.example13
-rw-r--r--docs/conf/helpop-full.conf.example395
-rw-r--r--docs/conf/helpop.conf.example181
-rw-r--r--docs/conf/inspircd.conf.example164
-rw-r--r--docs/conf/links.conf.example42
-rw-r--r--docs/conf/modules.conf.example796
-rw-r--r--docs/conf/modules/charybdis.conf.example152
-rw-r--r--docs/conf/modules/unrealircd.conf.example240
-rw-r--r--docs/conf/motd.txt.example6
-rw-r--r--docs/conf/opermotd.txt.example6
-rw-r--r--docs/conf/opers.conf.example44
-rw-r--r--docs/conf/services/anope.conf.example9
-rw-r--r--docs/conf/services/atheme.conf.example52
-rw-r--r--docs/conf/services/generic.conf.example45
-rw-r--r--extras/m_sqloper.mssql.sql8
-rw-r--r--extras/m_sqloper.mysql.sql19
-rw-r--r--extras/m_sqloper.postgresql.sql8
-rw-r--r--extras/m_sqloper.sqlite3.sql4
-rw-r--r--include/aligned_storage.h (renamed from src/modes/cmode_o.cpp)44
-rw-r--r--include/bancache.h20
-rw-r--r--include/base.h10
-rw-r--r--include/builtinmodes.h77
-rw-r--r--include/caller.h38
-rw-r--r--include/channels.h55
-rw-r--r--include/command_parse.h14
-rw-r--r--include/commands/cmd_whowas.h226
-rw-r--r--include/compat.h4
-rw-r--r--include/configparser.h2
-rw-r--r--include/configreader.h53
-rw-r--r--include/consolecolors.h36
-rw-r--r--include/convto.h110
-rw-r--r--include/ctables.h36
-rw-r--r--include/dynref.h38
-rw-r--r--include/event.h146
-rw-r--r--include/extensible.h47
-rw-r--r--include/flat_map.h383
-rw-r--r--include/hashcomp.h231
-rw-r--r--include/inspircd.h217
-rw-r--r--include/inspsocket.h192
-rw-r--r--include/intrusive_list.h125
-rw-r--r--include/intrusive_list_impl.h172
-rw-r--r--include/iohook.h118
-rw-r--r--include/isupportmanager.h45
-rw-r--r--include/listmode.h8
-rw-r--r--include/logger.h10
-rw-r--r--include/membership.h115
-rw-r--r--include/mode.h168
-rw-r--r--include/modechange.h110
-rw-r--r--include/modules.h177
-rw-r--r--include/modules/account.h27
-rw-r--r--include/modules/cap.h326
-rw-r--r--include/modules/dns.h49
-rw-r--r--include/modules/hash.h34
-rw-r--r--include/modules/httpd.h29
-rw-r--r--include/modules/invite.h128
-rw-r--r--include/modules/ircv3.h (renamed from src/modes/cmode_v.cpp)39
-rw-r--r--include/modules/ldap.h47
-rw-r--r--include/modules/reload.h80
-rw-r--r--include/modules/sasl.h12
-rw-r--r--include/modules/spanningtree.h29
-rw-r--r--include/modules/ssl.h44
-rw-r--r--include/modules/stats.h173
-rw-r--r--include/modules/whois.h146
-rw-r--r--include/numeric.h103
-rw-r--r--include/numericbuilder.h198
-rw-r--r--include/numerics.h12
-rw-r--r--include/parammode.h2
-rw-r--r--include/protocol.h21
-rw-r--r--include/server.h9
-rw-r--r--include/snomasks.h2
-rw-r--r--include/socket.h28
-rw-r--r--include/socketengine.h120
-rw-r--r--include/stdalgo.h97
-rw-r--r--include/threadengine.h20
-rw-r--r--include/threadengines/threadengine_pthread.h54
-rw-r--r--include/threadengines/threadengine_win32.h52
-rw-r--r--include/timer.h12
-rw-r--r--include/typedefs.h38
-rw-r--r--include/usermanager.h65
-rw-r--r--include/users.h240
-rw-r--r--include/xline.h5
-rwxr-xr-xmake/calcdep.pl108
-rw-r--r--make/common.pm131
-rw-r--r--make/configure.pm275
-rw-r--r--make/console.pm159
-rw-r--r--make/directive.pm271
-rwxr-xr-xmake/run-cc.pl237
-rw-r--r--make/template/config.h3
-rw-r--r--make/template/inspircd71
-rw-r--r--make/template/inspircd-genssl.146
-rw-r--r--make/template/inspircd.1104
-rw-r--r--make/template/inspircd.service35
-rw-r--r--make/template/main.mk137
-rw-r--r--make/template/org.inspircd.plist12
-rw-r--r--make/test/clock_gettime.cpp1
-rw-r--r--make/test/compiler.cpp39
-rw-r--r--make/test/compiler_info.cpp41
-rw-r--r--make/test/eventfd.cpp2
-rw-r--r--make/test/kqueue.cpp1
-rwxr-xr-xmake/unit-cc.pl64
-rw-r--r--make/utilities.pm516
-rwxr-xr-xmodulemanager90
-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.cpp48
-rw-r--r--src/modules/m_spanningtree/translate.h30
-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
-rwxr-xr-xtools/genssl33
-rwxr-xr-xtools/test-build53
-rwxr-xr-xtools/travis-ci.sh12
-rw-r--r--win/CMakeLists.txt43
-rw-r--r--win/config.h.cmake15
-rw-r--r--win/inspircd.rc.cmake10
-rw-r--r--win/inspircd_win32wrapper.cpp13
-rw-r--r--win/inspircd_win32wrapper.h35
-rw-r--r--win/make_gnutls_cert.bat26
-rw-r--r--win/modules/CMakeLists.txt2
436 files changed, 19959 insertions, 13333 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..ec1c66dc1
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.bat text eol=crlf
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 000000000..6fdf33bba
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,40 @@
+<!--
+---------------------------------------------------
+GENERAL SUPPORT INFORMATION
+---------------------------------------------------
+
+The GitHub issue tracker is for bug reports and feature requests.
+General support can be found at the following locations:
+
+IRC:
+irc.inspircd.org #inspircd
+
+Example configs:
+2.0 - https://github.com/inspircd/inspircd/tree/insp20/docs/conf
+3.0 (alpha) - https://github.com/inspircd/inspircd/tree/master/docs/conf
+
+Wiki:
+https://wiki.inspircd.org/
+-->
+
+**Description**
+
+<!--
+Briefly describe the problem you are having in a few paragraphs.
+-->
+
+**Steps to reproduce the issue:**
+1.
+2.
+3.
+
+**Describe the results you received:**
+
+
+**Describe the results you expected:**
+
+
+**Additional information you deem important (e.g. issue happens only occasionally):**
+
+**Output of `./bin/inspircd --version`:**
+
diff --git a/.gitignore b/.gitignore
index d473e66cc..3aea64f53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,23 +2,20 @@
*.pem
*.swp
-/.config.cache
-/.gdbargs
-/.modulemanager
+.*
+!.git*
+
+/.configure
/BSDmakefile
/GNUmakefile
/build
-/inspircd
-/org.inspircd.plist
+/docs/doxygen
/run
-/bin
/include/config.h
/src/modules/m_geoip.cpp
-/src/modules/m_ldapauth.cpp
-/src/modules/m_ldapoper.cpp
-/src/modules/m_mssql.cpp
+/src/modules/m_ldap.cpp
/src/modules/m_mysql.cpp
/src/modules/m_pgsql.cpp
/src/modules/m_regex_pcre.cpp
@@ -28,5 +25,6 @@
/src/modules/m_regex_tre.cpp
/src/modules/m_sqlite3.cpp
/src/modules/m_ssl_gnutls.cpp
+/src/modules/m_ssl_mbedtls.cpp
/src/modules/m_ssl_openssl.cpp
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..f5c1fff54
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+compiler:
+ - clang
+ - gcc
+dist: trusty
+env:
+ - TEST_BUILD_DYNAMIC=1
+ - TEST_BUILD_STATIC=1
+language: cpp
+notifications:
+ email: false
+script:
+ - sh ./tools/travis-ci.sh
+sudo: required
diff --git a/README.md b/README.md
index 89524e7e7..6e6110696 100644
--- a/README.md
+++ b/README.md
@@ -25,4 +25,4 @@ many different users.
* [Website](http://inspircd.org)
* [GitHub](https://github.com/inspircd)
-* [IRC](irc://irc.chatspike.net/inspircd) \ No newline at end of file
+* IRC: \#inspircd on irc.inspircd.org
diff --git a/configure b/configure
index 45bb0e911..ca2e2f19b 100755
--- a/configure
+++ b/configure
@@ -3,6 +3,7 @@
#
# InspIRCd -- Internet Relay Chat Daemon
#
+# Copyright (C) 2012-2017 Peter Powell <petpow@saberuk.com>
# Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
# Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
# Copyright (C) 2003, 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
@@ -27,23 +28,40 @@
BEGIN {
- require 5.8.0;
+ require 5.10.0;
}
+use feature ':5.10';
use strict;
use warnings FATAL => qw(all);
-use File::Copy ();
+use File::Basename qw(basename);
+use File::Copy ();
use File::Spec::Functions qw(rel2abs);
-use Cwd;
-use Getopt::Long;
+use FindBin qw($RealDir);
+use Getopt::Long qw(GetOptions);
+use POSIX qw(getgid getuid);
+use lib $RealDir;
+use make::common;
use make::configure;
-use make::utilities;
-
-our ($opt_use_gnutls, $opt_use_openssl, $opt_nointeractive, $opt_socketengine,
- $opt_system, $opt_uid, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir,
- $opt_data_dir, $opt_log_dir);
+use make::console;
+use make::directive;
+
+my ($opt_binary_dir,
+ $opt_config_dir,
+ $opt_data_dir,
+ $opt_development,
+ $opt_disable_interactive,
+ $opt_distribution_label,
+ $opt_gid,
+ $opt_log_dir,
+ $opt_manual_dir,
+ $opt_module_dir,
+ $opt_prefix,
+ $opt_socketengine,
+ $opt_system,
+ $opt_uid);
sub list_extras ();
@@ -54,25 +72,30 @@ sub disable_extras (@);
my @opt_enableextras;
my @opt_disableextras;
-GetOptions (
- 'enable-gnutls' => \$opt_use_gnutls,
- 'system' => \$opt_system,
- 'uid=s' => \$opt_uid,
- 'enable-openssl' => \$opt_use_openssl,
- 'disable-interactive' => \$opt_nointeractive,
- 'socketengine=s' => \$opt_socketengine,
- 'prefix=s' => \$opt_base_dir,
- 'config-dir=s' => \$opt_config_dir,
- 'module-dir=s' => \$opt_module_dir,
- 'binary-dir=s' => \$opt_binary_dir,
- 'data-dir=s' => \$opt_data_dir,
- 'log-dir=s' => \$opt_log_dir,
- 'help' => \&cmd_help,
+GetOptions(
+ 'clean' => \&cmd_clean,
+ 'help' => \&cmd_help,
'update' => \&cmd_update,
- 'clean' => \&cmd_clean,
- 'list-extras' => sub { list_extras; exit 0; }, # This, --enable-extras, and --disable-extras are for non-interactive managing.
- 'enable-extras=s@' => \@opt_enableextras, # ^
- 'disable-extras=s@' => \@opt_disableextras, # ^
+
+ 'development' => \$opt_development,
+ 'disable-interactive' => \$opt_disable_interactive,
+ 'distribution-label=s' => \$opt_distribution_label,
+ 'binary-dir=s' => \$opt_binary_dir,
+ 'config-dir=s' => \$opt_config_dir,
+ 'data-dir=s' => \$opt_data_dir,
+ 'gid=s' => \$opt_gid,
+ 'log-dir=s' => \$opt_log_dir,
+ 'manual-dir=s' => \$opt_manual_dir,
+ 'module-dir=s' => \$opt_module_dir,
+ 'prefix=s' => \$opt_prefix,
+ 'socketengine=s' => \$opt_socketengine,
+ 'system' => \$opt_system,
+ 'uid=s' => \$opt_uid,
+
+ # TODO: when the modulemanager rewrite is done these should be removed.
+ 'disable-extras=s@' => \@opt_disableextras,
+ 'enable-extras=s@' => \@opt_enableextras,
+ 'list-extras' => sub { list_extras; exit 0; },
);
if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
@@ -86,348 +109,239 @@ if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
}
our $interactive = !(
- (defined $opt_base_dir) ||
- (defined $opt_config_dir) ||
- (defined $opt_module_dir) ||
- (defined $opt_base_dir) ||
- (defined $opt_binary_dir) ||
- (defined $opt_data_dir) ||
- (defined $opt_log_dir) ||
- (defined $opt_nointeractive) ||
- (defined $opt_socketengine) ||
- (defined $opt_use_openssl) ||
- (defined $opt_system) ||
- (defined $opt_uid) ||
- (defined $opt_use_gnutls)
+ !-t STDIN ||
+ !-t STDOUT ||
+ defined $opt_binary_dir ||
+ defined $opt_config_dir ||
+ defined $opt_data_dir ||
+ defined $opt_development ||
+ defined $opt_disable_interactive ||
+ defined $opt_distribution_label ||
+ defined $opt_gid ||
+ defined $opt_log_dir ||
+ defined $opt_manual_dir ||
+ defined $opt_module_dir ||
+ defined $opt_prefix ||
+ defined $opt_socketengine ||
+ defined $opt_system ||
+ defined $opt_uid
);
-our %config = read_configure_cache();
-
-print "Checking for cache from previous configure... ";
-print %config ? "found\n" : "not found\n";
-
-$config{BASE_DIR} = getcwd()."/run";
-
-if (defined $opt_base_dir) {
- $config{BASE_DIR} = $opt_base_dir;
-} elsif (defined $opt_system) {
- $config{BASE_DIR} = '/var/lib/inspircd';
-}
-
-if (defined $opt_system) {
- $config{UID} = defined $opt_uid ? $opt_uid : 'ircd';
- $config{CONFIG_DIR} = '/etc/inspircd';
- $config{MODULE_DIR} = '/usr/lib/inspircd';
- $config{BINARY_DIR} = '/usr/sbin/';
- $config{DATA_DIR} = '/var/inspircd';
- $config{LOG_DIR} = '/var/log/inspircd';
-} else {
- $config{UID} = defined $opt_uid ? $opt_uid : $<;
- $config{CONFIG_DIR} = rel2abs($config{BASE_DIR}."/conf");
- $config{MODULE_DIR} = rel2abs($config{BASE_DIR}."/modules");
- $config{BINARY_DIR} = rel2abs($config{BASE_DIR}."/bin");
- $config{DATA_DIR} = rel2abs($config{BASE_DIR}."/data");
- $config{LOG_DIR} = rel2abs($config{BASE_DIR}."/logs");
+my %version = get_version $opt_distribution_label;
+print_format "<|BOLD Configuring InspIRCd $version{FULL} on $^O.|>\n";
+
+my %config;
+if ($interactive) {
+ %config = read_config_file(CONFIGURE_CACHE_FILE);
+ run_test CONFIGURE_CACHE_FILE, %config;
+ if (!defined $config{VERSION}) {
+ $config{VERSION} = CONFIGURE_CACHE_VERSION;
+ } elsif ($config{VERSION} != CONFIGURE_CACHE_VERSION) {
+ print_warning "ignoring contents of ${\CONFIGURE_CACHE_FILE} as it was generated by an incompatible version of $0!";
+ %config = ('VERSION', CONFIGURE_CACHE_VERSION);
+ }
}
-if (defined $opt_config_dir) {
- $config{CONFIG_DIR} = $opt_config_dir;
-}
-if (defined $opt_module_dir) {
- $config{MODULE_DIR} = $opt_module_dir;
-}
-if (defined $opt_binary_dir) {
- $config{BINARY_DIR} = $opt_binary_dir;
-}
-if (defined $opt_data_dir) {
- $config{DATA_DIR} = $opt_data_dir;
-}
-if (defined $opt_log_dir) {
- $config{LOG_DIR} = $opt_log_dir;
+$config{CXX} = find_compiler($config{CXX} // $ENV{CXX});
+unless ($config{CXX}) {
+ say 'A suitable C++ compiler could not be detected on your system!';
+ unless ($interactive) {
+ say 'Set the CXX environment variable to the path to a C++ compiler binary if this is incorrect.';
+ exit 1;
+ }
+ until ($config{CXX}) {
+ my $compiler_path = prompt_string 1, 'Please enter the path to a C++ compiler binary:', 'c++';
+ $config{CXX} = find_compiler $compiler_path;
+ }
}
-chomp($config{HAS_GNUTLS} = `pkg-config --modversion gnutls 2>/dev/null`);
-chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
+my %compiler = get_compiler_info($config{CXX});
-chomp(our $gnutls_ver = $config{HAS_GNUTLS});
-chomp(our $openssl_ver = $config{HAS_OPENSSL});
-$config{USE_GNUTLS} = 0;
-if (defined $opt_use_gnutls)
-{
- $config{USE_GNUTLS} = "y"; # Use gnutls.
-}
-$config{USE_OPENSSL} = 0; # Use openssl.
-if (defined $opt_use_openssl)
-{
- $config{USE_OPENSSL} = "y";
-}
+$config{HAS_CLOCK_GETTIME} = run_test 'clock_gettime()', test_file($config{CXX}, 'clock_gettime.cpp', $^O eq 'darwin' ? undef : '-lrt');
+$config{HAS_EVENTFD} = run_test 'eventfd()', test_file($config{CXX}, 'eventfd.cpp');
-$config{CXX} = defined $ENV{CXX} && !system("$ENV{CXX} -v > /dev/null 2>&1") ? $ENV{CXX} : find_compiler();
-if ($config{CXX} eq "") {
- print "A C++ compiler could not be detected on your system!\n";
- print "Set the CXX environment variable to the full path if this is incorrect.\n";
- exit 1;
-}
+my @socketengines;
+push @socketengines, 'epoll' if run_test 'epoll', test_header $config{CXX}, 'sys/epoll.h';
+push @socketengines, 'kqueue' if run_test 'kqueue', test_file $config{CXX}, 'kqueue.cpp';
+push @socketengines, 'ports' if run_test 'Solaris IOCP', test_header $config{CXX}, 'port.h';
+push @socketengines, 'poll' if run_test 'poll', test_header $config{CXX}, 'poll.h';
+push @socketengines, 'select';
-our %cxx = get_compiler_info($config{CXX});
-if ($cxx{UNSUPPORTED}) {
- print "Your C++ compiler is too old to build InspIRCd!\n";
- print "Reason: $cxx{REASON}\n";
- exit 1;
+if (defined $opt_socketengine) {
+ unless (grep { $_ eq $opt_socketengine } @socketengines) {
+ my $reason = -f "src/socketengines/socketengine_$opt_socketengine.cpp" ? 'is not available on this platform' : 'does not exist';
+ print_error "The socket engine you requested ($opt_socketengine) $reason!",
+ 'Available socket engines are:',
+ map { " * $_" } @socketengines;
+ }
}
+$config{SOCKETENGINE} = $opt_socketengine // $socketengines[0];
-if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)(?:[a-z])?(?:\-[a-z][0-9])?/) {
- $config{HAS_OPENSSL} = $1;
+if (defined $opt_system) {
+ $config{BASE_DIR} = $opt_prefix // '/var/lib/inspircd';
+ $config{BINARY_DIR} = $opt_binary_dir // '/usr/sbin';
+ $config{CONFIG_DIR} = $opt_config_dir // '/etc/inspircd';
+ $config{DATA_DIR} = $opt_data_dir // '/var/inspircd';
+ $config{LOG_DIR} = $opt_module_dir // '/var/log/inspircd';
+ $config{MANUAL_DIR} = $opt_manual_dir // '/usr/share/man/man1';
+ $config{MODULE_DIR} = $opt_module_dir // '/usr/lib/inspircd';
} else {
- $config{HAS_OPENSSL} = "";
-}
-
-$config{HAS_CLOCK_GETTIME} = run_test 'clock_gettime()', test_file($config{CXX}, 'clock_gettime.cpp', '-lrt');
-$config{HAS_EVENTFD} = run_test 'eventfd()', test_file($config{CXX}, 'eventfd.cpp');
-
-if ($config{HAS_EPOLL} = run_test 'epoll', test_header($config{CXX}, 'sys/epoll.h')) {
- $config{SOCKETENGINE} ||= 'epoll';
+ $config{BASE_DIR} = $opt_prefix // $config{BASE_DIR} // rel2abs 'run';
+ $config{BINARY_DIR} = $opt_binary_dir // $config{BINARY_DIR} // rel2abs $config{BASE_DIR} . '/bin';
+ $config{CONFIG_DIR} = $opt_config_dir // $config{CONFIG_DIR} // rel2abs $config{BASE_DIR} . '/conf';
+ $config{DATA_DIR} = $opt_data_dir // $config{DATA_DIR} // rel2abs $config{BASE_DIR} . '/data';
+ $config{LOG_DIR} = $opt_log_dir // $config{LOG_DIR} // rel2abs $config{BASE_DIR} . '/logs';
+ $config{MANUAL_DIR} = $opt_manual_dir // $config{MANUAL_DIR} // rel2abs $config{BASE_DIR} . '/manuals';
+ $config{MODULE_DIR} = $opt_module_dir // $config{MODULE_DIR} // rel2abs $config{BASE_DIR} . '/modules';
}
-if ($config{HAS_KQUEUE} = run_test 'kqueue', test_file($config{CXX}, 'kqueue.cpp')) {
- $config{SOCKETENGINE} ||= 'kqueue';
+# Parse --gid=123 or --gid=foo and extract the group id.
+my @group;
+if (defined $opt_gid) {
+ @group = $opt_gid =~ /^\d+$/ ? getgrgid($opt_gid) : getgrnam($opt_gid);
+ print_error "there is no '$opt_gid' group on this system!" unless @group;
+} else {
+ @group = $opt_system ? getgrnam('irc') : getgrgid($config{GID} // getgid());
+ print_error "you need to specify a group to run as using '--gid [id|name]'!" unless @group;
}
-
-if ($config{HAS_PORTS} = run_test 'Solaris IOCP', test_header($config{CXX}, 'port.h')) {
- $config{SOCKETENGINE} ||= 'ports';
+$config{GROUP} = $group[0];
+$config{GID} = $group[2];
+
+# Parse --uid=123 or --uid=foo and extract the user id.
+my @user;
+if (defined $opt_uid) {
+ @user = $opt_uid =~ /^\d+$/ ? getpwuid($opt_uid) : getpwnam($opt_uid);
+ print_error "there is no '$opt_uid' user on this system!" unless @user;
+} else {
+ @user = $opt_system ? getpwnam('irc') : getpwuid($config{UID} // getuid());
+ print_error "you need to specify a user to run as using '--uid [id|name]'!" unless @user;
}
-
-if ($config{HAS_POLL} = run_test 'poll', test_header($config{CXX}, 'poll.h')) {
- $config{SOCKETENGINE} ||= 'poll';
+$config{USER} = $user[0];
+$config{UID} = $user[2];
+
+# Clear the screen.
+system 'tput', 'clear' if $interactive;
+
+# Warn the user about clock drifting when running on OpenVZ.
+if (-e '/proc/user_beancounters' || -e '/proc/vz/vzaquota') {
+ print_warning <<'EOW';
+You are building InspIRCd inside of an an OpenVZ container. If you
+plan to use InspIRCd in this container then you should make sure that NTP is
+configured on the Hardware Node. Failure to do so may result in clock drifting!
+EOW
}
-# Select is available on all platforms
-$config{HAS_SELECT} = 1;
-$config{SOCKETENGINE} ||= "select";
+# Check that the user actually wants this version.
+if ($version{LABEL} ne 'release') {
+ print_warning <<'EOW';
+You are building a development version. This contains code which has
+not been tested as heavily and may contain various faults which could seriously
+affect the running of your server. It is recommended that you use a stable
+version instead.
-if (defined $opt_socketengine) {
- my $cfgkey = "HAS_" . uc $opt_socketengine;
- if ($config{$cfgkey} && -f "src/socketengines/socketengine_$opt_socketengine.cpp") {
- $config{SOCKETENGINE} = $opt_socketengine;
- } else {
- print "Unable to use a socket engine which is not supported on this platform ($opt_socketengine)!\n";
- print "Available socket engines are:";
- foreach (<src/socketengines/socketengine_*.cpp>) {
- s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
- print " $1" if $config{"HAS_" . uc $1};
- }
- print "\n";
+You can obtain the latest stable version from http://www.inspircd.org/ or by
+running `git checkout insp20` if you are installing from Git.
+EOW
+ if (!prompt_bool $interactive, 'I understand this warning and want to continue anyway.', $opt_development // 0) {
+ say STDERR 'If you understand this warning and still want to continue pass the --development flag.' unless $interactive;
exit 1;
}
}
-print "Checking for libgnutls... ";
-if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) {
- if (defined($gnutls_ver) && ($gnutls_ver ne "")) {
- print "yes\n";
- $config{HAS_GNUTLS} = "y";
- } else {
- print "no\n";
- $config{HAS_GNUTLS} = "n";
+# Configure directory settings.
+my $question = <<"EOQ";
+Currently, InspIRCd is configured with the following paths:
+
+<|BOLD Base:|> $config{BASE_DIR}
+<|BOLD Binary:|> $config{BINARY_DIR}
+<|BOLD Config:|> $config{CONFIG_DIR}
+<|BOLD Data:|> $config{DATA_DIR}
+<|BOLD Log:|> $config{LOG_DIR}
+<|BOLD Manual:|> $config{MANUAL_DIR}
+<|BOLD Module:|> $config{MODULE_DIR}
+
+Do you want to change these settings?
+EOQ
+if (prompt_bool $interactive, $question, 0) {
+ my $original_base_dir = $config{BASE_DIR};
+ $config{BASE_DIR} = prompt_dir $interactive, 'In what directory do you wish to install the InspIRCd base?', $config{BASE_DIR};
+ foreach my $key (qw(BINARY_DIR CONFIG_DIR DATA_DIR LOG_DIR MANUAL_DIR MODULE_DIR)) {
+ $config{$key} =~ s/^\Q$original_base_dir\E/$config{BASE_DIR}/;
}
-} else {
- print "no\n";
- $config{HAS_GNUTLS} = "n";
+ $config{BINARY_DIR} = prompt_dir $interactive, 'In what directory should the InspIRCd binary be placed?', $config{BINARY_DIR};
+ $config{CONFIG_DIR} = prompt_dir $interactive, 'In what directory are configuration files to be stored?', $config{CONFIG_DIR};
+ $config{DATA_DIR} = prompt_dir $interactive, 'In what directory are variable data files to be stored?', $config{DATA_DIR};
+ $config{LOG_DIR} = prompt_dir $interactive, 'In what directory are log files to be stored?', $config{LOG_DIR};
+ $config{MANUAL_DIR} = prompt_dir $interactive, 'In what directory are manual pages to be placed?', $config{MANUAL_DIR};
+ $config{MODULE_DIR} = prompt_dir $interactive, 'In what directory are modules to be placed?', $config{MODULE_DIR};
}
-print "Checking for openssl... ";
-if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) {
- if (defined($openssl_ver) && ($openssl_ver ne "")) {
- print "yes\n";
- $config{HAS_OPENSSL} = "y";
- } else {
- print "no\n";
- $config{HAS_OPENSSL} = "n";
+# Configure module settings.
+$question = <<'EOQ';
+Currently, InspIRCd is configured to automatically enable all available extra modules.
+
+Would you like to enable extra modules manually?
+EOQ
+if (prompt_bool $interactive, $question, 0) {
+ foreach my $extra (<src/modules/extra/m_*.cpp>) {
+ my $module_name = basename $extra, '.cpp';
+ if (prompt_bool $interactive, "Would you like to enable $module_name?", 0) {
+ enable_extras "$module_name.cpp";
+ }
}
} else {
- print "no\n";
- $config{HAS_OPENSSL} = "n";
+ # TODO: finish modulemanager rewrite and replace this code with:
+ # system './modulemanager', 'enable', '--auto';
+ enable_extras 'm_ssl_gnutls.cpp' unless system 'pkg-config --exists gnutls >/dev/null 2>&1';
+ enable_extras 'm_ssl_mbedtls.cpp' if -e '/usr/include/mbedtls/ssl.h';
+ enable_extras 'm_ssl_openssl.cpp' unless system 'pkg-config --exists openssl >/dev/null 2>&1';
}
-if ($interactive)
-{
- # Clear the screen.
- system 'tput', 'clear';
-
- my %version = get_version();
-
- # Display Introduction Message..
- print <<"STOP" ;
-Welcome to the \e[1mInspIRCd\e[0m configuration program! (\e[1minteractive mode\e[0m)
-\e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m
-
-*** If you are unsure of any of these values, leave it blank for ***
-*** standard settings that will work, and your server will run ***
-*** using them. Please consult your IRC network admin if in doubt. ***
-
-Press \e[1m<RETURN>\e[0m to accept the default for any option, or enter
-a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
-dir, otherwise you won't have a config file!
-
-Your operating system is: \e[1;32m$^O\e[0m
-STOP
- print "Your InspIRCd version is: \e[1;32m";
- print "$version{MAJOR}.$version{MINOR}.$version{PATCH}+$version{LABEL}";
- print "\e[0m\n\n";
- print "The following compiler has been detected: \e[1;32m$cxx{NAME} $cxx{VERSION}\e[0m ($config{CXX})\n\n";
-
- # Check that the user actually wants this version.
- if ($version{LABEL} ne 'release') {
- print <<"EOW" ;
-\e[1;31mWARNING!\e[0m You are building a development version. This contains code which has
-not been tested as heavily and may contain various faults which could seriously
-affect the running of your server. It is recommended that you use a stable
-version instead.
+# Generate SSL certificates.
+if (<src/modules/m_ssl_*.cpp> && prompt_bool $interactive, 'Would you like to generate SSL certificates now?', $interactive) {
+ system './tools/genssl', 'auto';
+}
-You can obtain the latest stable version from https://github.com/inspircd/inspircd/releases
-or by running `git checkout insp20` if you are installing from Git.
+# Cache the distribution label so that its not lost when --update is run.
+$config{DISTRIBUTION} = $opt_distribution_label if $opt_distribution_label;
-EOW
- exit 1 unless prompt_bool(1, 'I understand this warning and want to continue anyway.', 0);
- }
+write_configure_cache %config;
+parse_templates \%config, \%compiler, \%version;
- # Directory Settings..
- my $tmpbase = $config{BASE_DIR};
- $config{BASE_DIR} = prompt_dir(1, 'What directory do you wish to install the InspIRCd base?', $config{BASE_DIR});
- if ($tmpbase ne $config{BASE_DIR}) {
- $config{CONFIG_DIR} = rel2abs($config{BASE_DIR}."/conf");
- $config{MODULE_DIR} = rel2abs($config{BASE_DIR}."/modules");
- $config{DATA_DIR} = rel2abs($config{BASE_DIR}."/data");
- $config{LOG_DIR} = rel2abs($config{BASE_DIR}."/logs");
- $config{BINARY_DIR} = rel2abs($config{BASE_DIR}."/bin");
- }
+print_format <<"EOM";
- $config{BINARY_DIR} = prompt_dir(1, 'In what directory should the InspIRCd binary be placed?', $config{BINARY_DIR});
- $config{CONFIG_DIR} = prompt_dir(1, 'In what directory are the configuration files to be stored?', $config{CONFIG_DIR});
- $config{DATA_DIR} = prompt_dir(1, 'In what directory are variable data files to be stored?', $config{DATA_DIR});
- $config{LOG_DIR} = prompt_dir(1, 'In what directory are log files to be stored?', $config{LOG_DIR});
- $config{MODULE_DIR} = prompt_dir(1, 'In what directory are the modules to be placed?', $config{MODULE_DIR});
-
- my $chose_hiperf = 0;
- if ($config{HAS_KQUEUE}) {
- $config{USE_KQUEUE} = prompt_bool(1, 'Your operating system has support for the high performance kqueue socket engine. Would you like to enable it?', 1);
- if ($config{USE_KQUEUE}) {
- $config{SOCKETENGINE} = "kqueue";
- $chose_hiperf = 1;
- }
- }
- if ($config{HAS_EPOLL}) {
- $config{USE_EPOLL} = prompt_bool(1, 'Your operating system has support for the high performance epoll socket engine. Would you like to enable it?', 1);
- if ($config{USE_EPOLL}) {
- $config{SOCKETENGINE} = "epoll";
- $chose_hiperf = 1;
- }
- }
- if ($config{HAS_PORTS}) {
- $config{USE_PORTS} = prompt_bool(1, 'Your operating system has support for the high performance IOCP socket engine. Would you like to enable it?', 1);
- if ($config{USE_PORTS}) {
- $config{SOCKETENGINE} = "ports";
- $chose_hiperf = 1;
- }
- }
+Configuration is complete! You have chosen to build with the following settings:
- if (!$chose_hiperf && $config{HAS_POLL}) {
- $config{USE_POLL} = prompt_bool(1, 'Your operating system has support for the mid performance poll socket engine. Would you like to enable it?', 1);
- if ($config{USE_POLL}) {
- $config{SOCKETENGINE} = "poll";
- }
- }
- unless ($chose_hiperf || $config{USE_POLL})
- {
- print "No high-performance socket engines are available, or you chose not to enable one. Defaulting to select() engine.\n\n";
- $config{SOCKETENGINE} = "select";
- }
+<|GREEN Compiler:|>
+ <|GREEN Binary:|> $config{CXX}
+ <|GREEN Name:|> $compiler{NAME}
+ <|GREEN Version:|> $compiler{VERSION}
- if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
- {
- print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n";
- print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n";
-
- $config{USE_SSL} = prompt_bool(1, 'One or more SSL libraries detected. Would you like to enable SSL support?', 1);
- if ($config{USE_SSL})
- {
- if ($config{HAS_GNUTLS} eq "y")
- {
- $config{USE_GNUTLS} = prompt_bool(1, 'Would you like to enable SSL with m_ssl_gnutls (recommended)?', 1);
- if ($config{USE_GNUTLS})
- {
- print "Using GnuTLS SSL module.\n\n";
- unlink 'src/modules/m_ssl_gnutls.cpp' if -f 'src/modules/m_ssl_gnutls.cpp';
- symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!\n";
- }
- }
+<|GREEN Extra Modules:|>
+EOM
- if ($config{HAS_OPENSSL} eq "y")
- {
- $config{USE_OPENSSL} = prompt_bool(1, 'Would you like to enable SSL with m_ssl_openssl (recommended)?', 1);
- if ($config{USE_OPENSSL})
- {
- print "Using OpenSSL SSL module.\n\n";
- unlink 'src/modules/m_ssl_openssl.cpp' if -f 'src/modules/m_ssl_openssl.cpp';
- symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!\n";
- }
- }
- }
- }
- else
- {
- print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed and\n";
- print "is in your path.\n\n";
- }
+for my $file (<src/modules/m_*>) {
+ my $module = basename $file, '.cpp';
+ say " * $module" if -l $file;
}
-# We are on a POSIX system, we can enable POSIX extras without asking
-symlink "extra/m_regex_posix.cpp", "src/modules/m_regex_posix.cpp";
+print_format <<"EOM";
-if (($config{USE_GNUTLS}) && ($config{HAS_GNUTLS} ne "y"))
-{
- print "Sorry, but I couldn't detect GnuTLS. Make sure pkg-config is in your path.\n";
- exit 1;
-}
-if (($config{USE_OPENSSL}) && ($config{HAS_OPENSSL} ne "y"))
-{
- print "Sorry, but I couldn't detect OpenSSL. Make sure pkg-config is in your path.\n";
- exit 1;
-}
+<|GREEN Paths:|>
+ <|GREEN Base:|> $config{BASE_DIR}
+ <|GREEN Binary:|> $config{BINARY_DIR}
+ <|GREEN Config:|> $config{CONFIG_DIR}
+ <|GREEN Data:|> $config{DATA_DIR}
+ <|GREEN Log:|> $config{LOG_DIR}
+ <|GREEN Manual:|> $config{MANUAL_DIR}
+ <|GREEN Module:|> $config{MODULE_DIR}
-if ($config{USE_GNUTLS} || $config{USE_OPENSSL}) {
- if (my $val = prompt_bool($interactive, 'Would you like to generate SSL certificates now?', $interactive)) {
- unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem" && -r "$config{CONFIG_DIR}/dhparams.pem") {
- unless (system './tools/genssl auto') {
- print "\nCertificate generation complete, copying to config directory... ";
- File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
- File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
- File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
- print "Done.\n\n";
- }
- } else {
- print "SSL certificates found, skipping.\n\n"
- }
- } else {
- print "Skipping SSL certificate generation in non-interactive mode.\n\n";
- }
-} else {
- print "Skipping SSL Certificate generation, SSL support is not available.\n\n";
-}
+<|GREEN Execution Group:|> $config{GROUP} ($config{GID})
+<|GREEN Execution User:|> $config{USER} ($config{UID})
+<|GREEN Socket Engine:|> $config{SOCKETENGINE}
-print "Writing \e[1;32m.config.cache\e[0m ...\n";
-write_configure_cache(%config);
-parse_templates(\%config, \%cxx);
-dump_hash();
-
-print "\n";
-print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
-if ($config{USE_GNUTLS} || $config{USE_OPENSSL}) {
- print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n";
- print "modules in your config. This configure script has added those modules to the\n";
- print "build process. For more info, please refer to:\n";
- print "\e[1;32mhttp://wiki.inspircd.org/Installation_From_Tarball\e[0m\n";
-}
-print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n";
+To build with these settings run '<|GREEN make -j${\get_cpu_count}|>' now.
+
+EOM
# Routine to list out the extra/ modules that have been enabled.
# Note: when getting any filenames out and comparing, it's important to lc it if the
@@ -492,7 +406,7 @@ EXTRA: for my $extra (@extras) {
for my $extra (keys(%extras)) {
next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
- my @deps = split /\s+/, get_property($abs_extra, 'ModDep');
+ my @deps = split /\s+/, get_directive($abs_extra, 'ModDep', '');
for my $dep (@deps) {
if (exists($extras{$dep})) {
my $ref = \$extras{$dep}; # Take reference.
@@ -539,7 +453,7 @@ sub enable_extras (@) {
next;
}
# Get dependencies, and add them to be processed.
- my @deps = split /\s+/, get_property($extrapath, 'ModDep');
+ my @deps = split /\s+/, get_directive($extrapath, 'ModDep', '');
for my $dep (@deps) {
next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
if (!-e "src/modules/$dep" && !-e "include/$dep") {
@@ -575,7 +489,7 @@ EXTRA: for my $extra (@extras) {
}
# Check if anything needs this.
for my $file (@files) {
- my @deps = split /\s+/, get_property("src/modules/extra/$file", 'ModDep');
+ my @deps = split /\s+/, get_directive("src/modules/extra/$file", 'ModDep', '');
# File depends on this extra...
if (scalar(grep { $_ eq $extra } @deps) > 0) {
# And is both enabled and not about to be disabled.
diff --git a/docs/Doxyfile b/docs/Doxyfile
index e731ec590..f4e526bc7 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -1,1873 +1,265 @@
-# Doxyfile 1.8.3.1
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project.
-#
-# All text after a hash (#) is considered a comment and will be ignored.
-# The format is:
-# TAG = value [value, ...]
-# For lists items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ").
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
-
DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or sequence of words) that should
-# identify the project. Note that if you do not use Doxywizard you need
-# to put quotes around the project name if it contains spaces.
-
PROJECT_NAME = InspIRCd
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER = 2.0
-
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer
-# a quick idea about the purpose of the project. Keep the description short.
-
+PROJECT_NUMBER = 3.0
PROJECT_BRIEF =
-
-# With the PROJECT_LOGO tag one can specify an logo or icon that is
-# included in the documentation. The maximum height of the logo should not
-# exceed 55 pixels and the maximum width should not exceed 200 pixels.
-# Doxygen will copy the logo to the output directory.
-
PROJECT_LOGO =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
OUTPUT_DIRECTORY = docs/doxygen
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
CREATE_SUBDIRS = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
-
+ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
REPEAT_BRIEF = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
ABBREVIATE_BRIEF =
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
ALWAYS_DETAILED_SEC = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
INLINE_INHERITED_MEMB = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
FULL_PATH_NAMES = YES
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip. Note that you specify absolute paths here, but also
-# relative paths, which will be relative from the directory where doxygen is
-# started.
-
STRIP_FROM_PATH =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
STRIP_FROM_INC_PATH =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful if your file system
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
SHORT_NAMES = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
-
JAVADOC_AUTOBRIEF = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
-
QT_AUTOBRIEF = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
INHERIT_DOCS = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
SEPARATE_MEMBER_PAGES = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
TAB_SIZE = 8
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
ALIASES =
-
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding
-# "class=itcl::class" will allow you to use the command class in the
-# itcl::class meaning.
-
TCL_SUBST =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
OPTIMIZE_OUTPUT_FOR_C = NO
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
-
OPTIMIZE_OUTPUT_JAVA = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
-
OPTIMIZE_FOR_FORTRAN = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
-
OPTIMIZE_OUTPUT_VHDL = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given
-# extension. Doxygen has a built-in mapping, but you can override or extend it
-# using this tag. The format is ext=language, where ext is a file extension,
-# and language is one of the parsers supported by doxygen: IDL, Java,
-# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
-# C++. For instance to make doxygen treat .inc files as Fortran files (default
-# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
-# that for custom extensions you also need to set FILE_PATTERNS otherwise the
-# files are not read by doxygen.
-
EXTENSION_MAPPING =
-
-# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
-# comments according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you
-# can mix doxygen, HTML, and XML commands with Markdown formatting.
-# Disable only in case of backward compatibilities issues.
-
MARKDOWN_SUPPORT = YES
-
-# When enabled doxygen tries to link words that correspond to documented classes,
-# or namespaces to their corresponding documentation. Such a link can be
-# prevented in individual cases by by putting a % sign in front of the word or
-# globally by setting AUTOLINK_SUPPORT to NO.
-
AUTOLINK_SUPPORT = YES
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also makes the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
BUILTIN_STL_SUPPORT = YES
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
CPP_CLI_SUPPORT = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
-
SIP_SUPPORT = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate
-# getter and setter methods for a property. Setting this option to YES (the
-# default) will make doxygen replace the get and set methods by a property in
-# the documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
-
IDL_PROPERTY_SUPPORT = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
DISTRIBUTE_GROUP_DOC = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
SUBGROUPING = YES
-
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
-# unions are shown inside the group in which they are included (e.g. using
-# @ingroup) instead of on a separate page (for HTML and Man pages) or
-# section (for LaTeX and RTF).
-
INLINE_GROUPED_CLASSES = NO
-
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
-# unions with only public data fields will be shown inline in the documentation
-# of the scope in which they are defined (i.e. file, namespace, or group
-# documentation), provided this scope is documented. If set to NO (the default),
-# structs, classes, and unions are shown on a separate page (for HTML and Man
-# pages) or section (for LaTeX and RTF).
-
INLINE_SIMPLE_STRUCTS = NO
-
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
TYPEDEF_HIDES_STRUCT = NO
-
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penalty.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will roughly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols.
-
-SYMBOL_CACHE_SIZE = 0
-
-# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
-# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
-# their name and scope. Since this can be an expensive process and often the
-# same symbol appear multiple times in the code, doxygen keeps a cache of
-# pre-resolved symbols. If the cache is too small doxygen will become slower.
-# If the cache is too large, memory is wasted. The cache size is given by this
-# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols.
-
LOOKUP_CACHE_SIZE = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
EXTRACT_ALL = NO
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
-# scope will be included in the documentation.
-
EXTRACT_PACKAGE = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
EXTRACT_STATIC = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
EXTRACT_LOCAL_CLASSES = YES
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
EXTRACT_LOCAL_METHODS = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespaces are hidden.
-
EXTRACT_ANON_NSPACES = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
HIDE_UNDOC_CLASSES = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
HIDE_FRIEND_COMPOUNDS = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
HIDE_IN_BODY_DOCS = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
INTERNAL_DOCS = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
CASE_SENSE_NAMES = NO
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
HIDE_SCOPE_NAMES = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
+HIDE_COMPOUND_REFERENCE= NO
SHOW_INCLUDE_FILES = YES
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
-
+SHOW_GROUPED_MEMB_INC = NO
FORCE_LOCAL_INCLUDES = NO
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
SORT_MEMBER_DOCS = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
SORT_BRIEF_DOCS = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
-
SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
-
SORT_GROUP_NAMES = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
SORT_BY_SCOPE_NAME = NO
-
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
-# do proper type resolution of all parameters of a function it will reject a
-# match between the prototype and the implementation of a member function even
-# if there is only one candidate or it is obvious which candidate to choose
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
-# will still accept a match between prototype and implementation in such cases.
-
STRICT_PROTO_MATCHING = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
GENERATE_TODOLIST = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
GENERATE_TESTLIST = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
GENERATE_BUGLIST = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if section-label ... \endif
-# and \cond section-label ... \endcond blocks.
-
ENABLED_SECTIONS =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or macro consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and macros in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
MAX_INITIALIZER_LINES = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
SHOW_USED_FILES = YES
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
-
SHOW_FILES = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
-
SHOW_NAMESPACES = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
FILE_VERSION_FILTER =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
-
LAYOUT_FILE =
-
-# The CITE_BIB_FILES tag can be used to specify one or more bib files
-# containing the references data. This must be a list of .bib files. The
-# .bib extension is automatically appended if omitted. Using this command
-# requires the bibtex tool to be installed. See also
-# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
-# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
-# feature you need bibtex and perl available in the search path. Do not use
-# file names with spaces, bibtex cannot handle them.
-
CITE_BIB_FILES =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
WARNINGS = NO
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
WARN_IF_UNDOCUMENTED = NO
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
WARN_IF_DOC_ERROR = YES
-
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
WARN_NO_PARAMDOC = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
WARN_FORMAT = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
INPUT =
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
-
INPUT_ENCODING = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
-# *.f90 *.f *.for *.vhd *.vhdl
-
FILE_PATTERNS =
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
RECURSIVE = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-# Note that relative paths are relative to the directory from which doxygen is
-# run.
-
EXCLUDE =
-
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
-# from the input.
-
EXCLUDE_SYMLINKS = YES
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
EXCLUDE_PATTERNS = */.git/* \
*/doxygen/* \
*/commands/* \
*/modes/* \
*/modules/*
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-
EXCLUDE_SYMBOLS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
EXAMPLE_PATH =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
EXAMPLE_PATTERNS =
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
EXAMPLE_RECURSIVE = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be
-# ignored.
-
INPUT_FILTER =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty or if
-# non of the patterns match the file name, INPUT_FILTER is applied.
-
FILTER_PATTERNS =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
FILTER_SOURCE_FILES = NO
-
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
-# and it is also possible to disable source filtering for a specific pattern
-# using *.ext= (so without naming a filter). This option only has effect when
-# FILTER_SOURCE_FILES is enabled.
-
FILTER_SOURCE_PATTERNS =
-
-# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
-# is part of the input, its contents will be placed on the main page (index.html).
-# This can be useful if you have a project on for instance GitHub and want reuse
-# the introduction page also for the doxygen output.
-
USE_MDFILE_AS_MAINPAGE = README.md
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
SOURCE_BROWSER = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C, C++ and Fortran comments will always remain visible.
-
STRIP_CODE_COMMENTS = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
-
REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
REFERENCES_RELATION = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
-
REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
+SOURCE_TOOLTIPS = YES
USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
VERBATIM_HEADERS = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
ALPHABETICAL_INDEX = YES
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
COLS_IN_ALPHA_INDEX = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
HTML_OUTPUT = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header. Note that when using a custom header you are responsible
-# for the proper inclusion of any scripts and style sheets that doxygen
-# needs, which is dependent on the configuration options used.
-# It is advised to generate a default header using "doxygen -w html
-# header.html footer.html stylesheet.css YourConfigFile" and then modify
-# that header. Note that the header is subject to change so you typically
-# have to redo this when upgrading to a newer version of doxygen or when
-# changing the value of configuration settings such as GENERATE_TREEVIEW!
-
HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
HTML_FOOTER =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If left blank doxygen will
-# generate a default style sheet. Note that it is recommended to use
-# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
-# tag will in the future become obsolete.
-
HTML_STYLESHEET =
-
-# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
-# user-defined cascading style sheet that is included after the standard
-# style sheets created by doxygen. Using this option one can overrule
-# certain style aspects. This is preferred over using HTML_STYLESHEET
-# since it does not replace the standard style sheet and is therefor more
-# robust against future updates. Doxygen will copy the style sheet file to
-# the output directory.
-
HTML_EXTRA_STYLESHEET =
-
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that
-# the files will be copied as-is; there are no commands or markers available.
-
HTML_EXTRA_FILES =
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the style sheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
-
HTML_COLORSTYLE_HUE = 220
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
-
HTML_COLORSTYLE_SAT = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
-
HTML_COLORSTYLE_GAMMA = 80
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
-
HTML_TIMESTAMP = NO
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded.
-
HTML_DYNAMIC_SECTIONS = NO
-
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
-# entries shown in the various tree structured indices initially; the user
-# can expand and collapse entries dynamically later on. Doxygen will expand
-# the tree to such a level that at most the specified number of entries are
-# visible (unless a fully collapsed tree already exceeds this amount).
-# So setting the number of entries 1 will produce a full collapsed tree by
-# default. 0 is a special value representing an infinite number of entries
-# and will result in a full expanded tree by default.
-
HTML_INDEX_NUM_ENTRIES = 100
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
-
GENERATE_DOCSET = YES
-
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
-
DOCSET_FEEDNAME = "Doxygen generated documentation"
-
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
-
DOCSET_BUNDLE_ID = org.doxygen.Project
-
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
-# identify the documentation publisher. This should be a reverse domain-name
-# style string, e.g. com.mycompany.MyDocSet.documentation.
-
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
-
DOCSET_PUBLISHER_NAME = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
-
GENERATE_HTMLHELP = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
CHM_FILE =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
HHC_LOCATION =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
GENERATE_CHI = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
-
CHM_INDEX_ENCODING =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
TOC_EXPAND = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
-
GENERATE_QHP = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
-
QCH_FILE =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
-
QHP_NAMESPACE = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
-
QHP_VIRTUAL_FOLDER = doc
-
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
-
QHP_CUST_FILTER_NAME =
-
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
-
QHP_CUST_FILTER_ATTRS =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
-
QHP_SECT_FILTER_ATTRS =
-
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
-
QHG_LOCATION =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
-
GENERATE_ECLIPSEHELP = NO
-
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
-
ECLIPSE_DOC_ID = org.doxygen.Project
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
-# at top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it. Since the tabs have the same information as the
-# navigation tree you can set this option to NO if you already set
-# GENERATE_TREEVIEW to YES.
-
DISABLE_INDEX = NO
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-# Since the tree basically has the same information as the tab index you
-# could consider to set DISABLE_INDEX to NO when enabling this option.
-
GENERATE_TREEVIEW = NO
-
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML
-# documentation. Note that a value of 0 will completely suppress the enum
-# values from appearing in the overview section.
-
ENUM_VALUES_PER_LINE = 4
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
TREEVIEW_WIDTH = 250
-
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
-
EXT_LINKS_IN_WINDOW = NO
-
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
-
FORMULA_FONTSIZE = 10
-
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
-
FORMULA_TRANSPARENT = YES
-
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
-# (see http://www.mathjax.org) which uses client side Javascript for the
-# rendering instead of using prerendered bitmaps. Use this if you do not
-# have LaTeX installed or if you want to formulas look prettier in the HTML
-# output. When enabled you may also need to install MathJax separately and
-# configure the path to it using the MATHJAX_RELPATH option.
-
USE_MATHJAX = NO
-
-# When MathJax is enabled you can set the default output format to be used for
-# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
-# SVG. The default value is HTML-CSS, which is slower, but has the best
-# compatibility.
-
MATHJAX_FORMAT = HTML-CSS
-
-# When MathJax is enabled you need to specify the location relative to the
-# HTML output directory using the MATHJAX_RELPATH option. The destination
-# directory should contain the MathJax.js script. For instance, if the mathjax
-# directory is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to
-# the MathJax Content Delivery Network so you can quickly see the result without
-# installing MathJax.
-# However, it is strongly recommended to install a local
-# copy of MathJax from http://www.mathjax.org before deployment.
-
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
-
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
-# names that should be enabled during MathJax rendering.
-
MATHJAX_EXTENSIONS =
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
-
+MATHJAX_CODEFILE =
SEARCHENGINE = YES
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript.
-# There are two flavours of web server based search depending on the
-# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
-# searching and an index file used by the script. When EXTERNAL_SEARCH is
-# enabled the indexing and searching needs to be provided by external tools.
-# See the manual for details.
-
SERVER_BASED_SEARCH = NO
-
-# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
-# script for searching. Instead the search results are written to an XML file
-# which needs to be processed by an external indexer. Doxygen will invoke an
-# external search engine pointed to by the SEARCHENGINE_URL option to obtain
-# the search results. Doxygen ships with an example indexer (doxyindexer) and
-# search engine (doxysearch.cgi) which are based on the open source search engine
-# library Xapian. See the manual for configuration details.
-
EXTERNAL_SEARCH = NO
-
-# The SEARCHENGINE_URL should point to a search engine hosted by a web server
-# which will returned the search results when EXTERNAL_SEARCH is enabled.
-# Doxygen ships with an example search engine (doxysearch) which is based on
-# the open source search engine library Xapian. See the manual for configuration
-# details.
-
SEARCHENGINE_URL =
-
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
-# search data is written to a file for indexing by an external tool. With the
-# SEARCHDATA_FILE tag the name of this file can be specified.
-
SEARCHDATA_FILE = searchdata.xml
-
-# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
-# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
-# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
-# projects and redirect the results back to the right project.
-
EXTERNAL_SEARCH_ID =
-
-# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
-# projects other than the one defined by this configuration file, but that are
-# all added to the same external search index. Each project needs to have a
-# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
-# of to a relative location where the documentation can be found.
-# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
-
EXTRA_SEARCH_MAPPINGS =
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
GENERATE_LATEX = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
LATEX_OUTPUT = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
-
LATEX_CMD_NAME = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
-
MAKEINDEX_CMD_NAME = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
COMPACT_LATEX = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, letter, legal and
-# executive. If left blank a4wide will be used.
-
PAPER_TYPE = a4
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
EXTRA_PACKAGES =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
LATEX_HEADER =
-
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
-# the generated latex document. The footer should contain everything after
-# the last chapter. If it is left blank doxygen will generate a
-# standard footer. Notice: only use this tag if you know what you are doing!
-
LATEX_FOOTER =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
+LATEX_EXTRA_STYLESHEET =
+LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
-
USE_PDFLATEX = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
LATEX_BATCHMODE = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
-
LATEX_HIDE_INDICES = NO
-
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
-
LATEX_SOURCE_CODE = NO
-
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
-# http://en.wikipedia.org/wiki/BibTeX for more info.
-
LATEX_BIB_STYLE = plain
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
GENERATE_RTF = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
RTF_OUTPUT = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
COMPACT_RTF = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
RTF_HYPERLINKS = NO
-
-# Load style sheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-
RTF_STYLESHEET_FILE =
-
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
-
RTF_EXTENSIONS_FILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
+RTF_SOURCE_CODE = NO
GENERATE_MAN = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
MAN_OUTPUT = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
MAN_EXTENSION = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
-
+MAN_SUBDIR =
MAN_LINKS = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
GENERATE_XML = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
-
XML_OUTPUT = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_SCHEMA =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
-
XML_PROGRAMLISTING = YES
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
+GENERATE_DOCBOOK = NO
+DOCBOOK_OUTPUT = docbook
+DOCBOOK_PROGRAMLISTING = NO
GENERATE_AUTOGEN_DEF = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
GENERATE_PERLMOD = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
-
PERLMOD_LATEX = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
-
PERLMOD_PRETTY = YES
-
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
-
PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
ENABLE_PREPROCESSING = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
MACRO_EXPANSION = YES
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
EXPAND_ONLY_PREDEF = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# pointed to by INCLUDE_PATH will be searched when a #include is found.
-
SEARCH_INCLUDES = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
INCLUDE_PATH =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
INCLUDE_FILE_PATTERNS =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
PREDEFINED =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition that
-# overrules the definition found in the source code.
-
EXPAND_AS_DEFINED =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all references to function-like macros
-# that are alone on a line, have an all uppercase name, and do not end with a
-# semicolon, because these will confuse the parser if not removed.
-
SKIP_FUNCTION_MACROS = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles. For each
-# tag file the location of the external documentation should be added. The
-# format of a tag file without this location is as follows:
-#
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-#
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths
-# or URLs. Note that each tag file must have a unique name (where the name does
-# NOT include the path). If a tag file is not located in the directory in which
-# doxygen is run, you must also specify the path to the tagfile here.
-
TAGFILES =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
GENERATE_TAGFILE =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
ALLEXTERNALS = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
EXTERNAL_GROUPS = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
+EXTERNAL_PAGES = YES
PERL_PATH = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option also works with HAVE_DOT disabled, but it is recommended to
-# install and use dot, since it yields more powerful graphs.
-
CLASS_DIAGRAMS = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
MSCGEN_PATH =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
+DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
HAVE_DOT = NO
-
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
-
DOT_NUM_THREADS = 0
-
-# By default doxygen will use the Helvetica font for all dot files that
-# doxygen generates. When you want a differently looking font you can specify
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find
-# the font, which can be done by putting it in a standard location or by setting
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
-# directory containing the font.
-
DOT_FONTNAME = Helvetica
-
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
-
DOT_FONTSIZE = 10
-
-# By default doxygen will tell dot to use the Helvetica font.
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
-# set the path where dot can find it.
-
DOT_FONTPATH =
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# CLASS_DIAGRAMS tag to NO.
-
CLASS_GRAPH = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
COLLABORATION_GRAPH = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
GROUP_GRAPHS = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
UML_LOOK = NO
-
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside
-# the class node. If there are many fields or methods and many nodes the
-# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
-# threshold limits the number of items for each type to make the size more
-# managable. Set this to 0 for no limit. Note that the threshold may be
-# exceeded by 50% before the limit is enforced.
-
UML_LIMIT_NUM_FIELDS = 10
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
TEMPLATE_RELATIONS = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
INCLUDE_GRAPH = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
INCLUDED_BY_GRAPH = YES
-
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
-
CALL_GRAPH = NO
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
-
CALLER_GRAPH = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will generate a graphical hierarchy of all classes instead of a textual one.
-
GRAPHICAL_HIERARCHY = YES
-
-# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
DIRECTORY_GRAPH = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are svg, png, jpg, or gif.
-# If left blank png will be used. If you choose svg you need to set
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible in IE 9+ (other browsers do not have this requirement).
-
DOT_IMAGE_FORMAT = png
-
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
-# enable generation of interactive SVG images that allow zooming and panning.
-# Note that this requires a modern browser other than Internet Explorer.
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible. Older versions of IE do not have SVG support.
-
INTERACTIVE_SVG = NO
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
DOT_PATH =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
DOTFILE_DIRS =
-
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the
-# \mscfile command).
-
MSCFILE_DIRS =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
+DIAFILE_DIRS =
+PLANTUML_JAR_PATH =
+PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
MAX_DOT_GRAPH_DEPTH = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
-
DOT_TRANSPARENT = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
-
DOT_MULTI_TARGETS = NO
-
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
-
GENERATE_LEGEND = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
-
DOT_CLEANUP = YES
diff --git a/docs/conf/aliases/anope.conf.example b/docs/conf/aliases/anope.conf.example
deleted file mode 100644
index 406adc29a..000000000
--- a/docs/conf/aliases/anope.conf.example
+++ /dev/null
@@ -1,27 +0,0 @@
-# Aliases for nickserv, chanserv, operserv, memoserv, hostserv, botserv
-<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
-<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
-<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
-<alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
-<alias text="HOSTSERV" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
-<alias text="BOTSERV" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes">
-
-# Note: We can't have a shorthand version of this, it conflicts with HS for helpserv
-<alias text="HELPSERV" replace="PRIVMSG HelpServ :$2-" requires="HelpServ" uline="yes">
-
-# Shorthand aliases for nickserv, chanserv, operserv, memoserv, hostserv, botserv
-<alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
-<alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
-<alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
-<alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
-<alias text="HS" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
-<alias text="BS" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes">
-
-
-# /id [channel] <password>
-# Identify for a channel or nickname
-<alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" requires="ChanServ" uline="yes">
-<alias text="ID" format="*" replace="PRIVMSG NickServ :IDENTIFY $2" requires="NickServ" uline="yes">
-<alias text="IDENTIFY" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" requires="ChanServ" uline="yes">
-<alias text="IDENTIFY" format="*" replace="PRIVMSG NickServ :IDENTIFY $2" requires="NickServ" uline="yes">
-
diff --git a/docs/conf/aliases/atheme.conf.example b/docs/conf/aliases/atheme.conf.example
deleted file mode 100644
index 7a0bc015a..000000000
--- a/docs/conf/aliases/atheme.conf.example
+++ /dev/null
@@ -1,25 +0,0 @@
-# Aliases for nickserv, chanserv, operserv, memoserv
-<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
-<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
-<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
-<alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
-<alias text="GAMESERV" replace="PRIVMSG GameServ :$2-" requires="GameServ" uline="yes">
-<alias text="BOTSERV" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes">
-<alias text="HOSTSERV" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
-<alias text="ALIS" replace="PRIVMSG ALIS :$2-" requires="ALIS" uline="yes">
-
-# Shorthand aliases for nickserv, chanserv, operserv, memoserv
-<alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
-<alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
-<alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
-<alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
-<alias text="GS" replace="PRIVMSG GameServ :$2-" requires="GameServ" uline="yes">
-<alias text="BS" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes">
-<alias text="HS" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
-<alias text="LS" replace="PRIVMSG ALIS :$2-" requires="ALIS" uline="yes">
-
-# /id [channel] <password>
-# Identify for a channel or nickname
-<alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" requires="ChanServ" uline="yes">
-<alias text="ID" format="*" replace="PRIVMSG NickServ :IDENTIFY $2-" requires="NickServ" uline="yes">
-
diff --git a/docs/conf/aliases/ircservices.conf.example b/docs/conf/aliases/ircservices.conf.example
deleted file mode 100644
index a4c31dd05..000000000
--- a/docs/conf/aliases/ircservices.conf.example
+++ /dev/null
@@ -1,21 +0,0 @@
-# Aliases for nickserv, chanserv, operserv, memoserv, hostserv
-<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
-<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
-<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
-<alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
-<alias text="STATSERV" replace="PRIVMSG StatServ :$2-" requires="StatServ" uline="yes" operonly="yes">
-<alias text="HOSTSERV" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
-
-# Shorthand aliases for nickserv, chanserv, operserv, memoserv, hostserv
-<alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
-<alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
-<alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
-<alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
-<alias text="SS" replace="PRIVMSG StatServ :$2-" requires="StatServ" uline="yes" operonly="yes">
-<alias text="HS" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
-
-# /id [channel] <password>
-# Identify for a channel or nickname
-<alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3" requires="ChanServ" uline="yes">
-<alias text="ID" format="*" replace="PRIVMSG NickServ :IDENTIFY $2" requires="NickServ" uline="yes">
-
diff --git a/docs/conf/aliases/neostats.conf.example b/docs/conf/aliases/neostats.conf.example
deleted file mode 100644
index baa7fe0ba..000000000
--- a/docs/conf/aliases/neostats.conf.example
+++ /dev/null
@@ -1,35 +0,0 @@
-# Aliases for BLSB, ConnectServ, FloodServ, NeoStats, Operlog, QuoteServ, SecureServ, SeenServ, TextServ, WarServ, YahtzeeServ, LogServ, opsb, GamesServ, ProfileServ & StupidServ
-<alias text="BLSB" replace="PRIVMSG BLSB :$2-" requires="BLSB" uline="yes" operonly="yes">
-<alias text="CONNECTSERV" replace="PRIVMSG ConnectServ :$2-" requires="ConnectServ" uline="yes" operonly="yes">
-<alias text="FLOODSERV" replace="PRIVMSG FloodServ :$2-" requires="FloodServ" uline="yes" operonly="yes">
-<alias text="NEOSTATS" replace="PRIVMSG NeoStats :$2-" requires="NeoStats" uline="yes" operonly="yes">
-<alias text="OPERLOG" replace="PRIVMSG Operlog :$2-" requires="Operlog" uline="yes" operonly="yes">
-<alias text="QUOTESERV" replace="PRIVMSG QuoteServ :$2-" requires="QuoteServ" uline="yes" operonly="yes">
-<alias text="SECURESERV" replace="PRIVMSG SecureServ :$2-" requires="SecureServ" uline="yes" operonly="yes">
-<alias text="SEENSERV" replace="PRIVMSG SeenServ :$2-" requires="SeenServ" uline="yes" operonly="yes">
-<alias text="TEXTSERV" replace="PRIVMSG TextServ :$2-" requires="TextServ" uline="yes" operonly="yes">
-<alias text="WARSERV" replace="PRIVMSG WarServ :$2-" requires="WarServ" uline="yes">
-<alias text="YAHTZEESERV" replace="PRIVMSG YahtzeeServ :$2-" requires="YahtzeeServ" uline="yes" operonly="yes">
-<alias text="LOGSERV" replace="PRIVMSG LogServ :$2-" requires="LogServ" uline="yes" operonly="yes">
-<alias text="OPSB" replace="PRIVMSG opsb :$2-" requires="opsb" uline="yes" operonly="yes">
-<alias text="GAMESSERV" replace="PRIVMSG GamesServ :$2-" requires="GamesServ" uline="yes">
-<alias text="PROFILESERV" replace="PRIVMSG ProfileServ :$2-" requires="ProfileServ" uline="yes">
-<alias text="STUPIDSERV" replace="PRIVMSG StupidServ :$2-" requires="StupidServ" uline="yes">
-
-# Shorthand aliases for FloodServ, Operlog, QuoteServ, TextServ, WarServ, YahtzeeServ, LogServ, GamesServ, ProfileServ, SecureServ, SeenServ, NeoStats, ConnectServ & StupidServ
-<alias text="FS" replace="PRIVMSG FloodServ :$2-" requires="FloodServ" uline="yes" operonly="yes">
-<alias text="OL" replace="PRIVMSG Operlog :$2-" requires="Operlog" uline="yes" operonly="yes">
-<alias text="QS" replace="PRIVMSG QuoteServ :$2-" requires="QuoteServ" uline="yes" operonly="yes">
-<alias text="TS" replace="PRIVMSG TextServ :$2-" requires="TextServ" uline="yes" operonly="yes">
-<alias text="WS" replace="PRIVMSG WarServ :$2-" requires="WarServ" uline="yes">
-<alias text="YS" replace="PRIVMSG YahtzeeServ :$2-" requires="YahtzeeServ" uline="yes" operonly="yes">
-<alias text="LS" replace="PRIVMSG LogServ :$2-" requires="LogServ" uline="yes" operonly="yes">
-<alias text="GS" replace="PRIVMSG GamesServ :$2-" requires="GamesServ" uline="yes">
-<alias text="PS" replace="PRIVMSG ProfileServ :$2-" requires="ProfileServ" uline="yes">
-
-<alias text="SECURE" replace="PRIVMSG SecureServ :$2-" requires="SecureServ" uline="yes" operonly="yes">
-<alias text="SEEN" replace="PRIVMSG SeenServ :$2-" requires="SeenServ" uline="yes" operonly="yes">
-<alias text="NEO" replace="PRIVMSG NeoStats :$2-" requires="NeoStats" uline="yes" operonly="yes">
-<alias text="CONN" replace="PRIVMSG ConnectServ :$2-" requires="ConnectServ" uline="yes" operonly="yes">
-<alias text="STUPID" replace="PRIVMSG StupidServ :$2-" requires="StupidServ" uline="yes">
-
diff --git a/docs/conf/censor.conf.example b/docs/conf/censor.conf.example
index ea9e08147..23924d14b 100644
--- a/docs/conf/censor.conf.example
+++ b/docs/conf/censor.conf.example
@@ -1,4 +1,4 @@
-# Configuration file for m_censor.so
+# Configuration file for the censor module
# The tags for this module are formatted as follows:
#
diff --git a/docs/conf/filter.conf.example b/docs/conf/filter.conf.example
index ef7f50588..ea62efd1f 100644
--- a/docs/conf/filter.conf.example
+++ b/docs/conf/filter.conf.example
@@ -1,4 +1,4 @@
-# Configuration file for m_filter.so
+# Configuration file for the filter module
# The tags for this module are formatted as follows:
#
@@ -6,7 +6,7 @@
# reason="reason for filtering"
# action="action to take"
# flags="filter flags"
-# duration="optional length of gline">
+# duration="optional length of gline">
#
# Valid actions for 'action' are:
#
@@ -40,19 +40,14 @@
# c: Strip color codes from text before trying to match
# *: Represents all of the above flags
# -: Does nothing, a no-op for when you do not want to specify any flags
-#
-# IMPORTANT NOTE: Because the InspIRCd config reader places special meaning on the
-# '\' character, you must use '\\' if you wish to specify a '\' character in a regular
-# expression. For example, to indicate numbers, use \\d and not \d. This does not
-# apply when adding a regular expression over irc with the /FILTER command.
-# Example filters for m_filter:
+# Example filters:
#
# <keyword pattern="*qwerty*" reason="You qwertied!" action="block" flags="pn">
# <keyword pattern="*killmenow*" reason="As you request." action="kill" flags="*">
# <keyword pattern="*blah*" reason="Dont blah!" action="gline" duration="1d6h" flags="-">
-# An example regexp filter for m_filter_pcre:
+# An example regexp filter:
#
# <keyword pattern="^blah.*?$" reason="Dont blah!" action="gline" duration="1d6h" flags="pnPq">
diff --git a/docs/conf/helpop-full.conf.example b/docs/conf/helpop-full.conf.example
index 3374dea34..a1e3c881a 100644
--- a/docs/conf/helpop-full.conf.example
+++ b/docs/conf/helpop-full.conf.example
@@ -43,16 +43,16 @@ USER PASS PING PONG QUIT
OPER">
-<helpop key="sslinfo" value="/SSLINFO [nick]
+<helpop key="sslinfo" value="/SSLINFO <nick>
Displays information on the SSL connection and certificate of the
target user.">
-<helpop key="uninvite" value="/UNINVITE [nick] [channel]
+<helpop key="uninvite" value="/UNINVITE <nick> <channel>
Uninvite a user from a channel, same syntax as INVITE.">
-<helpop key="tban" value="/TBAN [channel] [duration] [banmask]
+<helpop key="tban" value="/TBAN <channel> <duration> <banmask>
Sets a timed ban. The duration of the ban can be specified in the
following format: 1w2d3h4m6s which indicates a ban of one week, two
@@ -61,37 +61,48 @@ ban may just be specified as a number of seconds. All timed bans
appear in the banlist as normal bans and may be safely removed
before their time is up.">
-<helpop key="dccallow" value="/DCCALLOW [+|-] [nick] [duration]
+<helpop key="dccallow" value="/DCCALLOW - List allowed nicks
+/DCCALLOW LIST - This also lists allowed nicks
+/DCCALLOW +<nick> [<duration>] - Add a nick
+/DCCALLOW -<nick> - Remove a nick
+/DCCALLOW HELP - Display help
-Adds a nickname to or deletes a nickname from your DCCALLOW list.">
+Duration is optional, and may be specified in seconds or in the
+form of 1m2h3d4w5y.">
-<helpop key="accept" value="/ACCEPT [+-*] [nick]{ [, [+-]<nick> ] }
+<helpop key="accept" value="/ACCEPT * - List accepted nicks
+/ACCEPT +<nick> - Add a nick
+/ACCEPT -<nick> - Remove a nick
+This command accepts multiple nicks like so:
+/ACCEPT +<nick1>,+<nick2>,-<nick3>
Manages your accept list. This list is used to determine who can
-private message you when you have usermode +g set. ACCEPT +nick adds
-a nick to your accept list, ACCEPT -nick removes a nick from your
-accept list, and ACCEPT * displays your accept list.">
+private message you when you have usermode +g set.">
-<helpop key="cycle" value="/CYCLE [channel] :[reason]
+<helpop key="cycle" value="/CYCLE <channel> :[<reason>]
Cycles a channel (leaving and rejoining), overrides restrictions that
would stop a new user joining, such as user limits and channel keys.">
-<helpop key="title" value="/TITLE [name] [password]
+<helpop key="title" value="/TITLE <name> <password>
Authenticate for a WHOIS title line and optionally a vhost using the
specified username and password.">
-<helpop key="watch" value="/WATCH [C|S|+/-[NICK]]
+<helpop key="watch" value="/WATCH - List watched nicks that are online
+/WATCH L - List watched nicks, online and offline
+/WATCH C - Clear all watched nicks
+/WATCH S - Show statistics
+/WATCH +<nick> - Add a nick
+/WATCH -<nick> - Remove a nick
+This command accepts multiple nicks like so:
+/WATCH +<nick1> +<nick2> -<nick3>">
-Adds or deletes a user from the watch list. C clears the list
-and S queries the status.">
-
-<helpop key="vhost" value="/VHOST [username] [password]
+<helpop key="vhost" value="/VHOST <username> <password>
Authenticate for a vhost using the specified username and password.">
-<helpop key="remove" value="/REMOVE [nick] [channel] {[reason]}
+<helpop key="remove" value="/REMOVE <channel> <nick> [<reason>]
Removes a user from a channel you specify. You must be at least a
channel halfoperator to remove a user. A removed user will part with
@@ -102,18 +113,18 @@ a message stating they were removed from the channel and by whom.">
Removes listmodes from a channel.
E.g. /RMODE #Chan b m:* will remove all mute extbans.">
-<helpop key="fpart" value="/FPART [channel] [nick] {[reason]}
+<helpop key="fpart" value="/FPART <channel> <nick> [<reason>]
-This behaves identically to /REMOVE, the only difference is that the
-[channel] and [nick] parameters are switched around to match /KICK's
-syntax. Also, /REMOVE is a builtin mIRC command which caused trouble
-for some users.">
+This behaves identically to /REMOVE. /REMOVE is a built-in mIRC command
+which caused trouble for some users.">
-<helpop key="devoice" value="/DEVOICE [channel]
+<helpop key="devoice" value="/DEVOICE <channel>
Devoices yourself on the specified channel.">
-<helpop key="silence" value="/SILENCE [+/-][hostmask] [p|c|i|n|t|a|x]
+<helpop key="silence" value="/SILENCE - Shows a list of silenced masks
+/SILENCE +<mask> [<flags>] - Add a mask
+/SILENCE -<mask> - Remove a mask
A serverside ignore of the given n!u@h mask. The letter(s) at the end
specify what is to be ignored from this hostmask.
@@ -134,13 +145,13 @@ with what you want excepted. For example, if you wanted to except
everything from people with a host matching *.foo.net, you would do
/SILENCE +*!*@*.foo.net xa
-/SILENCE without a parameter will list the hostmasks that you have silenced.">
+/SILENCE without a parameter will list the masks that you have silenced.">
-<helpop key="knock" value="/KNOCK [channel]
+<helpop key="knock" value="/KNOCK <channel>
Sends a notice to a channel indicating you wish to join.">
-<helpop key="user" value="/USER [ident] [local host] [remote host] :[GECOS]
+<helpop key="user" value="/USER <ident> <local host> <remote host> :<GECOS>
This command is used by your client to register your
IRC session, providing your ident and GECOS to the
@@ -148,11 +159,11 @@ server.
You should not use it during an established connection.">
-<helpop key="nick" value="/NICK [new nick]
+<helpop key="nick" value="/NICK <new nick>
-Change your nickname to [new nick].">
+Change your nickname to <new nick>.">
-<helpop key="quit" value="/QUIT {[reason]}
+<helpop key="quit" value="/QUIT [<reason>]
Quit from IRC and end your current session.">
@@ -160,47 +171,51 @@ Quit from IRC and end your current session.">
Returns the server's version information.">
-<helpop key="ping" value="/PING [server]
+<helpop key="ping" value="/PING <server>
Ping a server. The server will answer with a PONG.">
-<helpop key="pong" value="/PONG [server]
+<helpop key="pong" value="/PONG <server>
Your client should send this to answer server PINGs. You
should not issue this command manually.">
-<helpop key="admin" value="/ADMIN [server]
+<helpop key="admin" value="/ADMIN [<server>]
Shows the administrative information for the given server.">
-<helpop key="privmsg" value="/PRIVMSG [target] [text]
+<helpop key="privmsg" value="/PRIVMSG <target> <text>
-Sends a message to a user or channel specified in [target].">
+Sends a message to a user or channel specified in <target>.">
-<helpop key="notice" value="/NOTICE [target] [text]
+<helpop key="notice" value="/NOTICE <target> <text>
-Sends a notice to a user or channel specified in [target].">
+Sends a notice to a user or channel specified in <target>.">
-<helpop key="join" value="/JOIN [channel]{,[channel]} [key]{,[key]}
+<helpop key="join" value="/JOIN <channel>[,<channel>] [<key>][,<key>]
Joins one or more channels you provide the names for.">
-<helpop key="names" value="/NAMES [channel]{,[channel]}
+<helpop key="names" value="/NAMES <channel>[,<channel>]
Return a list of users on the channels you provide.">
-<helpop key="part" value="/PART [channel]{,[channel] [reason]}
+<helpop key="part" value="/PART <channel>[,<channel>] [<reason>]
Leaves one or more channels you specify.">
-<helpop key="kick" value="/KICK [channel] [nick] {[reason]}
+<helpop key="kick" value="/KICK <channel> <nick>[,<nick>] [<reason>]
Kicks a user from a channel you specify. You must be
at least a channel halfoperator to kick a user.">
-<helpop key="mode" value="/MODE [target] [+|-][modes]{[+|-][modes]} {mode parameters}
+<helpop key="mode" value="/MODE <target> (+|-)<modes> [<mode parameters>] - Change modes of <target>.
+
+/MODE <target> - Show modes of <target>.
+
+/MODE <channel> <list mode char> - List bans, exceptions, etc. set on <channel>.
-Sets the mode for a channel or a nickname specified in [target].
+Sets the mode for a channel or a nickname specified in <target>.
A user may only set modes upon themselves, and may not set the
+o usermode, and a user may only change channel modes of
channels where they are at least a halfoperator.
@@ -208,14 +223,14 @@ channels where they are at least a halfoperator.
For a list of all user and channel modes, enter /HELPOP UMODES or
/HELPOP CHMODES.">
-<helpop key="topic" value="/TOPIC [channel] {topic}
+<helpop key="topic" value="/TOPIC <channel> [<topic>]
Sets or retrieves the channel topic. If a channel topic is
given in the command and either the channel is not +t, or
you are at least a halfoperator, the channel topic will be
changed to the new one you provide.">
-<helpop key="who" value="/WHO [ [search-pattern] [ohurmaiMplf] ]
+<helpop key="who" value="/WHO <search pattern> [ohurmaiMplf]
Looks up the information of users matching the range you provide.
You may only /WHO nicknames in channels or on servers where you
@@ -259,44 +274,44 @@ The following flags after the mask have the following effects:
You may combine multiple flags in one WHO command except where stated in the table above.">
-<helpop key="motd" value="/MOTD [server]
+<helpop key="motd" value="/MOTD [<server>]
-Show the message of the day for [server]. Messages of the day often
+Show the message of the day for <server>. Messages of the day often
contain important server rules and notices and should be read prior
to using a server.">
-<helpop key="oper" value="/OPER [login] [password]
+<helpop key="oper" value="/OPER <login> <password>
Attempts to authenticate a user as an IRC operator.
Both successful and unsuccessful oper attempts are
logged, and sent to online IRC operators.">
-<helpop key="list" value="/LIST [pattern]
+<helpop key="list" value="/LIST [<pattern>]
Creates a list of all existing channels matching the glob pattern
-[pattern], e.g. *chat* or bot*.">
+<pattern>, e.g. *chat* or bot*.">
<helpop key="lusers" value="/LUSERS
Shows a count of local and remote users, servers and channels.">
-<helpop key="userhost" value="/USERHOST [nickname]
+<helpop key="userhost" value="/USERHOST <nick> [<nick>]
Returns the hostname and nickname of a user, and some other
miscellaneous information.">
-<helpop key="away" value="/AWAY {message}
+<helpop key="away" value="/AWAY [<message>]
If a message is given, marks you as being away, otherwise
removes your away status and previous message.">
-<helpop key="ison" value="/ISON [nick] {[nick]...}
+<helpop key="ison" value="/ISON <nick> [<nick> ...]
Returns a subset of the nicks you give, showing only those
that are currently online.">
-<helpop key="invite" value="/INVITE [nick] [channel] {[time]}
+<helpop key="invite" value="/INVITE <nick> <channel> [<time>]
Invites a user to a channel. If the channel is NOT +A, only
channel halfoperators or above can invite people. If +A is set,
@@ -310,7 +325,7 @@ Invited users may override bans, +k, and similar in addition to
If a time is provided, the invite expires after that time and the user
can no longer use it to enter the channel.">
-<helpop key="pass" value="/PASS [password]
+<helpop key="pass" value="/PASS <password>
This command is used by your client when setting up
your IRC session to submit a server password to the
@@ -318,7 +333,7 @@ server.
You should not use it during an established connection.">
-<helpop key="whowas" value="/WHOWAS [nick]
+<helpop key="whowas" value="/WHOWAS <nick>
Returns a list of times the user was seen recently on IRC along with
the time they were last seen and their server.">
@@ -333,24 +348,24 @@ Shows a graphical representation of all users and servers on the
network, and the links between them, as a tree from the perspective
of your server.">
-<helpop key="whois" value="/WHOIS [nick] {server}
+<helpop key="whois" value="/WHOIS <nick> [<server>]
Returns the WHOIS information of a user, their channels, hostname,
etc. If a second nickname or server is provided, then a whois is
performed from the server where the user is actually located rather
than locally, showing idle and signon times.">
-<helpop key="time" value="/TIME [servermask]
+<helpop key="time" value="/TIME [<server>]
Returns the local time of the server, or remote time of another
server.">
-<helpop key="info" value="/INFO
+<helpop key="info" value="/INFO [<server>]
Returns information on the developers and supporters who made this
IRC server possible.">
-<helpop key="setname" value="/SETNAME [name]
+<helpop key="setname" value="/SETNAME <name>
Sets your name to the specified name.">
@@ -363,7 +378,8 @@ Sets your name to the specified name.">
-------------
OPERMOTD CHECK CLONES USERIP TLINE
-ALLTIME WALLOPS GLOBOPS
+ALLTIME WALLOPS GLOBOPS MODENOTICE
+CLOAK
SETHOST SETIDENT CHGHOST CHGIDENT CHGNAME
SETIDLE SWHOIS
@@ -381,20 +397,20 @@ CONNECT SQUIT RCONNECT RSQUIT
DIE RESTART REHASH
CLEARCACHE LOADMODULE UNLOADMODULE
RELOADMODULE GLOADMODULE GUNLOADMODULE
-GRELOADMODULE RELOAD CLOSE
-LOCKSERV UNLOCKSERV JUMPSERVER">
+GRELOADMODULE CLOSE JUMPSERVER
+LOCKSERV UNLOCKSERV">
-<helpop key="userip" value="/USERIP [nicknames]
+<helpop key="userip" value="/USERIP <nick> [<nick>]
Returns the ip and nickname of the given users.">
-<helpop key="tline" value="/TLINE [host or ip mask]
+<helpop key="tline" value="/TLINE <host/IP mask>
This command returns the number of local and global clients matched,
and the percentage of clients matched, plus how they were matched
(by IP address or by hostname).">
-<helpop key="lockserv" value="/LOCKSERV
+<helpop key="lockserv" value="/LOCKSERV :[<message>]
Locks out all new connections notifying connecting users that the
service is temporarily closed and to try again later.">
@@ -403,12 +419,12 @@ service is temporarily closed and to try again later.">
Opens the server up again for new connections.">
-<helpop key="jumpserver" value="/JUMPSERVER {[newserver] [newport] [+/-flags] {:[reason]}}
+<helpop key="jumpserver" value="/JUMPSERVER [<newserver> <newport> <(+|-)[flags]> :[<reason>]]
Sets or cancels jumpserver mode. If no parameters are given,
jumpserver mode is cancelled, if it is currently set. If parameters
-are given, a server address must be given for [newserver] and a
-server port must be given for [newport]. Zero or more status flags
+are given, a server address must be given for <newserver> and a
+server port must be given for <newport>. Zero or more status flags
should be given for 'flags', from the list below (if you do not
wish to specify any flags just place a '+' in this field):
@@ -424,7 +440,7 @@ reason parameter is optional, and if not provided defaults to
'Please use this server/port instead' (the default given in various
numeric lists)">
-<helpop key="filter" value="/FILTER [filter-definition] {[action] [flags] {[gline-duration]} :[reason]}
+<helpop key="filter" value="/FILTER <filter-definition> [<action> <flags> [<gline-duration>] :<reason>]
This command will add a filter when more than one parameter is given,
for messages of the types specified by the flags, with the given
@@ -471,19 +487,19 @@ filter will be removed. Note that if you remove a
configuration-defined filter, it will reappear at next rehash unless
it is also removed from the config file.">
-<helpop key="ojoin" value="/OJOIN [#chan]
+<helpop key="ojoin" value="/OJOIN <channel>
Force joins you to the specified channel, and gives you +Y and any other
configuration-defined modes on it, preventing you from being kicked.
Depending on configuration, may announce that you have joined the
channel on official network business.">
-<helpop key="clones" value="/CLONES [limit]
+<helpop key="clones" value="/CLONES <limit>
Retrieves a list of users with more clones than the specified
limit.">
-<helpop key="check" value="/CHECK [nick|ip|hostmask|channel] {[server]}
+<helpop key="check" value="/CHECK <nick|ip|hostmask|channel> [<server>]
Allows opers to look up advanced information on channels, hostmasks
or IP addresses, in a similar way to WHO but in more detail, displaying
@@ -497,22 +513,21 @@ specified server.">
Shows the time on all servers on the network.">
-<helpop key="rconnect" value="/RCONNECT [source mask] [target mask]
+<helpop key="rconnect" value="/RCONNECT <source mask> <target mask>
-The server matching [source mask] will try to connect to the first
-server in the config file matching [target mask].">
+The server matching <source mask> will try to connect to the first
+server in the config file matching <target mask>.">
-<helpop key="rsquit" value="/RSQUIT {[source mask]} [target mask]
+<helpop key="rsquit" value="/RSQUIT <target mask> [<reason>]
-Causes a remote server matching [target mask] to be disconnected from
-the network, only if connected via a server matching [source mask] if
-it is specified.">
+Causes a remote server matching <target mask> to be disconnected from
+the network.">
-<helpop key="globops" value="/GLOBOPS [message]
+<helpop key="globops" value="/GLOBOPS <message>
Sends a message to all users with the +g snomask.">
-<helpop key="cban" value="/CBAN [channel] {[duration] :[reason]}
+<helpop key="cban" value="/CBAN <channel> [<duration> :[<reason>]]
Sets or removes a channel ban. You must specify all three parameters
to add a ban, and one parameter to remove a ban (just the channel).
@@ -530,35 +545,43 @@ If no nick is given, it joins the oper doing the /SAJOIN.">
Forces the user to part the channel(s).">
-<helpop key="samode" value="/SAMODE [target] +/-[modes] {[parameters for modes]}
+<helpop key="samode" value="/SAMODE <target> (+|-)<modes> [<parameters for modes>]
Applies the given mode change to the channel or nick specified.">
-<helpop key="sanick" value="/SANICK [nick] [new nick]
+<helpop key="sanick" value="/SANICK <nick> <new nick>
Changes the user's nick to the new nick.">
-<helpop key="saquit" value="/SAQUIT [nick] [reason]
+<helpop key="sakick" value="/SAKICK <channel> <nick> <reason>
+
+Kicks the given user from the specified channel.">
+
+<helpop key="satopic" value="/SATOPIC <channel> <new topic>
+
+Applies the given topic to the specified channel.">
+
+<helpop key="saquit" value="/SAQUIT <nick> <reason>
Forces user to quit with the specified reason.">
-<helpop key="setidle" value="/SETIDLE [idle time]
+<helpop key="setidle" value="/SETIDLE <idle time>
Sets your idle time (in seconds) to the specified value.">
-<helpop key="sethost" value="/SETHOST [host]
+<helpop key="sethost" value="/SETHOST <host>
Sets your host to the specified host.">
-<helpop key="setident" value="/SETIDENT [ident]
+<helpop key="setident" value="/SETIDENT <ident>
Sets your ident to the specified ident.">
-<helpop key="swhois" line="/SWHOIS [nick] [swhois]
+<helpop key="swhois" line="/SWHOIS <nick> <swhois>
Sets the user's swhois field to the given swhois.">
-<helpop key="mkpasswd" value="/MKPASSWD [hashtype] [plaintext]
+<helpop key="mkpasswd" value="/MKPASSWD <hashtype> <plaintext>
Encodes the plaintext to a hash of the given type and displays
the result.">
@@ -567,28 +590,28 @@ the result.">
Displays the Oper MOTD.">
-<helpop key="nicklock" value="/NICKLOCK [nick] [new nick]
+<helpop key="nicklock" value="/NICKLOCK <nick> <new nick>
Changes the user's nick to the new nick, and forces
it to remain as such for the remainder of the session.">
-<helpop key="nickunlock" value="/NICKUNLOCK [nick]
+<helpop key="nickunlock" value="/NICKUNLOCK <nick>
Allows a previously locked user to change nicks again.">
-<helpop key="chghost" value="/CHGHOST [nickname] [new hostname]
+<helpop key="chghost" value="/CHGHOST <nickname> <new hostname>
Changes the hostname of the user to the new hostname.">
-<helpop key="chgname" value="/CHGNAME [nickname] [new name]
+<helpop key="chgname" value="/CHGNAME <nickname> <new name>
Changes the name of the user to the new name.">
-<helpop key="chgident" value="/CHGIDENT [nickname] [new ident]
+<helpop key="chgident" value="/CHGIDENT <nickname> <new ident>
Changes the ident of the user to the new ident.">
-<helpop key="shun" value="/SHUN [nick!user@host] {[duration] :[reason]}
+<helpop key="shun" value="/SHUN <nick!user@host> [[<duration>] :<reason>]
Sets or removes a shun (server side ignore) on a host and ident mask.
You must specify all three parameters to add a shun, and one parameter
@@ -598,39 +621,37 @@ The duration may be specified in seconds, or in the format
1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
5 minutes and 6 seconds. All fields in this format are optional.">
-<helpop key="die" value="/DIE [password]
+<helpop key="die" value="/DIE <server>
This command shuts down the local server. A single parameter is
-required, which must match the password in the configuration for the
-command to function.">
+required, which must match the name of the local server.">
-<helpop key="restart" value="/RESTART [password]
+<helpop key="restart" value="/RESTART <server>
This command restarts the local server. A single parameter is
-required, which must match the password in the configuration for the
-command to function.">
+required, which must match the name of the local server.">
<helpop key="commands" value="/COMMANDS
Shows all currently available commands.">
-<helpop key="kill" value="/KILL [user] [reason]
+<helpop key="kill" value="/KILL <user> <reason>
This command will disconnect a user from IRC with the given reason.">
-<helpop key="rehash" value="/REHASH [mask]
+<helpop key="rehash" value="/REHASH <mask>
This command will cause the server configuration file to be reread and
values reinitialized for all servers matching the server mask, or the
local server if one is not specified.">
-<helpop key="connect" value="/CONNECT [servermask]
+<helpop key="connect" value="/CONNECT <servermask>
Add a connection to the server matching the given server mask. You must
have configured the server for linking in your configuration file
before trying to link them.">
-<helpop key="squit" value="/SQUIT [servermask]
+<helpop key="squit" value="/SQUIT <servermask>
Disconnects the server matching the given server mask from this server.">
@@ -639,35 +660,35 @@ Disconnects the server matching the given server mask from this server.">
Lists currently loaded modules, their memory offsets, version numbers,
and flags. If you are not an operator, you will see reduced detail.">
-<helpop key="loadmodule" value="/LOADMODULE [filename.so]
+<helpop key="loadmodule" value="/LOADMODULE <modname>
Loads the specified module into the local server.">
-<helpop key="unloadmodule" value="/UNLOADMODULE [filename.so]
+<helpop key="unloadmodule" value="/UNLOADMODULE <modname>
Unloads a module from the local server. The module cannot have the
static flag set (see the output of /MODULES).">
-<helpop key="reloadmodule" value="/RELOADMODULE [filename.so]
+<helpop key="reloadmodule" value="/RELOADMODULE <modname>
Unloads and reloads a module on the local server. This module cannot
have the static flag set (see the output of /MODULES).">
-<helpop key="loadmodule" value="/GLOADMODULE [filename.so]
+<helpop key="gloadmodule" value="/GLOADMODULE <modname>
Loads the specified module on all linked servers.">
-<helpop key="unloadmodule" value="/GUNLOADMODULE [filename.so]
+<helpop key="gunloadmodule" value="/GUNLOADMODULE <modname>
Unloads a module from all linked servers. The module cannot have the
static flag set (see the output of /MODULES).">
-<helpop key="reloadmodule" value="/GRELOADMODULE [filename.so]
+<helpop key="greloadmodule" value="/GRELOADMODULE <modname>
Unloads and reloads a module on all linked servers. This module cannot
have the static flag set (see the output of /MODULES).">
-<helpop key="kline" value="/KLINE [user@host] {[duration] :[reason]}
+<helpop key="kline" value="/KLINE <user@host> [<duration> :<reason>]
Sets or removes a k-line (local host based ban) on a host and ident mask.
You must specify all three parameters to add a ban, and one parameter
@@ -677,7 +698,7 @@ The duration may be specified in seconds, or in the format
1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
5 minutes and 6 seconds. All fields in this format are optional.">
-<helpop key="zline" value="/ZLINE [ipmask] {[duration] :[reason]}
+<helpop key="zline" value="/ZLINE <ipmask> [<duration> :<reason>]
Sets or removes a z-line (ip based ban) on an ip range mask.
You must specify all three parameters to add a ban, and one parameter
@@ -687,7 +708,7 @@ The duration may be specified in seconds, or in the format
1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
5 minutes and 6 seconds. All fields in this format are optional.">
-<helpop key="qline" value="/QLINE [nickmask] {[duration] :[reason]}
+<helpop key="qline" value="/QLINE <nickmask> [<duration> :<reason>]
Sets or removes a q-line (nick based ban) on a nick mask.
You must specify all three parameters to add a ban, and one parameter
@@ -697,7 +718,7 @@ The duration may be specified in seconds, or in the format
1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
5 minutes and 6 seconds. All fields in this format are optional.">
-<helpop key="gline" value="/GLINE [user@host] {[duration] :[reason]}
+<helpop key="gline" value="/GLINE <user@host> [<duration> :<reason>]
Sets or removes a g-line (host based ban) on host mask.
You must specify all three parameters to add a ban, and one
@@ -707,9 +728,9 @@ The duration may be specified in seconds, or in the format
1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
5 minutes and 6 seconds. All fields in this format are optional.">
-<helpop key="eline" value="/ELINE [user@host] {[duration] :[reason]}
+<helpop key="eline" value="/ELINE <user@host> [<duration> :<reason>]
-Sets or removes a e-line (local ban exception) on host mask.
+Sets or removes a e-line (global ban exception) on host mask.
You must specify at least 3 parameters to add an exception, and one
parameter to remove an exception (just the user@host section).
@@ -722,19 +743,11 @@ be negated by an eline on *@<ip>, bans on *@<host> can be negated by
elines on *@<ip>, or *@<host>, and bans on <ident>@* or <ident>@<host>
can be negated by any eline that matches.">
-<helpop key="wallops" value="/WALLOPS [message]
+<helpop key="wallops" value="/WALLOPS <message>
Sends a message to all +w users.">
-<helpop key="sakick" value="/SAKICK [#chan] [nick] [reason]
-
-Kicks the given user from the specified channel.">
-
-<helpop key="satopic" value="/SATOPIC [#chan] [new topic]
-
-Applies the given topic to the specified channel.">
-
-<helpop key="rline" value="/RLINE [regex] {[duration] :[reason]}
+<helpop key="rline" value="/RLINE <regex> [<duration> :<reason>]
Sets or removes an r-line (regex line) on a n!u@h\\sgecos mask. You
must specify all three parameters to add an rline, and one parameter
@@ -744,14 +757,9 @@ The duration may be specified in seconds, or in the format
1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
5 minutes and 6 seconds. All fields in this format are optional.">
-<helpop key="clearcache" value="/CLEARCACHE {servername}
-
-This command clears the DNS cache of the specified server. If no
-server is specified, the local server's DNS cache will be cleared.">
-
-<helpop key="reload" value="/RELOAD [core command]
+<helpop key="clearcache" value="/CLEARCACHE
-Reloads the specified core command.">
+This command clears the DNS cache of the local server.">
<helpop key="close" value="/CLOSE
@@ -767,6 +775,18 @@ while methods G and Z also add G/Z-Lines for all the targets.
When used, the victims won't see each other getting kicked or quitting.">
+<helpop key="modenotice" value="/MODENOTICE <modeletters> <message>
+
+Sends a notice to all users who have the given mode(s) set.
+If multiple mode letters are given, the notice is only sent to users
+who have all of them set.">
+
+<helpop key="cloak" value="/CLOAK <host>
+
+Generate the cloak of a host or IP. This is useful for example when
+trying to get the cloak of a user from /WHOWAS and they were not
+using their cloak when they quit.">
+
######################
# User/Channel Modes #
######################
@@ -789,7 +809,7 @@ When used, the victims won't see each other getting kicked or quitting.">
having op modes removed from them (services only,
requires servprotect module).
o Marks as a IRC operator.
- s [mask] Receives server notices specified by [mask]
+ s <mask> Receives server notices specified by <mask>
(IRCop only).
r Marks as a having a registered nickname
(requires services account module).
@@ -802,6 +822,8 @@ When used, the victims won't see each other getting kicked or quitting.">
hideoper module).
I Hides a user's entire channel list in WHOIS from
non-IRCops (requires hidechans module).
+ L Stops redirections done by m_redirect (mode must be
+ enabled in the config).
R Blocks private messages from unregistered users
(requires services account module).
S Strips mIRC color/bold/underline codes out of private
@@ -812,40 +834,40 @@ When used, the victims won't see each other getting kicked or quitting.">
<helpop key="chmodes" value="Channel Modes
-------------
- v [nickname] Gives voice to [nickname], allowing them to speak
+ v <nickname> Gives voice to <nickname>, allowing them to speak
while the channel is +m.
- h [nickname] Gives halfop status to [nickname] (requires
+ h <nickname> Gives halfop status to <nickname> (requires
customprefix module).
- o [nickname] Gives op status to [nickname].
- a [nickname] Gives protected status to [nickname], preventing
+ o <nickname> Gives op status to <nickname>.
+ a <nickname> Gives protected status to <nickname>, preventing
them from them from being kicked (+q only,
requires customprefix module).
- q [nickname] Gives owner status to [nickname], preventing them
+ q <nickname> Gives owner status to <nickname>, preventing them
from being kicked (Services or only, requires
customprefix module).
- b [hostmask] Bans [hostmask] from the channel.
- e [hostmask] Excepts [hostmask] from bans (requires
+ b <hostmask> Bans <hostmask> from the channel.
+ e <hostmask> Excepts <hostmask> from bans (requires
banexception module).
- I [hostmask] Excepts [hostmask] from +i, allowing matching
+ I <hostmask> Excepts <hostmask> from +i, allowing matching
users to join while the channel is invite-only
(requires inviteexception module).
c Blocks messages containing mIRC color codes
(requires blockcolor module).
- d [time] Blocks messages to a channel from new users
- until they have been in the channel for [time]
+ d <time> Blocks messages to a channel from new users
+ until they have been in the channel for <time>
seconds (requires delaymsg module).
- f [*][lines]:[sec] Kicks on text flood equal to or above the
+ f [*]<lines>:<sec> Kicks on text flood equal to or above the
specified rate. With *, the user is banned
(requires messageflood module).
i Makes the channel invite-only.
Users can only join if an operator
uses /INVITE to invite them.
- j [joins]:[sec] Limits joins to the specified rate (requires
+ j <joins>:<sec> Limits joins to the specified rate (requires
joinflood module).
- k [key] Set the channel key (password) to [key].
- l [limit] Set the maximum allowed users to [limit].
+ k <key> Set the channel key (password) to <key>.
+ l <limit> Set the maximum allowed users to <limit>.
m Enable moderation. Only users with +v, +h, or +o
can speak.
n Blocks users who are not members of the channel
@@ -862,8 +884,8 @@ When used, the victims won't see each other getting kicked or quitting.">
see themselves or themselves and the operators,
while operators see all the users (requires
auditorium module).
- w [flag]:[banmask] Adds basic channel access controls of [flag] to
- [banmask], via the +w listmode.
+ w <flag>:<banmask> Adds basic channel access controls of <flag> to
+ <banmask>, via the +w listmode.
For example, +w o:R:Brain will op anyone identified
to the account 'Brain' on join.
(requires autoop module)
@@ -879,23 +901,27 @@ When used, the victims won't see each other getting kicked or quitting.">
module).
D Delays join messages from users until they
message the channel (requires delayjoin module).
- E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similiar messages.
+ E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similar messages.
Kicks as default, blocks with ~ and bans with *
The last two parameters are optional.
- F [changes]:[sec] Blocks nick changes when they equal or exceed the
+ F <changes>:<sec> Blocks nick changes when they equal or exceed the
specified rate (requires nickflood module).
G Censors messages to the channel based on the
network configuration (requires censor module).
- J [seconds] Prevents rejoin after kick for the specified
+ H <num>:<duration> Displays the last <num> lines of chat to joining
+ users. <duration> is the maximum time to keep
+ lines in the history buffer (requires chanhistory
+ module).
+ J <seconds> Prevents rejoin after kick for the specified
number of seconds. This prevents auto-rejoin
(requires kicknorejoin module).
K Blocks /KNOCK on the channel.
- L [channel] If the channel reaches its limit set by +l,
- redirect users to [channel] (requires redirect
+ L <channel> If the channel reaches its limit set by +l,
+ redirect users to <channel> (requires redirect
module).
M Blocks unregistered users from speaking (requires
services account module).
- N Prevents users on the channel from chainging nick
+ N Prevents users on the channel from changing nick
(requires nonicks module).
O Channel is IRCops only (can only be set by IRCops,
requires operchans module).
@@ -908,13 +934,13 @@ When used, the victims won't see each other getting kicked or quitting.">
R Blocks unregistered users from joining (requires
services account module).
S Strips mIRC color codes from messages to the
- channel (requirs stripcolor module).
+ channel (requires stripcolor module).
T Blocks /NOTICEs to the channel from users who are
not at least halfop (requires nonotice module).
- g [mask] Blocks messages matching the given blob mask
+ g <mask> Blocks messages matching the given glob mask
(requires chanfilter module).
- X [mode] Makes channel operators immune to the specified
+ X <mode> Makes channel operators immune to the specified
restrictive mode (requires exemptchanops module).
-------------
@@ -927,7 +953,7 @@ help channel if you have any questions.">
# Stats Symbols #
######################
-<helpop key="stats" value="/STATS [symbol]
+<helpop key="stats" value="/STATS <symbol>
Shows various server statistics. Depending on configuration, some
symbols may be only available to opers.
@@ -1023,46 +1049,47 @@ setting +I <extban>.
Matching extbans:
- j:#channel Matches anyone in #channel. Does not support wildcards
- for #channel (requires channelban module).
- r:realname Matches users with a matching realname (requires gecosban
+ j:<channel> Matches anyone in the given channel. Does not support
+ wildcards (requires channelban module).
+ r:<realname> Matches users with a matching realname (requires gecosban
module).
- s:server Matches users on a matching server (requires serverban
+ s:<server> Matches users on a matching server (requires serverban
module).
- z:fingerprint Matches users with a matching ssl fingerprint (requires
- sslmodes module)
- O:opertype Matches IRCops of a matching type, mostly useful as an
+ z:<certfp> Matches users with a matching SSL certificate fingerprint
+ (requires sslmodes module)
+ O:<opertype> Matches IRCops of a matching type, mostly useful as an
an invite exception (requires operchans module).
- R:account Matches users logged into a matching account (requires
+ R:<account> Matches users logged into a matching account (requires
services account module).
Acting extbans:
- c:<ban> Blocks any messages that contain color codes from
+ c:<banmask> Blocks any messages that contain color codes from
matching users (requires blockcolor module).
- m:<ban> Blocks messages from matching users (requires muteban
+ m:<banmask> Blocks messages from matching users (requires muteban
module). Users with +v or above are not affected.
- p:<ban> Blocks part messages from matching users (requires
+ p:<banmask> Blocks part messages from matching users (requires
nopartmsg module).
- A:<ban> Blocks invites by matching users even when +A is set
+ A:<banmask> Blocks invites by matching users even when +A is set
(requires allowinvite module).
- B:<ban> Blocks all capital or nearly all capital messages from
+ B:<banmask> Blocks all capital or nearly all capital messages from
matching users (requires blockcaps module).
- C:<ban> Blocks CTCPs from matching users (requires noctcp
+ C:<banmask> Blocks CTCPs from matching users (requires noctcp
module).
- N:<ban> Blocks nick changes from matching users (requires
+ N:<banmask> Blocks nick changes from matching users (requires
nonicks module).
- Q:<ban> Blocks kicks by matching users (requires nokicks
+ Q:<banmask> Blocks kicks by matching users (requires nokicks
module).
- S:<ban> Strips color/bold/underline from messages from matching
+ S:<banmask> Strips color/bold/underline from messages from matching
users (requires stripcolor module).
- T:<ban> Blocks notices from matching users (requires nonotice
+ T:<banmask> Blocks notices from matching users (requires nonotice
module).
- U:<ban> Blocks unregistered users matching the given ban.
- (requires m_services_account)
+ U:<banmask> Blocks unregistered users matching the given banmask.
+ (requires services account).
-A ban given to an acting extban may either be a nick!user@host mask,
-matched against users as for a normal ban, or a matching extban.
+A ban given to an Acting extban may either be a nick!user@host mask
+(unless stated otherwise), matched against users as for a normal ban,
+or a Matching extban.
There is an additional special type of extended ban, a redirect ban:
diff --git a/docs/conf/helpop.conf.example b/docs/conf/helpop.conf.example
index 7d505b261..20596e696 100644
--- a/docs/conf/helpop.conf.example
+++ b/docs/conf/helpop.conf.example
@@ -1,4 +1,4 @@
-# Sample configuration file for m_helpop.so
+# Sample configuration file for the helpop module.
# You can either copy this into your conf folder and set up the module to use it,
# or you can customize the responses for your network and/or add more.
#
@@ -50,7 +50,8 @@ OPER">
-------------
OPERMOTD CHECK CLONES USERIP TLINE
-ALLTIME WALLOPS GLOBOPS
+ALLTIME WALLOPS GLOBOPS MODENOTICE
+CLOAK
SETHOST SETIDENT CHGHOST CHGIDENT CHGNAME
SETIDLE SWHOIS
@@ -68,8 +69,8 @@ CONNECT SQUIT RCONNECT RSQUIT
DIE RESTART REHASH
CLEARCACHE LOADMODULE UNLOADMODULE
RELOADMODULE GLOADMODULE GUNLOADMODULE
-GRELOADMODULE RELOAD CLOSE
-LOCKSERV UNLOCKSERV JUMPSERVER">
+GRELOADMODULE CLOSE JUMPSERVER
+LOCKSERV UNLOCKSERV">
<helpop key="umodes" value="User Modes
----------
@@ -79,7 +80,7 @@ LOCKSERV UNLOCKSERV JUMPSERVER">
commonchans module).
d Deaf mode. User will not receive any messages or notices
from channels they are in (requires deaf module).
- g In combination with /allow, provides for server side
+ g In combination with /ACCEPT, provides for server side
ignore (requires callerid module).
h Marks as 'available for help' in WHOIS (IRCop only,
requires helpop module).
@@ -89,7 +90,7 @@ LOCKSERV UNLOCKSERV JUMPSERVER">
having op modes removed from them (services only,
requires servprotect module).
o Marks as a IRC operator.
- s [mask] Receives server notices specified by [mask]
+ s <mask> Receives server notices specified by <mask>
(IRCop only).
r Marks as a having a registered nickname
(requires services account module).
@@ -102,6 +103,8 @@ LOCKSERV UNLOCKSERV JUMPSERVER">
hideoper module).
I Hides a user's entire channel list in WHOIS from
non-IRCops (requires hidechans module).
+ L Stops redirections done by m_redirect (mode must be
+ enabled in the config).
R Blocks private messages from unregistered users
(requires services account module).
S Strips mIRC color/bold/underline codes out of private
@@ -112,48 +115,49 @@ LOCKSERV UNLOCKSERV JUMPSERVER">
<helpop key="chmodes" value="Channel Modes
-------------
- v [nickname] Gives voice to [nickname], allowing them to speak
+ v <nickname> Gives voice to <nickname>, allowing them to speak
while the channel is +m.
- h [nickname] Gives halfop status to [nickname] (requires
+ h <nickname> Gives halfop status to <nickname> (requires
customprefix module).
- o [nickname] Gives op status to [nickname].
- a [nickname] Gives protected status to [nickname], preventing
+ o <nickname> Gives op status to <nickname>.
+ a <nickname> Gives protected status to <nickname>, preventing
them from them from being kicked (+q only,
requires customprefix module).
- q [nickname] Gives owner status to [nickname], preventing them
+ q <nickname> Gives owner status to <nickname>, preventing them
from being kicked (Services or only, requires
customprefix module).
- b [hostmask] Bans [hostmask] from the channel.
- e [hostmask] Excepts [hostmask] from bans (requires
+ b <hostmask> Bans <hostmask> from the channel.
+ e <hostmask> Excepts <hostmask> from bans (requires
banexception module).
- I [hostmask] Excepts [hostmask] from +i, allowing matching
+ I <hostmask> Excepts <hostmask> from +i, allowing matching
users to join while the channel is invite-only
(requires inviteexception module).
c Blocks messages containing mIRC color codes
(requires blockcolor module).
- f [*][lines]:[sec] Kicks on text flood equal to or above the
+ d <time> Blocks messages to a channel from new users
+ until they have been in the channel for <time>
+ seconds (requires delaymsg module).
+ f [*]<lines>:<sec> Kicks on text flood equal to or above the
specified rate. With *, the user is banned
(requires messageflood module).
- g [mask] Blocks messages matching the given blob mask
- (requires chanfilter module).
i Makes the channel invite-only.
Users can only join if an operator
uses /INVITE to invite them.
- j [joins]:[sec] Limits joins to the specified rate (requires
+ j <joins>:<sec> Limits joins to the specified rate (requires
joinflood module).
- k [key] Set the channel key (password) to [key].
- l [limit] Set the maximum allowed users to [limit].
+ k <key> Set the channel key (password) to <key>.
+ l <limit> Set the maximum allowed users to <limit>.
m Enable moderation. Only users with +v, +h, or +o
can speak.
n Blocks users who are not members of the channel
from messaging it.
- p Make channel private, hiding it in user's whoises
+ p Make channel private, hiding it in users' whoises
and replacing it with * in /LIST.
r Marks the channel as registered with Services
(requires services account module).
- s Make channel secret, hiding it in user's whoises
+ s Make channel secret, hiding it in users' whoises
and /LIST.
t Prevents users without +h or +o from changing the
topic.
@@ -161,8 +165,8 @@ LOCKSERV UNLOCKSERV JUMPSERVER">
see themselves or themselves and the operators,
while operators see all the users (requires
auditorium module).
- w [flag]:[banmask] Adds basic channel access controls of [flag] to
- [banmask], via the +w listmode.
+ w <flag>:<banmask> Adds basic channel access controls of <flag> to
+ <banmask>, via the +w listmode.
For example, +w o:R:Brain will op anyone identified
to the account 'Brain' on join.
(requires autoop module)
@@ -178,23 +182,27 @@ LOCKSERV UNLOCKSERV JUMPSERVER">
module).
D Delays join messages from users until they
message the channel (requires delayjoin module).
- E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similiar messages.
+ E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similar messages.
Kicks as default, blocks with ~ and bans with *
The last two parameters are optional.
- F [changes]:[sec] Blocks nick changes when they equal or exceed the
+ F <changes>:<sec> Blocks nick changes when they equal or exceed the
specified rate (requires nickflood module).
G Censors messages to the channel based on the
network configuration (requires censor module).
- J [seconds] Prevents rejoin after kick for the specified
+ H <num>:<duration> Displays the last <num> lines of chat to joining
+ users. <duration> is the maximum time to keep
+ lines in the history buffer (requires chanhistory
+ module).
+ J <seconds> Prevents rejoin after kick for the specified
number of seconds. This prevents auto-rejoin
(requires kicknorejoin module).
K Blocks /KNOCK on the channel.
- L [channel] If the channel reaches its limit set by +l,
- redirect users to [channel] (requires redirect
+ L <channel> If the channel reaches its limit set by +l,
+ redirect users to <channel> (requires redirect
module).
M Blocks unregistered users from speaking (requires
services account module).
- N Prevents users on the channel from chainging nick
+ N Prevents users on the channel from changing nick
(requires nonicks module).
O Channel is IRCops only (can only be set by IRCops,
requires operchans module).
@@ -207,10 +215,15 @@ LOCKSERV UNLOCKSERV JUMPSERVER">
R Blocks unregistered users from joining (requires
services account module).
S Strips mIRC color codes from messages to the
- channel (requirs stripcolor module).
+ channel (requires stripcolor module).
T Blocks /NOTICEs to the channel from users who are
not at least halfop (requires nonotice module).
+ g <mask> Blocks messages matching the given glob mask
+ (requires chanfilter module).
+ X <mode> Makes channel operators immune to the specified
+ restrictive mode (requires exemptchanops module).
+
-------------
NOTE: A large number of these modes are dependent upon server-side modules
being loaded by a server/network administrator. The actual modes available
@@ -253,50 +266,62 @@ help channel if you have any questions.">
<helpop key="extbans" value="Extended Bans
----------
- c:n!u@h Blocks any messages that contain color codes from
- matching users (requires blockcolor module).
- j:#channel Prevents anyone in #channel from joining the channel
- (requires channelban module).
- m:n!u@h Blocks messages from matching users (requires muteban
- module).
- p:n!u@h Blocks part messages from matching users (requires
- nopartmsg module).
- r:realname Prevents users with a matching realname from joining
- the channel (requires gecosban module).
- s:server Prevents users on a matching server from joining the
- channel (requires services account).
-
- A:n!u@h Blocks invites by matching users even when +A is set
- (requires allowinvite module).
- B:n!u@h Blocks all capital or nearly all capital messages from
- matching users (requires blockcaps module).
- C:n!u@h Blocks CTCPs from matching users (requires noctcp
- module).
- M:account Blocks messages from users logged into a matching
- account (requires services account module).
- N:n!u@h Blocks nick changes from matching users (requires
- nonicks module).
- O:opertype Prevents IRCops of the specified opertype from joining
- the channel, mostly useful as an invite exception (IRCop
- only, requires operchans module).
- Q:n!u@h Blocks kicks by matching users (requires nokicks
- module).
- R:account Prevents users logged into a matching account from
- joining the channel (requires services account module).
- S:n!u@h Strips color/bold/underline from messages from matching
- users (requires stripcolor module).
- T:n!u@h Blocks notices from matching users (requires nonotice
- module).
- U:n!u@h Blocks unregistered users matching the given ban.
- (requires m_services_account)
- z:certfp Blocks users having the given certificate fingerprint
- (requires m_sslmodes)
-
- Redirect n!u@h#channel will redirect the banned user to #channel
- when they try to join (requires banredirect module).
-
-All extbans that prevent users from joining may by used for ban
-exceptions and invite exceptions (invex), and extbans blocking
-specific actions may be used for ban exceptions to exempt people from
-either wider extbans or the restrictive mode matching the extban,
-where one exists.">
+Extbans are split into two types; matching extbans, which match on
+users in additional ways, and acting extbans, which restrict users
+in different ways to a standard ban.
+
+To use an extban, simply set +b <ban> or +e <ban> with it as the ban,
+instead of a normal nick!user@host mask, to ban or exempt matching
+users. Ban exceptions on acting extbans exempt that user from matching
+an extban of that type, and from any channel mode corresponding to the
+restriction. Matching extbans may also be used for invite exceptions by
+setting +I <extban>.
+
+Matching extbans:
+
+ j:<channel> Matches anyone in the given channel. Does not support
+ wildcards (requires channelban module).
+ r:<realname> Matches users with a matching realname (requires gecosban
+ module).
+ s:<server> Matches users on a matching server (requires serverban
+ module).
+ z:<certfp> Matches users having the given SSL certificate
+ fingerprint (requires sslmodes module).
+ O:<opertype> Matches IRCops of a matching type, mostly useful as an
+ an invite exception (requires operchans module).
+ R:<account> Matches users logged into a matching account (requires
+ services account module).
+
+Acting extbans:
+
+ c:<banmask> Blocks any messages that contain color codes from
+ matching users (requires blockcolor module).
+ m:<banmask> Blocks messages from matching users (requires muteban
+ module). Users with +v or above are not affected.
+ p:<banmask> Blocks part messages from matching users (requires
+ nopartmsg module).
+ A:<banmask> Blocks invites by matching users even when +A is set
+ (requires allowinvite module).
+ B:<banmask> Blocks all capital or nearly all capital messages from
+ matching users (requires blockcaps module).
+ C:<banmask> Blocks CTCPs from matching users (requires noctcp
+ module).
+ N:<banmask> Blocks nick changes from matching users (requires
+ nonicks module).
+ Q:<banmask> Blocks kicks by matching users (requires nokicks
+ module).
+ S:<banmask> Strips color/bold/underline from messages from matching
+ users (requires stripcolor module).
+ T:<banmask> Blocks notices from matching users (requires nonotice
+ module).
+ U:<banmask> Blocks unregistered users matching the given banmask.
+ (requires services account).
+
+A ban given to an Acting extban may either be a nick!user@host mask
+(unless stated otherwise), matched against users as for a normal ban,
+or a Matching extban.
+
+There is an additional special type of extended ban, a redirect ban:
+
+ Redirect n!u@h#channel will redirect the banned user to #channel
+ when they try to join (requires banredirect module).">
diff --git a/docs/conf/inspircd.conf.example b/docs/conf/inspircd.conf.example
index 6ef9c9d11..7ba324e47 100644
--- a/docs/conf/inspircd.conf.example
+++ b/docs/conf/inspircd.conf.example
@@ -14,7 +14,7 @@
# |___/ #
# #
##################################||####################################
- #||#
+ #||#
##################################||####################################
# #
# This is an example of the config file for InspIRCd. #
@@ -62,7 +62,7 @@
#<include executable="/path/to/executable parameters"> #
# #
# Executable include example: #
-#<include executable="/usr/bin/wget -q -O - http://mynet.net/inspircd.conf">
+#<include executable="/usr/bin/wget -q -O - http://example.com/inspircd.conf">
# #
@@ -85,7 +85,7 @@
<server
# name: Hostname of your server. Does not need to resolve, but
# does need to be correct syntax (something.somethingelse.tld).
- name="penguin.omega.org.za"
+ name="penguin.omega.example.org"
# description: Server description. Spaces are allowed.
description="Waddle World"
@@ -132,7 +132,7 @@
# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
# #
# If you want to link servers to InspIRCd you must load the #
-# m_spanningtree.so module! Please see the modules list for #
+# spanningtree module! Please see the modules list for #
# information on how to load this module! If you do not load this #
# module, server ports will NOT work! #
@@ -152,10 +152,13 @@
# to this bind section.
type="clients"
- # ssl: If you want this bind section to use SSL, define either
- # gnutls or openssl here. The appropriate SSL modules must be loaded
- # for ssl to work. If you do not want this bind section to support ssl,
- # just remove or comment out this option.
+ # ssl: If you want the port(s) in this bind tag to use SSL, set this to
+ # the name of a custom <sslprofile> tag that you have defined or one
+ # of "openssl", "gnutls", "mbedtls" if you have not defined any. See the
+ # wiki page for the SSL module you are using for more details.
+ #
+ # You will need to load the ssl_openssl module for OpenSSL, ssl_gnutls
+ # for GnuTLS and ssl_mbedtls for mbedTLS.
ssl="gnutls"
# defer: When this is non-zero, connections will not be handed over to
@@ -168,47 +171,39 @@
# To change it on a running bind, you'll have to comment it out,
# rehash, comment it in and rehash again.
defer="0"
+
+ # free: When this is enabled the listener will be created regardless of
+ # whether the interface that provides the bind address is available. This
+ # is useful for if you are starting InspIRCd on boot when the server may
+ # not have brought the network interfaces up yet.
+ free="no"
>
<bind address="" port="6660-6669" type="clients">
-# When linking servers, the OpenSSL and GnuTLS implementations are completely
-# link-compatible and can be used alongside each other
-# on each end of the link without any significant issues.
-# Supported ssl types are: "openssl" and "gnutls".
-# You must load, m_ssl_openssl for OpenSSL or m_ssl_gnutls for GnuTLS.
+# Listener accepting HTML5 WebSocket connections.
+# Requires the websocket module and SHA-1 hashing support (provided by the sha1
+# module).
+#<bind address="" port="7002" type="clients" hook="websocket">
+
+# You can define a custom <sslprofile> tag which defines the SSL configuration
+# for this listener. See the wiki page for the SSL module you are using for
+# more details.
+#
+# Alternatively, you can use one of the default SSL profiles which are created
+# when you have not defined any:
+# "openssl" (requires the ssl_openssl module)
+# "gnutls" (requires the ssl_gnutls module)
+# "mbedtls" (requires the ssl_mbedtls module)
+#
+# When linking servers, the OpenSSL, GnuTLS, and mbedTLS implementations are
+# completely link-compatible and can be used alongside each other on each end
+# of the link without any significant issues.
<bind address="" port="7000,7001" type="servers">
<bind address="1.2.3.4" port="7005" type="servers" ssl="openssl">
-#-#-#-#-#-#-#-#-#-#- DIE/RESTART CONFIGURATION -#-#-#-#-#-#-#-#-#-#-
-# #
-# You can configure the passwords here which you wish to use for #
-# the /DIE and /RESTART commands. Only trusted IRCop's who will #
-# need this ability should know the die and restart password. #
-# #
-
-<power
- # hash: what hash these passwords are hashed with.
- # Requires the module for selected hash (m_md5.so, m_sha256.so
- # or m_ripemd160.so) be loaded and the password hashing module
- # (m_password_hash.so) loaded.
- # Options here are: "md5", "sha256" and "ripemd160", or one of
- # these prefixed with "hmac-", e.g.: "hmac-sha256".
- # Optional, but recommended. Create hashed passwords with:
- # /mkpasswd <hash> <password>
- #hash="sha256"
-
- # diepass: Password for opers to use if they need to shutdown (die)
- # a server.
- diepass=""
-
- # restartpass: Password for opers to use if they need to restart
- # a server.
- restartpass="">
-
-
#-#-#-#-#-#-#-#-#-#- CONNECTIONS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
# This is where you can configure which connections are allowed #
@@ -250,13 +245,14 @@
# allow: What IP addresses/hosts to allow for this block.
allow="203.0.113.*"
- # hash: what hash this password is hashed with. requires the module
- # for selected hash (m_md5.so, m_sha256.so or m_ripemd160.so) be
- # loaded and the password hashing module (m_password_hash.so)
- # loaded. Options here are: "md5", "sha256" and "ripemd160".
- # Optional, but recommended. Create hashed passwords with:
- # /mkpasswd <hash> <password>
- #hash="sha256"
+ # hash: the hash function this password is hashed with. Requires the
+ # module for the selected function (bcrypt, md5, sha1, sha256, or
+ # ripemd160) and the password hashing module (password_hash) to be
+ # loaded.
+ # You may also use any of the above other than bcrypt prefixed with
+ # either "hmac-" or "pbkdf2-hmac-" (requires the pbkdf2 module).
+ # Create hashed passwords with: /mkpasswd <hash> <password>
+ #hash="bcrypt"
# password: Password to use for this block/user(s)
password="secret"
@@ -277,7 +273,7 @@
# globalmax: Maximum global (network-wide) connections per IP (or CIDR mask, see below).
globalmax="3"
- # maxconnwarn: Enable warnings when localmax or globalmax is hit (defaults to on)
+ # maxconnwarn: Enable warnings when localmax or globalmax are reached (defaults to on)
maxconnwarn="off"
# resolvehostnames: If disabled, no DNS lookups will be performed on connecting users
@@ -285,7 +281,7 @@
resolvehostnames="yes"
# usednsbl: Defines whether or not users in this class are subject to DNSBL. Default is yes.
- # This setting only has effect when m_dnsbl is loaded.
+ # This setting only has effect when the dnsbl module is loaded.
#usednsbl="yes"
# useident: Defines if users in this class MUST respond to a ident query or not.
@@ -295,15 +291,15 @@
limit="5000"
# modes: Usermodes that are set on users in this block on connect.
- # Enabling this option requires that the m_conn_umodes module be loaded.
+ # Enabling this option requires that the conn_umodes module be loaded.
# This entry is highly recommended to use for/with IP Cloaking/masking.
- # For the example to work, this also requires that the m_cloaking
+ # For the example to work, this also requires that the "cloaking"
# module be loaded as well.
modes="+x"
# requireident, requiressl, requireaccount: require that users of this
# block have a valid ident response, use SSL, or have authenticated.
- # Requires m_ident, m_sslinfo, or m_services_account respectively.
+ # Requires ident, sslinfo, or the services_account module, respectively.
requiressl="on"
# NOTE: For requireaccount, you must complete the signon prior to full
# connection. Currently, this is only possible by using SASL
@@ -322,9 +318,9 @@
# \017 or \x = Stop all color sequences
allowmotdcolors="false"
- # port: What port this user is allowed to connect on. (optional)
- # The port MUST be set to listen in the bind blocks above.
- port="6697">
+ # port: What port range this user is allowed to connect on. (optional)
+ # The ports MUST be set to listen in the bind blocks above.
+ port="6697,9999">
<connect
# name: Name to use for this connect block. Mainly used for
@@ -402,9 +398,9 @@
limit="5000"
# modes: Usermodes that are set on users in this block on connect.
- # Enabling this option requires that the m_conn_umodes module be loaded.
+ # Enabling this option requires that the conn_umodes module be loaded.
# This entry is highly recommended to use for/with IP Cloaking/masking.
- # For the example to work, this also requires that the m_cloaking
+ # For the example to work, this also requires that the cloaking
# module be loaded as well.
modes="+x">
@@ -462,7 +458,7 @@
# server: DNS server to use to attempt to resolve IP's to hostnames.
# in most cases, you won't need to change this, as inspircd will
# automatically detect the nameserver depending on /etc/resolv.conf
- # (or, on windows, your set nameservers in the registry.)
+ # (or, on Windows, your set nameservers in the registry.)
# Note that this must be an IP address and not a hostname, because
# there is no resolver to resolve the name until this is defined!
#
@@ -496,7 +492,7 @@
# matched, the banlist size defaults to 64 entries. #
# #
-<banlist chan="#morons" limit="128">
+<banlist chan="#largechan" limit="128">
<banlist chan="*" limit="69">
#-#-#-#-#-#-#-#-#-#-#- DISABLED FEATURES -#-#-#-#-#-#-#-#-#-#-#-#-#-#
@@ -568,18 +564,13 @@
# triggers anti-takeover mechanisms of some obsolete bots.
cyclehostsfromuser="no"
- # ircumsgprefix: Use undernet-style message prefixing for NOTICE and
- # PRIVMSG. If enabled, it will add users' prefix to the line, if not,
- # it will just message the user normally.
- ircumsgprefix="no"
-
# announcets: If set to yes, when the timestamp on a channel changes, all users
# in the channel will be sent a NOTICE about it.
announcets="yes"
# allowmismatch: Setting this option to yes will allow servers to link even
# if they don't have the same "optionally common" modules loaded. Setting this to
- # yes may introduce some desyncs and weirdness.
+ # yes may introduce some desyncs and unwanted behaviour.
allowmismatch="no"
# defaultbind: Sets the default for <bind> tags without an address. Choices are
@@ -630,7 +621,7 @@
# somaxconn: The maximum number of connections that may be waiting
# in the accept queue. This is *NOT* the total maximum number of
# connections per server. Some systems may only allow this to be up
- # to 5, while others (such as linux and *BSD) default to 128.
+ # to 5, while others (such as Linux and *BSD) default to 128.
# Setting this above the limit imposed by your OS can have undesired
# effects.
somaxconn="128"
@@ -672,11 +663,6 @@
# higher ranked users. This is the recommended setting.
announceinvites="dynamic"
- # hidemodes: If enabled, then the listmodes given will be hidden
- # from users below halfop. This is not recommended to be set on +b
- # as it may break some functionality in popular clients such as mIRC.
- hidemodes="eI"
-
# hideulines: If this value is set to yes, U-lined servers will
# be hidden from non-opers in /links and /map.
hideulines="no"
@@ -701,6 +687,9 @@
# hidekills: If defined, replaces who set a /kill with a custom string.
hidekills=""
+ # hideulinekills: Hide kills from clients of ulined servers from server notices.
+ hideulinekills="yes"
+
# hidesplits: If enabled, non-opers will not be able to see which
# servers split in a netsplit, they will only be able to see that one
# occurred (If their client has netsplit detection).
@@ -825,7 +814,7 @@
# - OPER - succesful and failed oper attempts
# - KILL - kill related messages
# - snomask - server notices (*all* snomasks will be logged)
-# - FILTER - messages related to filter matches (m_filter)
+# - FILTER - messages related to filter matches (filter module)
# - CONFIG - configuration related messages
# - COMMAND - die and restart messages, and messages related to unknown user types
# - SOCKET - socket engine informational/error messages
@@ -849,6 +838,11 @@
# - USERINPUT
# - USEROUTPUT
#
+# If your server is producing a high levels of log messages you can also set the
+# flush="[positive number]" attribute to specify how many log messages should be
+# buffered before flushing to disk. You should probably not specify this unless
+# you are having problems.
+#
# The following log tag is highly default and uncustomised. It is recommended you
# sort out your own log tags. This is just here so you get some output.
@@ -895,19 +889,15 @@
nick="ChanServ"
# reason: Reason to display on /nick.
- reason="Reserved For Services">
-
-<badnick nick="NickServ" reason="Reserved For Services">
-<badnick nick="OperServ" reason="Reserved For Services">
-<badnick nick="MemoServ" reason="Reserved For Services">
+ reason="Reserved for a network service">
<badhost
# host: ident@hostname to ban.
# Wildcards and CIDR (if you specify an IP) can be used.
- host="*@hundredz.n.hundredz.o.1337.kiddies.example.net"
+ host="*@banneduser.example.net"
# reason: Reason to display when user is disconnected
- reason="Too many 1337 kiddiots">
+ reason="Evading Bans">
<badhost host="root@*" reason="Don't IRC as root!">
<badhost host="*@198.51.100.0/24" reason="This subnet is bad.">
@@ -954,7 +944,7 @@
# #
# You should already know what to do here :) #
-<die value="User error. Insert new user and press any key. (you didn't edit your config properly.)">
+<die value="User error. You didn't edit your config properly. Go back and try again.">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# MODULES #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# ____ _ _____ _ _ ____ _ _ _ #
@@ -987,6 +977,20 @@
# Settings similar to Charybdis IRCd defaults.
#<include file="examples/modules/charybdis.conf.example">
+#-#-#-#-#-#-#-#-#-#-#-# SERVICES CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#
+# #
+# If you use services you will probably want to include one of the #
+# following files which set up aliases, nick reservations and filter #
+# exemptions for services pseudoclients: #
+#
+# Anope users should uncomment this:
+#<include file="examples/services/anope.conf.example">
+#
+# Atheme users should uncomment this:
+#<include file="examples/services/atheme.conf.example">
+#
+# Users of other services should uncomment this:
+#<include file="examples/services/generic.conf.example">
#########################################################################
# #
diff --git a/docs/conf/links.conf.example b/docs/conf/links.conf.example
index dbb29f1ff..f3e07f7ed 100644
--- a/docs/conf/links.conf.example
+++ b/docs/conf/links.conf.example
@@ -10,18 +10,18 @@
# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
# #
# If you want to link servers to InspIRCd you must load the #
-# m_spanningtree.so module! #
+# spanningtree module! #
# #
# #
<link
# name: The name of the remote server. This must match
# the <server:name> value of the remote server.
- name="hub.penguin.org"
+ name="hub.example.org"
# ipaddr: The IP address of the remote server.
# Can also be a hostname, but hostname must resolve.
- ipaddr="penguin.box.com"
+ ipaddr="penguin.example.org"
# port: The port to connect to the server on.
# It must be bound as a server port on the other server.
@@ -36,30 +36,32 @@
# failover (see above).
timeout="300"
- # ssl: If defined, this states the SSL module that will be used when
- # making an outbound connection to the server. Options are: "openssl"
- # and "gnutls" (they are compatible with each other).
+ # ssl: If defined, this states the SSL profile that will be used when
+ # making an outbound connection to the server. Options are the name of an
+ # <sslprofile> tag that you have defined or one of "openssl", "gnutls",
+ # "mbedtls" if you have not defined any. See the wiki page for the SSL
+ # module you are using for more details.
#
- # You will need to load the m_ssl_openssl.so module for OpenSSL,
- # m_ssl_gnutls.so for GnuTLS. The server port that you connect to
- # must be capable of accepting this type of connection.
+ # You will need to load the ssl_openssl module for OpenSSL, ssl_gnutls
+ # for GnuTLS and ssl_mbedtls for mbedTLS. The server port that you
+ # connect to must be capable of accepting this type of connection.
ssl="gnutls"
# fingerprint: If defined, this option will force servers to be
- # authenticated using SSL Fingerprints. See http://wiki.inspircd.org/SSL
- # for more information. This will require an SSL link for both inbound
- # and outbound connections.
+ # authenticated using SSL certificate fingerprints. See
+ # http://wiki.inspircd.org/SSL for more information. This will
+ # require an SSL link for both inbound and outbound connections.
#fingerprint=""
# bind: Local IP address to bind to.
bind="1.2.3.4"
# statshidden: Defines if IP is shown to opers when
- # /stats c is invoked.
+ # /STATS c is invoked.
statshidden="no"
# hidden: If this is set to yes, this server and its "child"
- # servers will not be shown when users do a /map or /links
+ # servers will not be shown when users do a /MAP or /LINKS.
hidden="no"
# passwords: the passwords we send and receive.
@@ -71,8 +73,8 @@
# A duplicate of the first link block without comments
# if you like copying & pasting.
-<link name="hub.penguin.org"
- ipaddr="penguin.box.com"
+<link name="hub.example.org"
+ ipaddr="penguin.example.org"
port="7000"
allowmask="203.0.113.0/24"
timeout="300"
@@ -85,7 +87,7 @@
# Link block for services. Options are the same as for the first
# link block (depending on what your services package supports).
-<link name="services.antarctic.com"
+<link name="services.example.com"
ipaddr="localhost"
port="7000"
allowmask="127.0.0.0/8"
@@ -95,7 +97,7 @@
# Simple autoconnect block. This enables automatic connection of a server
# Recommended setup is to have leaves connect to the hub, and have no
# automatic connections started by the hub.
-<autoconnect period="10m" server="hub.penguin.org">
+<autoconnect period="10m" server="hub.example.org">
# Failover autoconnect block. If you have multiple hubs, or want your network
# to automatically link even if the hub is down, you can specify multiple
@@ -103,7 +105,7 @@
# robin fashion until one succeeds. Period defines the time for restarting
# a single loop.
<autoconnect period="120"
- server="hub.us.penguin.org hub.eu.penguin.org leaf.eu.penguin.org">
+ server="hub.us.example.org hub.eu.example.org leaf.eu.example.org">
#-#-#-#-#-#-#-#-#-#-#-#- ULINES CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
@@ -115,4 +117,4 @@
# not generate quit and connect notices, which can cut down on noise #
# to opers on the network. #
# #
-<uline server="services.antarctic.com" silent="yes">
+<uline server="services.example.com" silent="yes">
diff --git a/docs/conf/modules.conf.example b/docs/conf/modules.conf.example
index e40273d39..93e26059c 100644
--- a/docs/conf/modules.conf.example
+++ b/docs/conf/modules.conf.example
@@ -19,10 +19,11 @@
# | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| #
# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
# #
-# To link servers to InspIRCd, you MUST load the m_spanningtree #
-# module. If you don't do this, server links will NOT work at all. #
+# To link servers to InspIRCd, you MUST load the spanningtree module. #
+# If you don't do this, server links will NOT work at all. #
# This is by design, to allow for the implementation of other linking #
-# protocols in modules in the future. #
+# protocols in modules in the future. This module is at the bottom of #
+# this file. #
# #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
@@ -30,38 +31,38 @@
# cryptographic uses and security.
#
# IMPORTANT:
-# Other modules such as m_cloaking.so and m_password_hash.so may rely on
+# Other modules such as cloaking and password_hash may rely on
# this module being loaded to function.
#
-#<module name="m_md5.so">
+#<module name="md5">
#
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SHA256 module: Allows other modules to generate SHA256 hashes,
# usually for cryptographic uses and security.
#
# IMPORTANT:
-# Other modules such as m_password_hash.so may rely on this module being
-# loaded to function. Certain modules such as m_spanningtree.so will
+# Other modules such as password_hash may rely on this module being
+# loaded to function. Certain modules such as spanningtree will
# function without this module but when it is loaded their features will
# be enhanced (for example the addition of HMAC authentication).
#
-#<module name="m_sha256.so">
+#<module name="sha256">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# RIPEMD160 module: Allows other modules to generate RIPEMD160 hashes,
# usually for cryptographic uses and security.
#
# IMPORTANT:
# Other modules may rely on this module being loaded to function.
-#<module name="m_ripemd160.so">
+#<module name="ripemd160">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Abbreviation module: Provides the ability to abbreviate commands a-la
# BBC BASIC keywords.
-#<module name="m_abbreviation.so">
+#<module name="abbreviation">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Alias module: Allows you to define server-side command aliases.
-#<module name="m_alias.so">
+#<module name="alias">
#
# Set the 'prefix' for in-channel aliases (fantasy commands) to the
# specified character. If not set, the default is "!".
@@ -71,9 +72,9 @@
#
#-#-#-#-#-#-#-#-#-#-#- ALIAS DEFINITIONS -#-#-#-#-#-#-#-#-#-#-#-#-#-#
# #
-# If you have the m_alias.so module loaded, you may also define #
-# aliases as shown below. They are commonly used to provide shortcut #
-# commands to services, however they are not limited to just this use.#
+# If you have the alias module loaded, you may also define aliases as #
+# shown below. They are commonly used to provide shortcut commands to #
+# services, however they are not limited to just this use. #
# An alias tag requires the following values to be defined in it: #
# #
# text - The text to detect as the actual command line. #
@@ -134,18 +135,6 @@
# If a non-oper attempts to use the alias, it will #
# appear to not exist. #
# #
-#<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
-#<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
-#<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
-#<alias text="BOTSERV" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes">
-#<alias text="HOSTSERV" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
-#<alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
-#<alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
-#<alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
-#<alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
-#<alias text="BS" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes">
-#<alias text="HS" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
-#<alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
#
# An example of using the format value to create an alias with two
# different behaviours depending on the format of the parameters.
@@ -158,8 +147,7 @@
#
# This alias fixes a glitch in xchat 2.6.x and above and the way it
# assumes IDENTIFY must be prefixed by a colon (:) character. It should
-# be placed ABOVE the default NICKSERV alias (the first example) listed
-# above.
+# be placed ABOVE the default NICKSERV alias.
#
#<alias text="NICKSERV" format=":IDENTIFY *" replace="PRIVMSG NickServ :IDENTIFY $3-"
# requires="NickServ" uline="yes">
@@ -179,18 +167,18 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Allowinvite module: Gives channel mode +A to allow all users to use
# /INVITE, and extban A to deny invite from specific masks.
-#<module name="m_allowinvite.so">
+#<module name="allowinvite">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Alltime module: Shows time on all connected servers at once.
# This module is oper-only and provides /ALLTIME.
# To use, ALLTIME must be in one of your oper class blocks.
-#<module name="m_alltime.so">
+#<module name="alltime">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Auditorium module: Adds channel mode +u which makes everyone else
# except you in the channel invisible, used for large meetings etc.
-#<module name="m_auditorium.so">
+#<module name="auditorium">
#
# Auditorium settings:
#
@@ -214,25 +202,34 @@
# Another useful combination is with SSL client certificate
# fingerprints: +w h:z:72db600734bb9546c1bdd02377bc21d2a9690d48 will
# give halfop to the user(s) having the given certificate.
-#<module name="m_autoop.so">
+#<module name="autoop">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Ban except module: Adds support for channel ban exceptions (+e).
-#<module name="m_banexception.so">
+#<module name="banexception">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Ban redirection module: Allows bans which redirect to a specified
# channel. e.g. +b nick!ident@host#channelbanneduserissentto
-#<module name="m_banredirect.so">
+#<module name="banredirect">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# bcrypt module: Allows other modules to generate bcrypt hashes,
+# usually for cryptographic uses and security.
+#<module name="bcrypt">
+#
+# rounds: Defines how many rounds the bcrypt function will run when
+# generating new hashes.
+#<bcrypt rounds="10">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Block amsg module: Attempt to block all usage of /amsg and /ame.
-#<module name="m_blockamsg.so">
+#<module name="blockamsg">
#
#-#-#-#-#-#-#-#-#-#-#- BLOCKAMSG CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# If you have the m_blockamsg.so module loaded, you can configure it #
-# with the <blockamsg> tag: #
+# If you have the blockamsg module loaded, you can configure it with #
+# the <blockamsg> tag: #
# #
# delay - How many seconds between two messages to force #
# them to be recognised as unrelated. #
@@ -243,42 +240,42 @@
#<blockamsg delay="3" action="killopers">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Block CAPS module: Blocking all-CAPS messages with channel mode +B.
-#<module name="m_blockcaps.so">
+# Block CAPS module: Adds channel mode +B, blocks all-CAPS messages.
+#<module name="blockcaps">
#
#-#-#-#-#-#-#-#-#-#-#- BLOCKCAPS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# percent - How many percent of text must be caps before text #
-# will be blocked. #
+# percent - What percentage of the text must be caps before #
+# text will be blocked. #
# #
# minlen - The minimum length a line must be for the block #
# percent to have any effect. #
# #
-# capsmap - A list of chars to be considered CAPS, this was #
-# you can add CAPS for your language. Also you can #
-# add things like ! and space to further lock down #
-# on caps usage. #
+# capsmap - A list of chars to be considered CAPS. Can be used #
+# to add CAPS characters for your language. Also you #
+# can add things like ! and space to further lock #
+# down on caps usage. #
#<blockcaps percent="50"
# minlen="5"
# capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! ">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Block color module: Blocking color-coded messages with chan mode +c.
-#<module name="m_blockcolor.so">
+#<module name="blockcolor">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Botmode module: Adds the user mode +B. If set on a user, it will
# show that the user is a bot in /WHOIS.
-#<module name="m_botmode.so">
+#<module name="botmode">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# CallerID module: Adds usermode +g which activates hybrid-style
-# callerid: block all private messages unless you /accept first
-#<module name="m_callerid.so">
+# callerid: block all private messages unless you /ACCEPT first.
+#<module name="callerid">
#
#-#-#-#-#-#-#-#-#-#-#- CALLERID CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# maxaccepts - Maximum number of entries a user can add to his #
-# /accept list. Default is 16 entries. #
+# /ACCEPT list. Default is 16 entries. #
# operoverride - Can opers (note: ALL opers) override callerid? #
# Default is no. #
# tracknick - Preserve /accept entries when a user changes nick? #
@@ -293,23 +290,24 @@
# cooldown="60">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# CAP module: Provides the CAP negotiation mechanism seen in
-# ratbox-derived ircds.
-#<module name="m_cap.so">
+# CAP module: Provides the CAP negotiation mechanism required by the
+# sasl, namesx, uhnames, and ircv3 modules.
+# It is also recommended for STARTTLS support in the starttls module.
+#<module name="cap">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# CBAN module: Lets you disallow channels from being used at runtime.
-# This module is oper-only and provides /cban.
+# This module is oper-only and provides /CBAN.
# To use, CBAN must be in one of your oper class blocks.
-#<module name="m_cban.so">
+#<module name="cban">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Censor module: Adds channel and user mode +G.
-#<module name="m_censor.so">
+#<module name="censor">
#
#-#-#-#-#-#-#-#-#-#-#- CENSOR CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# Optional - If you specify to use the m_censor module, then you must #
+# Optional - If you specify to use the censor module, then you must #
# specify some censor tags. See also: #
# http://wiki.inspircd.org/Modules/censor #
#
@@ -319,11 +317,11 @@
# CGI:IRC module: Adds support for automatic host changing in CGI:IRC
# (http://cgiirc.sourceforge.net).
# Adds snomask +w for monitoring CGI:IRC connections.
-#<module name="m_cgiirc.so">
+#<module name="cgiirc">
#
#-#-#-#-#-#-#-#-#-#-#-# CGIIRC CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
#
-# Optional - If you specify to use m_cgiirc, then you must specify one
+# Optional - If you specify to use cgiirc, then you must specify one
# or more cgihost tags which indicate authorised CGI:IRC servers which
# will be connecting to your network, and an optional cgiirc tag.
# For more information see: http://wiki.inspircd.org/Modules/cgiirc
@@ -336,12 +334,12 @@
# CGI:IRC documentation.
#
# Old style:
-# <cgihost type="pass" mask="www.mysite.com"> # Get IP from PASS
-# <cgihost type="ident" mask="otherbox.mysite.com"> # Get IP from ident
-# <cgihost type="passfirst" mask="www.mysite.com"> # See the docs
+# <cgihost type="pass" mask="www.example.com"> # Get IP from PASS
+# <cgihost type="ident" mask="otherbox.example.com"> # Get IP from ident
+# <cgihost type="passfirst" mask="www.example.com"> # See the docs
# New style:
# <cgihost type="webirc" password="foobar"
-# mask="somebox.mysite.com"> # Get IP from WEBIRC
+# mask="somebox.example.com"> # Get IP from WEBIRC
#
# IMPORTANT NOTE:
# ---------------
@@ -360,12 +358,12 @@
# Channel create module: Adds snomask +j, which will notify opers of
# any new channels that are created.
# This module is oper-only.
-#<module name="m_chancreate.so">
+#<module name="chancreate">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Channel filter module: Allows channel-op defined message filtering
# using simple string matches (channel mode +g).
-#<module name="m_chanfilter.so">
+#<module name="chanfilter">
#
# If hidemask is set to yes, the user will not be shown the mask when
# his/her message is blocked.
@@ -376,7 +374,7 @@
# joining a channel with +H 'X:T' set; 'T' is the maximum time to keep
# lines in the history buffer. Designed so that the new user knows what
# the current topic of conversation is when joining the channel.
-#<module name="m_chanhistory.so">
+#<module name="chanhistory">
#
# Set the maximum number of lines allowed to be stored per channel below.
# This is the hard limit for 'X'.
@@ -392,7 +390,7 @@
# The "channel" field is where you want the messages to go, "snomasks"
# is what snomasks you want to be sent to that channel. Multiple tags
# are allowed.
-#<module name="m_chanlog.so">
+#<module name="chanlog">
#<chanlog snomasks="AOcC" channel="#opers">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
@@ -400,7 +398,7 @@
# characters in the channel name such as bold, colorcodes, etc. which
# can be quite annoying and allow users to on occasion have a channel
# that looks like the name of another channel on the network.
-#<module name="m_channames.so">
+#<module name="channames">
<channames
# denyrange: characters or range of characters to deny in channel
@@ -414,7 +412,10 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Channelban: Implements extended ban j:, which stops anyone already
# in a channel matching a ban like +b j:#channel*mask from joining.
-#<module name="m_channelban.so">
+# Note that by default wildcard characters * and ? are allowed in
+# channel names. To disallow them, load m_channames and add characters
+# 42 and 63 to denyrange (see above).
+#<module name="channelban">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Check module: Adds the /CHECK command.
@@ -422,7 +423,7 @@
# IP addresses and hosts.
# This module is oper-only.
# To use, CHECK must be in one of your oper class blocks.
-#<module name="m_check.so">
+#<module name="check">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# CHGHOST module: Adds the /CHGHOST command.
@@ -431,7 +432,7 @@
# NOTE: Services will not be able to set vhosts on users if this module
# isn't loaded. If you're planning on running services, you probably
# want to load this.
-#<module name="m_chghost.so">
+#<module name="chghost">
#
#-#-#-#-#-#-#-#-# /CHGHOST - /SETHOST CONFIGURATION #-#-#-#-#-#-#-#-#
# Optional - If you want to use special chars for hostnames you can #
@@ -446,29 +447,37 @@
# CHGIDENT module: Adds the /CHGIDENT command.
# This module is oper-only.
# To use, CHGIDENT must be in one of your oper class blocks.
-#<module name="m_chgident.so">
+#<module name="chgident">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# CHGNAME module: Adds the /CHGNAME command
+# CHGNAME module: Adds the /CHGNAME command.
# This module is oper-only.
# To use, CHGNAME must be in one of your oper class blocks.
-#<module name="m_chgname.so">
+#<module name="chgname">
+#
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Connection class ban module: Adds support for extban 'n' which
+# matches against the class name of the user's connection.
+# This module assumes that connection classes are named in a uniform
+# way on all servers of the network.
+#<module name="classban">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Clear chan module: Allows opers to masskick, masskill or mass-G/ZLine
# all users on a channel using /CLEARCHAN.
-#<module name="m_clearchan.so">
+#<module name="clearchan">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Cloaking module: Adds usermode +x and cloaking support.
-# Relies on the module m_md5.so being loaded.
-# To use, you should enable m_conn_umodes and add +x as
-# an enabled mode. See the m_conn_umodes module for more information.
-#<module name="m_cloaking.so">
+# Relies on the md5 module being loaded.
+# To cloak users when they connect, load the conn_umodes module and set
+# <connect:modes> to include the +x mode. The example <connect> tag
+# shows this. See the conn_umodes module for more information.
+#<module name="cloaking">
#
#-#-#-#-#-#-#-#-#-#-#- CLOAKING CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# To use m_cloaking, you must define a cloak key, and optionally a #
+# To use cloaking, you must define a cloak key, and optionally a #
# cloak prefix as shown below. The cloak key must be shared across #
# the network for correct cloaking. #
# #
@@ -493,7 +502,7 @@
# Close module: Allows an oper to close all unregistered connections.
# This module is oper-only and provides the /CLOSE command.
# To use, CLOSE must be in one of your oper class blocks.
-#<module name="m_close.so">
+#<module name="close">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Clones module: Adds an oper command /CLONES for detecting cloned
@@ -501,25 +510,25 @@
# issued, use with care.
# This module is oper-only.
# To use, CLONES must be in one of your oper class blocks.
-#<module name="m_clones.so">
+#<module name="clones">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Common channels module: Adds user mode +c, which, when set, requires
# that users must share a common channel with you to PRIVMSG or NOTICE
# you.
-#<module name="m_commonchans.so">
+#<module name="commonchans">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Auto join on connect module: Allows you to force users to join one
# or more channels automatically upon connecting to the server, or
# join them in case they aren't on any channels after being online
# for X seconds.
-#<module name="m_conn_join.so">
+#<module name="conn_join">
#
#-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
#
-# If you have m_conn_join.so loaded, you can configure it using the
-# following values, or set autojoin="#chat,#help" in <connect> blocks.
+# If you have the conn_join module loaded, you can configure it below
+# or set autojoin="#chat,#help" in <connect> blocks.
#
# Join users immediately after connection to #one #two and #three.
#<autojoin channel="#one,#two,#three">
@@ -530,18 +539,18 @@
# Set modes on connect module: When this module is loaded <connect>
# blocks may have an optional modes="" value, which contains modes to
# add or remove from users when they connect to the server.
-#<module name="m_conn_umodes.so">
+#<module name="conn_umodes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Wait for PONG on connect module: Send a PING to all connecting users
# and don't let them connect until they reply with a PONG.
# This is useful to stop certain kinds of bots and proxies.
-#<module name="m_conn_waitpong.so">
+#<module name="conn_waitpong">
#
#-#-#-#-#-#-#-#-#-#-#- WAITPONG CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# If you have the m_conn_waitpong.so module loaded, configure it with #
-# the <waitpong> tag: #
+# If you have the conn_waitpong module loaded, configure it with the #
+# <waitpong> tag: #
# #
# sendsnotice - Whether to send a helpful notice to users on #
# connect telling them how to connect, should #
@@ -555,13 +564,13 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Channel cycle module: Adds the /CYCLE command which is a server-side
# /HOP that bypasses restrictive modes.
-#<module name="m_cycle.so">
+#<module name="cycle">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Connectban: Provides IP connection throttling. Any IP range that
# connects too many times (configurable) in an hour is Z-Lined for a
# (configurable) duration, and their count resets to 0.
-#<module name="m_connectban.so">
+#<module name="connectban">
#
# ipv4cidr and ipv6cidr allow you to turn the comparison from
# individual IP addresses (32 and 128 bits) into CIDR masks, to allow
@@ -576,7 +585,7 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Connection throttle module.
-#<module name="m_connflood.so">
+#<module name="connflood">
#
#-#-#-#-#-#-#-#-#-#-#- CONNTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#
# seconds, maxconns - Amount of connections per <seconds>.
@@ -596,7 +605,7 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Custom prefixes: Allows for channel prefixes to be added.
-#<module name="m_customprefix.so">
+#<module name="customprefix">
#
# name The name of the mode, must be unique from other modes.
# letter The letter used for this mode. Required.
@@ -611,37 +620,38 @@
#<customprefix name="halfop" letter="h" prefix="%" rank="20000" ranktoset="30000">
#<customprefix name="halfvoice" letter="V" prefix="-" rank="1" ranktoset="20000">
#
-# Do /RELOADMODULE m_customprefix.so after changing the settings of this module.
+# Do /RELOADMODULE customprefix after changing the settings of this module.
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Custom title module: Adds the /TITLE command which allows for trusted
# users to gain a custom whois line and an optional vhost can be
# specified.
-#<module name="m_customtitle.so">
+#<module name="customtitle">
#
#-#-#-#-#-#-#-#-#-#- CUSTOM TITLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#
# name - The username used to identify.
# password - The password used to identify.
# hash - The hash for the specific user's password (optional).
-# m_password_hash.so and a hashing module must be loaded
+# password_hash and a hashing module must be loaded
# for this to work.
# host - Allowed hostmask (optional).
# title - Title shown in whois.
# vhost - Displayed host (optional).
#
#<title name="foo" password="bar" title="Official Chat Helper">
-#<title name="bar" password="foo" host="ident@host.name" title="Official Chat Helper" vhost="helper.network.chat">
-#<title name="foo" password="fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" hash="sha256" title="Official Chat Helper">
+#<title name="bar" password="foo" host="ident@test.org" title="Official Chat Helper" vhost="helper.test.org">
+#<title name="foo" password="$2a$10$UYZ4OcO8NNTCCGyCdY9SK.2GHiqGgxZfHFPOPmWuxEVWVQTtoDC7C" hash="bcrypt" title="Official Chat Helper">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# DCCALLOW module: Adds the /DCCALLOW command.
-#<module name="m_dccallow.so">
+#<module name="dccallow">
#
#-#-#-#-#-#-#-#-#-#-#- DCCALLOW CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# blockchat - Whether to block DCC CHAT as well as DCC SEND.
# length - Default duration of entries in DCCALLOW list.
# action - Default action to take if no action is
# specified, can be 'block' or 'allow'.
+# maxentries - Max number of nicks to allow on a DCCALLOW list.
#
# File configuration:
# pattern - The glob pattern to match against.
@@ -649,14 +659,14 @@
# that matches this pattern, can be 'block' or
# 'allow'.
#
-#<dccallow blockchat="yes" length="5m" action="block">
+#<dccallow blockchat="yes" length="5m" action="block" maxentries="20">
#<banfile pattern="*.exe" action="block">
#<banfile pattern="*.txt" action="allow">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Deaf module: Adds support for the usermode +d - deaf to channel
# messages and channel notices.
-#<module name="m_deaf.so">
+#<module name="deaf">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Delay join module: Adds the channel mode +D which delays all JOIN
@@ -664,22 +674,24 @@
# speaking, their quit or part message will not be shown to the channel
# which helps cut down noise on large channels in a more friendly way
# than the auditorium mode. Only channel ops may set the +D mode.
-#<module name="m_delayjoin.so">
+#<module name="delayjoin">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Delay message module: Adds the channel mode +d which disallows a user
# from talking in the channel unless they've been joined for X seconds.
# Settable using /MODE #chan +d 30
-#<module name="m_delaymsg.so">
+#<module name="delaymsg">
+# Set allownotice to no to disallow NOTICEs too. Defaults to yes.
+#<delaymsg allownotice="no">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Deny channels module: Deny channels from being used by users.
-#<module name="m_denychans.so">
+#<module name="denychans">
#
#-#-#-#-#-#-#-#-#-#-#- DENYCHAN DEFINITIONS -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# If you have the m_denychans.so module loaded, you need to specify #
-# the channels to deny: #
+# If you have the denychans module loaded, you need to specify the #
+# channels to deny: #
# #
# name - The channel name to deny (glob masks are ok). #
# allowopers - If operators are allowed to override the deny. #
@@ -687,25 +699,25 @@
# redirect - Redirect the user to a different channel. #
# #
#<badchan name="#gods*" allowopers="yes" reason="Tortoises!"> #
-#<badchan name="#heaven" redirect="#hell" reason="Nice try!"> #
+#<badchan name="#chan1" redirect="#chan2" reason="Chan1 is closed"> #
# #
# Redirects will not work if the target channel is set +L. #
# #
# Additionally, you may specify channels which are allowed, even if #
# a badchan tag specifies it would be denied: #
-#<goodchan name="#godsleeps"> #
+#<goodchan name="#funtimes"> #
# Glob masks are accepted here also. #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Devoice module: Let users devoice themselves using /DEVOICE #chan.
-#<module name="m_devoice.so">
+#<module name="devoice">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# DNS blacklist module: Provides support for looking up IPs on one or #
# more blacklists. #
-#<module name="m_dnsbl.so"> #
+#<module name="dnsbl"> #
# #
-# For configuration options please see the wiki page for m_dnsbl at #
+# For configuration options please see the wiki page for dnsbl at #
# http://wiki.inspircd.org/Modules/dnsbl #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
@@ -713,23 +725,25 @@
# channel operators to be exempt from some channel modes. Supported #
# modes are blockcaps, noctcp, blockcolor, nickflood, flood, censor, #
# filter, regmoderated, nonick, nonotice, and stripcolor. #
-#<module name="m_exemptchanops.so"> #
+#<module name="exemptchanops"> #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Filter module: Provides message filtering, similar to SPAMFILTER. #
-#<module name="m_filter.so">
+#<module name="filter">
# #
-# This module depends upon a regex provider such as m_regex_pcre or #
-# m_regex_glob to function. You must specify which of these you want #
-# m_filter to use via the tag below. #
+# This module depends upon a regex provider such as regex_pcre or #
+# regex_glob to function. You must specify which of these you want #
+# the filter module to use via the tag below. #
# #
# Valid engines are: #
# #
-# glob - Glob patterns, provided via m_regex_glob.so #
-# pcre - PCRE regexps, provided via m_regex_pcre.so, needs libpcre #
-# tre - TRE regexps, provided via m_regex_tre.so, requires libtre #
-# posix - POSIX regexps, provided via m_regex_posix.so, not availale #
-# on windows, no dependencies on other operating systems. #
+# glob - Glob patterns, provided via regex_glob. #
+# pcre - PCRE regexps, provided via regex_pcre, needs libpcre. #
+# tre - TRE regexps, provided via regex_tre, requires libtre. #
+# posix - POSIX regexps, provided via regex_posix, not available #
+# on Windows, no dependencies on other operating systems. #
+# stdlib - stdlib regexps, provided via regex_stdlib, see comment #
+# at the <module> tag for info on availability. #
# #
#<filteropts engine="glob"> #
# #
@@ -741,7 +755,7 @@
#
#-#-#-#-#-#-#-#-#-#-#- FILTER CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# Optional - If you specify to use the m_filter module, then #
+# Optional - If you specify to use the filter module, then #
# specify below the path to the filter.conf file, or define some #
# <filter> tags. #
# #
@@ -753,12 +767,12 @@
# allowing all IPs to connect to all plaintext IRC ports #
#<bind address="" port="8430" type="flashpolicyd"> #
#<flashpolicyd timeout="5" file=""> #
-#<module name="m_flashpolicyd.so"> #
+#<module name="flashpolicyd"> #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Gecos ban: Implements extended ban 'r', which stops anyone matching
# a mask like +b r:*realname?here* from joining a channel.
-#<module name="m_gecosban.so">
+#<module name="gecosban">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# GeoIP module: Allows the server admin to match users by country code.
@@ -768,7 +782,7 @@
# This module requires GeoIP to be installed on your system,
# use your package manager to find the appropriate packages
# or check the InspIRCd wiki page for this module.
-#<module name="m_geoip.so">
+#<module name="geoip">
#
# The actual allow/ban actions are done by connect classes, not by the
# GeoIP module. An example connect class to ban people from russia or
@@ -786,32 +800,32 @@
# Globops module: Provides the /GLOBOPS command and snomask +g.
# This module is oper-only.
# To use, GLOBOPS must be in one of your oper class blocks.
-#<module name="m_globops.so">
+#<module name="globops">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Global load module: Allows loading and unloading of modules network-
# wide (USE WITH EXTREME CAUTION!)
-# This module is oper-only and provides /gloadmodule, /gunloadmodule
-# and /greloadmodule.
+# This module is oper-only and provides /GLOADMODULE, /GUNLOADMODULE
+# and /GRELOADMODULE.
# To use, GLOADMODULE, GUNLOADMODULE and GRELOADMODULE
# must be in one of your oper class blocks.
-#<module name="m_globalload.so">
+#<module name="globalload">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# HELPOP module: Provides the /HELPOP command
-#<module name="m_helpop.so">
+#<module name="helpop">
#
#-#-#-#-#-#-#-#-#-#-#-#- HELPOP CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# If you specify to use the m_helpop.so module, then specify below #
-# the path to the helpop.conf file. #
+# If you specify to use the helpop module, then specify below the #
+# path to the helpop.conf file. #
# #
#<include file="examples/inspircd.helpop-full.example">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Hide chans module: Allows users to hide their channels list from non-
# opers by setting user mode +I on themselves.
-#<module name="m_hidechans.so">
+#<module name="hidechans">
#
# This mode can optionally prevent opers from seeing channels on a +I
# user, for more privacy if set to true.
@@ -819,83 +833,107 @@
#<hidechans affectsopers="false">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Hide list module: Allows for hiding the list of listmodes from users
+# who do not have sufficient channel rank.
+#<module name="hidelist">
+#
+# Each <hidelist> tag configures one listmode to hide.
+# mode: Name of the listmode to hide.
+# rank: Minimum rank required to view the list. If set to 0, all
+# members of the channel may view the list, but non-members may not.
+# The rank of the built-in op and voice mode is 30000 and 10000,
+# respectively; the rank of other prefix modes is configurable.
+# Defaults to 20000.
+#
+# Hiding the ban list is not recommended because it may break some
+# clients.
+#
+# Hide filter (+g) list:
+#<hidelist mode="filter" rank="30000">
+# Only show invite exceptions (+I) to channel members:
+#<hidelist mode="invex" rank="0">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Hide oper module: Allows opers to hide their oper status from non-
# opers by setting user mode +H on themselves.
# This module is oper-only.
-#<module name="m_hideoper.so">
+#<module name="hideoper">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Hostchange module: Allows a different style of cloaking.
-#<module name="m_hostchange.so">
+#<module name="hostchange">
#
#-#-#-#-#-#-#-#-#-#-#- HOSTCHANGE CONFIGURATION -#-#-#-#-#-#-#-#-#-#
# #
# See http://wiki.inspircd.org/Modules/hostchange for help. #
# #
-#<host suffix="polarbears.org" separator="." prefix="">
-#<hostchange mask="*@fbi.gov" action="addnick">
-#<hostchange mask="*r00t@*" action="suffix">
-#<hostchange mask="a@b.com" action="set" value="blah.blah.blah">
+#<host suffix="invalid.org" separator="." prefix="">
+#<hostchange mask="*@42.theanswer.example.org" action="addnick">
+#<hostchange mask="*root@*" action="suffix">
+#<hostchange mask="a@example.com" action="set" value="foo.bar.baz">
#<hostchange mask="localhost" ports="7000,7001,7005-7007" action="set" value="blahblah.foo">
# hostcycle: If loaded, when a user gets a host or ident set, it will
# cycle them in all their channels. If not loaded it will simply change
# their host/ident without cycling them.
-#<module name="m_hostcycle.so">
+# This module is compatible with the ircv3_chghost module. Clients
+# supporting the chghost extension will get the chghost message instead
+# of seeing a host cycle.
+#<module name="hostcycle">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# httpd module: Provides HTTP server support for InspIRCd.
-#<module name="m_httpd.so">
+#<module name="httpd">
#
#-#-#-#-#-#-#-#-#-#-#-#- HTTPD CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
#
-# If you choose to use the m_httpd.so module, then you will need to add
+# If you choose to use the httpd module, then you will need to add
# a <bind> tag with type "httpd", and load at least one of the other
-# m_httpd_* modules to provide pages to display.
+# httpd_* modules to provide pages to display.
#
# You can adjust the timeout for HTTP connections below. All HTTP
# connections will be closed after (roughly) this many seconds.
#<httpd timeout="20">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# HTTP ACL module: Provides access control lists for m_httpd dependent
+# HTTP ACL module: Provides access control lists for httpd dependent
# modules. Use this module to restrict pages by IP address and by
# password.
-#<module name="m_httpd_acl.so">
+#<module name="httpd_acl">
#
#-#-#-#-#-#-#-#-#-#-#-#- HTTPD ACL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
#
-# Restrict access to the m_httpd_stats module to all but the local
+# Restrict access to the httpd_stats module to all but the local
# network and when the correct password is specified:
# <httpdacl path="/stats*" types="password,whitelist"
-# username="secretstuff" password="mypasshere" whitelist="127.0.0.*,10.*">
+# username="secrets" password="mypasshere" whitelist="127.0.0.*,10.*">
#
# Deny all connections to all but the main index page:
# <httpdacl path="/*" types="blacklist" blacklist="*">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# HTTP config module: Allows the configuration of the server to be
-# viewed over HTTP. Requires m_httpd.so to be loaded for it to function.
-#<module name="m_httpd_config.so">
+# viewed over HTTP. Requires httpd to be loaded for it to function.
+#<module name="httpd_config">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# HTTP stats module: Provides basic stats pages over HTTP.
-# Requires m_httpd.so to be loaded for it to function.
-#<module name="m_httpd_stats.so">
+# Requires httpd to be loaded for it to function.
+#<module name="httpd_stats">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Ident: Provides RFC 1413 ident lookup support.
# When this module is loaded <connect:allow> tags may have an optional
# useident="yes|no" boolean value, determining whether or not to lookup
# ident on users matching that connect tag.
-#<module name="m_ident.so">
+#<module name="ident">
#
#-#-#-#-#-#-#-#-#-#-#-#- IDENT CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# Optional - If you are using the m_ident.so module, then you can #
-# specify the timeout for ident lookups here. If not defined, it will #
-# default to 5 seconds. This is a non-blocking timeout which holds #
-# the user in a 'connecting' state until the lookup is complete. #
+# Optional - If you are using the ident module, then you can specify #
+# the timeout for ident lookups here. If not defined, it will default #
+# to 5 seconds. This is a non-blocking timeout which holds the user #
+# in a 'connecting' state until the lookup is complete. #
# The bind value indicates which IP to bind outbound requests to. #
# nolookupprefix: If on, the idents of users being in a connect class #
# with ident lookups disabled (i.e. <connect useident="off">) will be #
@@ -907,8 +945,8 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Invite exception module: Adds support for channel invite exceptions
# (+I).
-#<module name="m_inviteexception.so">
-# Does a +I bypass channel +k in addition to +i?
+#<module name="inviteexception">
+# bypasskey: If this is enabled, exceptions will bypass +k as well as +i
#<inviteexception bypasskey="yes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
@@ -916,22 +954,51 @@
# extended-join, away-notify and account-notify. These are optional
# enhancements to the client-to-server protocol. An extension is only
# active for a client when the client specifically requests it, so this
-# module needs m_cap to work.
+# module needs the cap module to work.
#
# Further information on these extensions can be found at the IRCv3
# working group website:
# http://ircv3.org/extensions/
#
-#<module name="m_ircv3.so">
+#<module name="ircv3">
# The following block can be used to control which extensions are
-# enabled. Note that extended-join can be incompatible with m_delayjoin
+# enabled. Note that extended-join can be incompatible with delayjoin
# and host cycling.
#<ircv3 accountnotify="on" awaynotify="on" extendedjoin="on">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 cap-notify module: Provides the cap-notify IRCv3.2 extension.
+# Required for IRCv3.2 conformance.
+#<module name="ircv3_capnotify">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 chghost module: Provides the chghost IRCv3.2 extension which
+# allows capable clients to learn when the host/ident of another user
+# changes without cycling the user. This module is compatible with the
+# hostcycle module. If both are loaded, clients supporting the chghost
+# extension will get the chghost message and won't see host cycling.
+#<module name="ircv3_chghost">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 echo-message module: Provides the echo-message IRCv3.2
+# extension which allows capable clients to get an acknowledgement when
+# their messages are delivered and learn what modifications, if any,
+# were applied to them.
+#<module name="ircv3_echomessage">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 invite-notify module: Provides the invite-notify IRCv3.2
+# extension which notifies supporting clients when a user invites
+# another user into a channel. This respects <options:announceinvites>.
+#<module name="ircv3_invitenotify">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Join flood module: Adds support for join flood protection +j X:Y.
-# Closes the channel for 60 seconds if X users join in Y seconds.
-#<module name="m_joinflood.so">
+# Closes the channel for N seconds if X users join in Y seconds.
+#<module name="joinflood">
+#
+# The number of seconds to close the channel for:
+#<joinflood duration="1m">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Jump server module: Adds support for the RPL_REDIR numeric.
@@ -939,15 +1006,15 @@
# To use, JUMPSERVER must be in one of your oper class blocks.
# If your server is redirecting new clients and you get disconnected,
# do a REHASH from shell to open up again.
-#<module name="m_jumpserver.so">
+#<module name="jumpserver">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Anti auto rejoin: Adds support for prevention of auto-rejoin (+J).
-#<module name="m_kicknorejoin.so">
+#<module name="kicknorejoin">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Knock module: Adds the /KNOCK command and channel mode +K.
-#<module name="m_knock.so">
+#<module name="knock">
#
# This setting specifies what to do when someone successfully /KNOCKs.
# If set to "notice", then a NOTICE will be sent to the channel.
@@ -965,7 +1032,7 @@
# ./configure --enable-extras=m_ldap.cpp
# and run make install, then uncomment this module to enable it.
#
-#<module name="m_ldap.so">
+#<module name="ldap">
#<database module="ldap" id="ldapdb" server="ldap://localhost" binddn="cn=Manager,dc=inspircd,dc=org" bindauth="mysecretpass" searchscope="subtree">
# The server parameter indicates the LDAP server to connect to. The #
# ldap:// style scheme before the hostname proper is MANDATORY. #
@@ -982,7 +1049,7 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# LDAP authentication module: Adds the ability to authenticate users #
# via LDAP. #
-#<module name="m_ldapauth.so">
+#<module name="ldapauth">
# #
# Configuration: #
# #
@@ -992,7 +1059,8 @@
# allowpattern="Guest* Bot*" #
# killreason="Access denied" #
# verbose="yes" #
-# host="$uid.$ou.inspircd.org"> #
+# host="$uid.$ou.inspircd.org" #
+# useusername="no"> #
# #
# <ldapwhitelist cidr="10.42.0.0/16"> #
# #
@@ -1009,6 +1077,10 @@
# regardless of if they have an account, for example guest and bot #
# users. #
# #
+# The useusername setting chooses whether the user's username or #
+# nickname is used when locating a user account, if a username isn't #
+# provided in PASS. #
+# #
# Killreason indicates the QUIT reason to give to users if they fail #
# to authenticate. #
# #
@@ -1035,7 +1107,7 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# LDAP oper configuration module: Adds the ability to authenticate #
# opers via LDAP. #
-#<module name="m_ldapoper.so">
+#<module name="ldapoper">
# #
# Configuration: #
# #
@@ -1044,8 +1116,8 @@
# attribute="uid">
# #
# Available configuration items are identical to the same items in #
-# m_ldapauth above (except for the verbose setting, that is only #
-# supported in m_ldapauth). #
+# ldapauth above (except for the verbose setting, that is only #
+# supported in ldapauth). #
# Please always specify a password in your <oper> tags even if the #
# opers are to be authenticated via LDAP, so in case this module is #
# not loaded the oper accounts are still protected by a password. #
@@ -1059,44 +1131,37 @@
# If your server is locked and you get disconnected, do a REHASH from #
# shell to open up again. #
# This module is oper-only.
-#<module name="m_lockserv.so">
+#<module name="lockserv">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Map hiding module: replaces /MAP and /LINKS output to users with a #
-# message to see a website, set by maphide="http://link.to/site" in #
+# message to see a website, set by maphide="http://test.org/map" in #
# the <security> tag, instead. #
-#<module name="m_maphide.so">
+#<module name="maphide">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Message flood module: Adds message/notice flood protection via
# channel mode +f.
-#<module name="m_messageflood.so">
+#<module name="messageflood">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# MLOCK module: Adds support for server-side enforcement of services
# side MLOCKs. Basically, this module suppresses any mode change that
# would likely be immediately bounced by services.
-#<module name="m_mlock.so">
+#<module name="mlock">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Modenotice module: Adds the /MODENOTICE command that allows opers to
# send notices to all users having the given user mode(s) set.
-#<module name="m_modenotice.so">
+#<module name="modenotice">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# MsSQL module: Allows other SQL modules to access MS SQL Server
-# through a unified API.
-# This module is in extras. Re-run configure with:
-# ./configure --enable-extras=m_mssql.cpp
-# and run make install, then uncomment this module to enable it.
-#<module name="m_mssql.so">
-#
-#-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
-# #
-# m_mssql.so is more complex than described here, see wiki for more #
-# info http://wiki.inspircd.org/Modules/mssql #
+# Monitor module: Adds support for MONITOR which is used by clients to
+# maintain notify lists.
+#<module name="monitor">
#
-#<database module="mssql" name="db" user="user" pass="pass" host="localhost" id="db1">
+# Set the maximum number of entries on a user's monitor list below.
+#<monitor maxentries="30">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# MySQL module: Allows other SQL modules to access MySQL databases
@@ -1104,12 +1169,12 @@
# This module is in extras. Re-run configure with:
# ./configure --enable-extras=m_mysql.cpp
# and run make install, then uncomment this module to enable it.
-#<module name="m_mysql.so">
+#<module name="mysql">
#
#-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
# #
-# m_mysql.so is more complex than described here, see the wiki for #
-# more: http://wiki.inspircd.org/Modules/mysql #
+# mysql is more complex than described here, see the wiki for more #
+# info: http://wiki.inspircd.org/Modules/mysql #
#
#<database module="mysql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database2">
@@ -1118,69 +1183,76 @@
# modes via long-form mode names via +Z and the /PROP command.
# For example, to set a ban, do /mode #channel +Z ban=foo!bar@baz or
# /PROP #channel ban=foo!bar@baz
-#<module name="m_namedmodes.so">
+#<module name="namedmodes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# NAMESX module: Provides support for the NAMESX extension which allows
# clients to see all the prefixes set on a user without getting confused.
# This is supported by mIRC, x-chat, klient, and maybe more.
-#<module name="m_namesx.so">
+#<module name="namesx">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# National characters module:
# 1) Allows using national characters in nicknames.
# 2) Allows using custom (national) casemapping over the network.
-#<module name="m_nationalchars.so">
-#
-# file - filename of existing file in "locales" directory
-# casemapping - custom value for 005 numeric (if you want it to be
-# different from the filename).
+#<module name="nationalchars">
+#
+# file - Location of the file which contains casemapping rules. If this
+# is a relative path then it is relative to "<PWD>/../locales"
+# on UNIX and "<PWD>/locales" on Windows.
+# casemapping - The name of the casemapping sent to clients in the 005
+# numeric. If this is not set then it defaults to the name
+# of the casemapping file unless the file name contains a
+# space in which case you will have to specify it manually.
#<nationalchars file="bynets/russian-w1251-charlink" casemapping="ru_RU.cp1251-charlink">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Nickchange flood protection module: Provides channel mode +F X:Y
# which allows up to X nick changes in Y seconds.
-#<module name="m_nickflood.so">
+#<module name="nickflood">
+#
+# The number of seconds to prevent nick changes for:
+#<nickflood duration="1m">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Nicklock module: Let opers change a user's nick and then stop that
-# user from changing their nick again.
+# user from changing their nick again until unlocked.
# This module is oper-only.
# To use, NICKLOCK and NICKUNLOCK must be in one of your oper class blocks.
-#<module name="m_nicklock.so">
+#<module name="nicklock">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# No CTCP module: Adds the channel mode +C to block CTCPs and extban
# 'C' to block CTCPs sent by specific users.
-#<module name="m_noctcp.so">
+#<module name="noctcp">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# No kicks module: Adds the +Q channel mode and the Q: extban to deny
# certain users from kicking.
-#<module name="m_nokicks.so">
+#<module name="nokicks">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# No nicks module: Adds the +N channel mode, as well as the 'N' extban.
# +N stops all users from changing their nick, the N extban stops
# anyone from matching a +b N:nick!user@host mask from changing their
# nick.
-#<module name="m_nonicks.so">
+#<module name="nonicks">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# No part message module: Adds extban 'p' to block part messages from #
# matching users. #
-#<module name="m_nopartmsg.so">
+#<module name="nopartmsg">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# No notice module: Adds the channel mode +T and the extban 'T' to
# block specific users from noticing the channel.
-#<module name="m_nonotice.so">
+#<module name="nonotice">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Network business join module:
# Allows an oper to join a channel using /OJOIN, giving them +Y on the
# channel which makes them immune to kick/deop/etc.
-#<module name="m_ojoin.so">
+#<module name="ojoin">
#
# Specify the prefix that +Y will grant here.
# Leave 'prefix' empty if you do not wish +Y to grant a prefix.
@@ -1195,16 +1267,17 @@
# /mode #channel +iI O:* is equivalent to channel mode +O, but you
# may also set +iI O:AdminTypeOnly to only allow admins.
# Modes +I and +e work in a similar fashion.
-#<module name="m_operchans.so">
+#<module name="operchans">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Oper join module: Auto-joins opers to a channel upon oper-up.
-# This module is oper-only. For the user equivalent, see m_conn_join.
-#<module name="m_operjoin.so">
+# This module is oper-only. For the user equivalent, see the conn_join
+# module.
+#<module name="operjoin">
#
#-#-#-#-#-#-#-#-#-#-# OPERJOIN CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# If you are using the m_operjoin.so module, specify options here: #
+# If you are using the operjoin module, specify options here: #
# #
# channel - The channel name to join, can also be a comma #
# separated list e.g. "#channel1,#channel2". #
@@ -1224,16 +1297,21 @@
# type "m_operlog" at default loglevel), and optionally to the 'r'
# snomask.
# This module is oper-only.
-#<module name="m_operlog.so">
+#<module name="operlog">
#
# If the following option is on then all oper commands will be sent to
# the snomask 'r'. The default is off.
#<operlog tosnomask="off">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Oper prefixing module: Gives server operators a prefix status
-# character on all channels they are in.
-#<module name="m_operprefix.so">
+# Oper prefixing module: Adds a channel prefix mode +y which is given
+# to all IRC operators automatically on all channels they are in.
+# This prefix mode is more powerful than channel op and other regular
+# prefix modes.
+#
+# Load this module if you want all your IRC operators to have channel
+# operator powers.
+#<module name="operprefix">
#
# You may additionally customise the prefix character.
#<operprefix prefix="!">
@@ -1242,11 +1320,11 @@
# Oper MOTD module: Provides support for separate message of the day
# on oper-up.
# This module is oper-only.
-#<module name="m_opermotd.so">
+#<module name="opermotd">
#
#-#-#-#-#-#-#-#-#-#-# OPERMOTD CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# If you are using the m_opermotd.so module, specify the motd here. #
+# If you are using the opermotd module, specify the motd here. #
# #
# onoper - If on, the message is sent on /OPER, otherwise it's #
# only sent when /OPERMOTD is used. #
@@ -1260,11 +1338,11 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Override module: Adds support for oper override.
# This module is oper-only.
-#<module name="m_override.so">
+#<module name="override">
#
#-#-#-#-#-#-#-#-#-#-# OVERRIDE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# m_override.so is too complex it describe here, see the wiki: #
+# override is too complex it describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/override #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
@@ -1272,21 +1350,22 @@
# being taken by lower level opers against higher level opers.
# Specify the level as the 'level' parameter of the <type> tag.
# This module is oper-only.
-#<module name="m_operlevels.so">
+#<module name="operlevels">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Oper modes module: Allows you to specify modes to add/remove on oper.
# Specify the modes as the 'modes' parameter of the <type> tag
# and/or as the 'modes' parameter of the <oper> tag.
-# This module is oper-only. For the user equivalent, see m_conn_umodes.
-#<module name="m_opermodes.so">
+# This module is oper-only. For the user equivalent, see the
+# conn_umodes module.
+#<module name="opermodes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Password forwarding module: Forwards a password users can send on
# connect to the specified client below. The client is usually NickServ
# and this module is usually used to authenticate users with NickServ
# using their connect password.
-#<module name="m_passforward.so">
+#<module name="passforward">
<passforward
# nick: nick to forward connect passwords to.
@@ -1304,8 +1383,8 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Password hash module: Allows hashed passwords to be used.
-# To be useful, a hashing module like m_sha256.so also needs to be loaded.
-#<module name="m_password_hash.so">
+# To be useful, a hashing module like bcrypt also needs to be loaded.
+#<module name="password_hash">
#
#-#-#-#-#-#-#-#-#-# PASSWORD HASH CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#
#
@@ -1313,28 +1392,44 @@
# password you want to hash. For example:
#
# <oper name="Brain"
-# host="ident@dialup15.isp.com"
-# hash="sha256"
-# password="01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
+# host="ident@dialup15.isp.test.com"
+# hash="bcrypt"
+# password="$2a$10$Mss9AtHHslZTLBrXqM0FB.JBwD.UTSu8A48SfrY9exrpxbsRiRTbO"
# type="NetAdmin">
#
-# Starting from 2.0, you can use a more secure salted hash that prevents simply
-# looking up the hash's value in a rainbow table built for the hash.
+# If you are using a hash algorithm which does not perform salting you can use
+# HMAC to salt your passwords in order to prevent them from being looked up in
+# a rainbow table.
+#
# hash="hmac-sha256" password="lkS1Nbtp$CyLd/WPQXizsbxFUTqFRoMvaC+zhOULEeZaQkUJj+Gg"
#
# Generate hashes using the /MKPASSWD command on the server.
# Don't run it on a server you don't trust with your password.
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# PBKDF2 module: Allows other modules to generate PBKDF2 hashes,
+# usually for cryptographic uses and security.
+# This module relies on other hash providers (e.g. SHA256).
+#<module name="pbkdf2">
+#
+# iterations: Iterations the hashing function runs when generating new
+# hashes.
+# length: Length in bytes of the derived key.
+#<pbkdf2 iterations="12288" length="32">
+# You can override these values with specific values
+# for specific providers if you want to. Example given for SHA256.
+#<pbkdf2prov hash="sha256" iterations="24576">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Permanent channels module: Channels with the permanent channel mode
# will remain open even after everyone else has left the channel, and
# therefore keep things like modes, ban lists and topic. Permanent
# channels -may- need support from your Services package to function
# properly with them. This adds channel mode +P.
# This module is oper-only.
-#<module name="m_permchannels.so">
+#<module name="permchannels">
#
-# If you like, m_permchannels can write a config file of permanent channels
+# If you like, this module can write a config file of permanent channels
# whenever +P is set, unset, or the topic/modes on a +P channel is changed.
# If you want to do this, set the filename below, and uncomment the include.
#
@@ -1344,7 +1439,6 @@
#<include file="permchannels.conf">
#
# You may also create channels on startup by using the <permchannels> block.
-# Don't forget to set them +P in the modes, or they won't stay permanent.
#<permchannels channel="#opers" modes="isP" topic="Opers only.">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
@@ -1353,11 +1447,11 @@
# This module is in extras. Re-run configure with:
# ./configure --enable-extras=m_pgsql.cpp
# and run make install, then uncomment this module to enable it.
-#<module name="m_pgsql.so">
+#<module name="pgsql">
#
#-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
# #
-# m_pgsql.so is more complex than described here, see the wiki for #
+# pgsql is more complex than described here, see the wiki for #
# more: http://wiki.inspircd.org/Modules/pgsql #
#
#<database module="pgsql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database" ssl="no">
@@ -1365,18 +1459,18 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Muteban: Implements extended ban 'm', which stops anyone matching
# a mask like +b m:nick!user@host from speaking on channel.
-#<module name="m_muteban.so">
+#<module name="muteban">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Random quote module: Provides a random quote on connect.
# NOTE: Some of these may mimic fatal errors and confuse users and
# opers alike - BEWARE!
-#<module name="m_randquote.so">
+#<module name="randquote">
#
#-#-#-#-#-#-#-#-#-#- RANDOMQUOTES CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# Optional - If you specify to use the m_randquote.so module, then #
-# specify below the path to the quotes file. #
+# Optional - If you specify to use the randquote module, then specify #
+# below the path to the quotes file. #
# #
#<randquote file="quotes.txt">
@@ -1390,37 +1484,37 @@
# This also breaks linking to servers that do not have the option. #
# This defaults to false for the 2.0 version, it will be enabled in #
# all the future versions. #
-#<module name="m_redirect.so">
+#<module name="redirect">
#<redirect antiredirect="true">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for glob or wildcard (?/*) matching.
-# You must have at least 1 provider loaded to use m_filter or m_rline
+# You must have at least 1 provider loaded to use the filter or rline
# modules. This module has no additional requirements, as it uses the
# matching already present in InspIRCd core.
-#<module name="m_regex_glob.so">
+#<module name="regex_glob">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for PCRE (Perl-Compatible Regular
# Expressions). You need libpcre installed to compile and load this
-# module. You must have at least 1 provider loaded to use m_filter or
-# m_rline.
-#<module name="m_regex_pcre.so">
+# module. You must have at least 1 provider loaded to use the filter or
+# rline modules.
+#<module name="regex_pcre">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular Expression Provider for RE2 Regular Expressions.
# You need libre2 installed and in your include/library paths in order
# to compile and load this module.
-#<module name="m_regex_re2.so">
+#<module name="regex_re2">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for POSIX regular expressions.
# You shouldn't need any additional libraries on a POSIX-compatible
# system (i.e.: any Linux, BSD, but not Windows). You must have at
-# least 1 provider loaded to use m_filter or m_rline.
+# least 1 provider loaded to use filter or rline.
# On POSIX-compliant systems, regex syntax can be found by using the
# command: 'man 7 regex'.
-#<module name="m_regex_posix.so">
+#<module name="regex_posix">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for C++11 std::regex regular expressions.
@@ -1430,7 +1524,7 @@
# You should verify that std::regex is supported by your setup before
# using this module, as it may compile normally but won't do anything
# on some implementations.
-#<module name="m_regex_stdlib.so">
+#<module name="regex_stdlib">
#
# Specify the regular expression engine to use here. Valid settings are
# bre, ere, awk, grep, egrep, ecmascript (default if not specified).
@@ -1439,10 +1533,10 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for TRE regular expressions.
# This is the same regular expression engine used by UnrealIRCd, so
-# if you are most familiar with the syntax of /spamfilter from there,
+# if you are most familiar with the syntax of /SPAMFILTER from there,
# this is the provider you want. You need libtre installed in order
# to compile and load this module.
-#<module name="m_regex_tre.so">
+#<module name="regex_tre">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Registered users only channel creation module. If enabled, only
@@ -1450,15 +1544,21 @@
#
# You probably *DO NOT* want to load this module on a public network.
#
-#<module name="m_regonlycreate.so">
+#<module name="regonlycreate">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Remove module: Adds the /REMOVE command which is a peaceful
# alternative to /KICK.
-#<module name="m_remove.so">
+#<module name="remove">
+#
+# supportnokicks: If true, /REMOVE is not allowed on channels where the
+# nokicks (+Q) mode is set. Defaults to false.
+# protectedrank: Members having this rank or above may not be /REMOVE'd
+# by anyone. Set to 0 to disable this feature. Defaults to 50000.
+#<remove supportnokicks="true" protectedrank="50000">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# A module to block, kick or ban upon similiar messages being uttered several times.
+# A module to block, kick or ban upon similar messages being uttered several times.
# Syntax [~*][lines]:[sec]{[:difference]}{[:matchlines]}
# ~ is to block, * is to ban, default is kick.
# lines - In mode 1 the amount of lines that has to match consecutively - In mode 2 the size of the backlog to keep for matching
@@ -1475,25 +1575,25 @@
# before they are checked, resulting in less CPU usage. Increasing this beyond 512
# doesn't have any effect, as the maximum length of a message on IRC cannot exceed that.
#<repeat maxbacklog="20" maxlines="20" maxdistance="50" maxsecs="0" size="512">
-#<module name="m_repeat.so">
+#<module name="repeat">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Restricted channels module: Allows only opers to create channels.
#
# You probably *DO NOT* want to load this module on a public network.
#
-#<module name="m_restrictchans.so">
+#<module name="restrictchans">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Restrict message module: Allows users to only message opers.
#
# You probably *DO NOT* want to load this module on a public network.
#
-#<module name="m_restrictmsg.so">
+#<module name="restrictmsg">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# R-Line module: Ban users through regular expression patterns.
-#<module name="m_rline.so">
+#<module name="rline">
#
#-#-#-#-#-#-#-#-#-#-#-#- RLINE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
#
@@ -1503,7 +1603,7 @@
# Also, this is where you set what Regular Expression engine is to be
# used. If you ever change it while running, all of your R-Lines will
# be wiped. This is the regex engine used by all R-Lines set, and
-# m_regex_<engine>.so must be loaded, or rline will be non-functional
+# regex_<engine> must be loaded, or rline will be non-functional
# until you load it or change the engine to one that is loaded.
#
#<rline matchonnickchange="yes" engine="pcre">
@@ -1521,7 +1621,7 @@
# Allows channel mods to remove list modes en masse.
# Syntax: /rmode <channel> <mode> [pattern]
# E.g. '/rmode #Channel b m:*' will remove all mute-extbans on the channel.
-#<module name="m_rmode.so">
+#<module name="rmode">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SAJOIN module: Adds the /SAJOIN command which forcibly joins a user
@@ -1530,14 +1630,14 @@
# To use, SAJOIN must be in one of your oper class blocks.
# Opers need the users/sajoin-others priv to be able to /SAJOIN users
# other than themselves.
-#<module name="m_sajoin.so">
+#<module name="sajoin">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SAKICK module: Adds the /SAKICK command which kicks a user from the
# given channel.
# This module is oper-only.
# To use, SAKICK must be in one of your oper class blocks.
-#<module name="m_sakick.so">
+#<module name="sakick">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SAMODE module: Adds the /SAMODE command which allows server operators
@@ -1545,55 +1645,54 @@
# channel priviliges. Also allows changing user modes for any user.
# This module is oper-only.
# To use, SAMODE must be in one of your oper class blocks.
-#<module name="m_samode.so">
+#<module name="samode">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SANICK module: Adds the /SANICK command which allows opers to change
# users' nicks.
# This module is oper-only.
# To use, SANICK must be in one of your oper class blocks.
-#<module name="m_sanick.so">
+#<module name="sanick">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SAPART module: Adds the /SAPART command which forcibly parts a user
# from a channel.
# This module is oper-only.
# To use, SAPART must be in one of your oper class blocks.
-#<module name="m_sapart.so">
+#<module name="sapart">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SAQUIT module: Adds the /SAQUIT command which forcibly quits a user.
# This module is oper-only.
# To use, SAQUIT must be in one of your oper class blocks.
-#<module name="m_saquit.so">
+#<module name="saquit">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SATOPIC module: Adds the /SATOPIC command which allows changing the
# topic on a channel without requiring any channel priviliges.
# This module is oper-only.
# To use, SATOPIC must be in one of your oper class blocks.
-#<module name="m_satopic.so">
+#<module name="satopic">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SASL authentication module: Provides support for IRC Authentication
-# Layer via AUTHENTICATE. Note: You also need to have m_cap.so loaded
+# Layer via AUTHENTICATE. Note: You also need to have cap loaded
# for SASL to work.
-#<module name="m_sasl.so">
+#<module name="sasl">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Secure list module: Prevent /LIST in the first minute of connection,
# crippling most spambots and trojan spreader bots.
-#<module name="m_securelist.so">
+#<module name="securelist">
#
#-#-#-#-#-#-#-#-#-# SECURELIST CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
# #
-# Securelist can be harmful to some IRC search engines such as #
-# netsplit.de and searchirc.com. To prevent securelist blocking these #
-# sites from listing, define exception tags as shown below: #
-#<securehost exception="*@*.searchirc.org">
+# Securelist can be harmful to some IRC search engines. To prevent #
+# securelist blocking these sites from listing, define exception tags #
+# as shown below: #
#<securehost exception="*@*.netsplit.de">
-#<securehost exception="*@echo940.server4you.de">
#<securehost exception="*@*.ircdriven.com">
+#<securehost exception="*@*.irc-source.com">
# #
# Define the following variable to change how long a user must wait #
# before issuing a LIST. If not defined, defaults to 60 seconds. #
@@ -1603,19 +1702,19 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Servprotect module: Provides support for Austhex style +k /
# UnrealIRCD +S services mode.
-#<module name="m_servprotect.so">
+#<module name="servprotect">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# See nicks module: Adds snomask +n and +N which show local and remote
# nick changes.
# This module is oper-only.
-#<module name="m_seenicks.so">
+#<module name="seenicks">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Set idle module: Adds a command for opers to change their idle time.
# This module is oper-only.
# To use, SETIDLE must be in one of your oper class blocks.
-#<module name="m_setidle.so">
+#<module name="setidle">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Services support module: Adds several usermodes such as +R and +M.
@@ -1626,41 +1725,45 @@
# as identified separately from the idea of a master account, which
# can be useful for services which are heavily nick-as-account centric.
#
-# Also of note is that this module implements three extbans:
+# Also of note is that this module implements two extbans:
# +b R: (stop matching account names from joining)
-# +b M: (stop matching account names from speaking)
# +b U:n!u@h (blocks matching unregistered users)
#
-#<module name="m_services_account.so">
+#<module name="services_account">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Sethost module: Adds the /SETHOST command.
# This module is oper-only.
# To use, SETHOST must be in one of your oper class blocks.
-# See m_chghost for how to customise valid chars for hostnames
-#<module name="m_sethost.so">
+# See the chghost module for how to customise valid chars for hostnames.
+#<module name="sethost">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Setident module: Adds the /SETIDENT command.
# This module is oper-only.
# To use, SETIDENT must be in one of your oper class blocks.
-#<module name="m_setident.so">
+#<module name="setident">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SETNAME module: Adds the /SETNAME command.
-#<module name="m_setname.so">
+#<module name="setname">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Serverban: Implements extended ban 's', which stops anyone connected
# to a server matching a mask like +b s:server.mask.here from joining.
-#<module name="m_serverban.so">
+#<module name="serverban">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SHA1 module: Allows other modules to generate SHA1 hashes.
+# Required by the WebSocket module.
+#<module name="sha1">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Showfile: Provides support for showing a text file to users when #
# they enter a command. #
# This module adds one command for each <showfile> tag that shows the #
# given file to the user as a series of messages or numerics. #
-#<module name="m_showfile.so"> #
+#<module name="showfile"> #
# #
#-#-#-#-#-#-#-#-#-#-# SHOWFILE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
# #
@@ -1670,7 +1773,7 @@
# By default same as the command name. #
# method - How should the file be shown? #
# * numeric: Send contents using a numeric #
-# (similiar to /MOTD; the default). #
+# (similar to /MOTD; the default). #
# * notice: Send contents as a series of notices. #
# * msg: Send contents as a series of private messages. #
# colors - If true, color codes (\c, \b, \u, etc.) will be processed #
@@ -1696,7 +1799,7 @@
# Show whois module: Adds the +W usermode which allows opers to see
# when they are /WHOIS'd.
# This module is oper-only by default.
-#<module name="m_showwhois.so">
+#<module name="showwhois">
#
# If you wish, you may also let users set this mode. Only opers with the
# users/auspex priv will see real hosts of people, though.
@@ -1711,7 +1814,7 @@
# executing all except configured commands.
# This module is oper-only.
# To use, SHUN must be in one of your oper class blocks.
-#<module name="m_shun.so">
+#<module name="shun">
#
# You may also configure which commands you wish a user to be able to
# perform when shunned. It should be noted that if a shunned user
@@ -1727,60 +1830,68 @@
# SSL channel mode module: Adds support for SSL-only channels via
# channel mode +z and the 'z' extban which matches SSL client
# certificate fingerprints.
-# Does not do anything useful without a working SSL module (see below).
-#<module name="m_sslmodes.so">
+# Does not do anything useful without a working SSL module and the
+# sslinfo module (see below).
+#<module name="sslmodes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
# if enabled. You must answer 'yes' in ./configure when asked or
# manually symlink the source for this module from the directory
# src/modules/extra, if you want to enable this, or it will not load.
-#<module name="m_ssl_gnutls.so">
+#<module name="ssl_gnutls">
#
#-#-#-#-#-#-#-#-#-#-#- GNUTLS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# m_ssl_gnutls.so is too complex to describe here, see the wiki: #
+# ssl_gnutls is too complex to describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/ssl_gnutls #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SSL info module: Allows users to retrieve information about other
# users' peer SSL certificates and keys. This can be used by client
-# scripts to validate users. For this to work, one of m_ssl_gnutls.so
-# or m_ssl_openssl.so must be loaded. This module also adds the
+# scripts to validate users. For this to work, one of ssl_gnutls
+# or ssl_openssl must be loaded. This module also adds the
# "* <user> is using a secure connection" whois line, the ability for
-# opers to use SSL fingerprints to verify their identity and the
+# opers to use SSL cert fingerprints to verify their identity and the
# ability to force opers to use SSL connections in order to oper up.
# It is highly recommended to load this module if you use SSL on your
# network.
# For how to use the oper features, please see the first example <oper> tag
# in opers.conf.example.
#
-#<module name="m_sslinfo.so">
+#<module name="sslinfo">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# mbedTLS SSL module: Adds support for SSL/TLS connections using mbedTLS.
+#<module name="ssl_mbedtls">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
# if enabled. You must answer 'yes' in ./configure when asked or symlink
# the source for this module from the directory src/modules/extra, if
# you want to enable this, or it will not load.
-#<module name="m_ssl_openssl.so">
+#<module name="ssl_openssl">
#
#-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# m_ssl_openssl.so is too complex to describe here, see the wiki: #
+# ssl_openssl is too complex to describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/ssl_openssl #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Strip color module: Adds channel mode +S that strips mIRC color
-# codes from all messages sent to the channel.
-#<module name="m_stripcolor.so">
+# Strip color module: Adds channel mode +S that strips color codes and
+# all control codes except CTCP from all messages sent to the channel.
+#<module name="stripcolor">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Silence module: Adds support for the /SILENCE command, which allows
# users to have a server-side ignore list for their client.
-#<module name="m_silence.so">
+#<module name="silence">
#
# Set the maximum number of entries allowed on a user's silence list.
-#<silence maxentries="32">
+#<silence maxentries="32"
+#
+# Whether messages from U-lined servers will bypass silence masks.
+#exemptuline="yes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SQLite3 module: Allows other SQL modules to access SQLite3 #
@@ -1789,12 +1900,12 @@
# ./configure --enable-extras=m_sqlite.cpp
# and run make install, then uncomment this module to enable it. #
#
-#<module name="m_sqlite3.so">
+#<module name="sqlite3">
#
#-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
# #
-# m_sqlite.so is more complex than described here, see the wiki for #
-# more: http://wiki.inspircd.org/Modules/sqlite3 #
+# sqlite is more complex than described here, see the wiki for more #
+# info: http://wiki.inspircd.org/Modules/sqlite3 #
#
#<database module="sqlite" hostname="/full/path/to/database.db" id="anytext">
@@ -1805,11 +1916,11 @@
# ./configure --enable-extras=m_sqlauth.cpp
# and run make install, then uncomment this module to enable it.
#
-#<module name="m_sqlauth.so">
+#<module name="sqlauth">
#
#-#-#-#-#-#-#-#-#-#-#- SQLAUTH CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# m_sqlauth.so is too complex to describe here, see the wiki: #
+# sqlauth is too complex to describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/sqlauth #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
@@ -1818,7 +1929,7 @@
# ./configure --enable-extras=m_sqloper.cpp
# and run make install, then uncomment this module to enable it.
#
-#<module name="m_sqloper.so">
+#<module name="sqloper">
#
#-#-#-#-#-#-#-#-#-#-#- SQLOPER CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
@@ -1827,18 +1938,18 @@
# #
# See also: http://wiki.inspircd.org/Modules/sqloper #
# #
-#<sqloper dbid="1" hash="md5">
+#<sqloper dbid="1" hash="bcrypt">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# StartTLS module: Implements STARTTLS, which allows clients #
# connected to non SSL enabled ports to enable SSL, if a proper SSL #
-# module is loaded (either m_ssl_gnutls or m_ssl_openssl). #
-#<module name="m_starttls.so">
+# module is loaded (either ssl_gnutls or ssl_openssl). #
+#<module name="starttls">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SVSHold module: Implements SVSHOLD. Like Q:Lines, but can only be #
# added/removed by Services. #
-#<module name="m_svshold.so">
+#<module name="svshold">
# SVSHOLD does not generate server notices by default, you can turn
# notices on by uncommenting the next line.
#<svshold silent="false">
@@ -1847,29 +1958,23 @@
# SWHOIS module: Allows you to add arbitrary lines to user WHOIS.
# This module is oper-only.
# To use, SWHOIS must be in one of your oper class blocks.
-#<module name="m_swhois.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Test module: Enable this to create a command useful in testing
-# flood control. To avoid accidental use on live networks, the server
-# name must contain ".test" to load the module
-#<module name="m_testnet.so">
+#<module name="swhois">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Timed bans module: Adds timed channel bans with the /TBAN command.
-#<module name="m_timedbans.so">
+#<module name="timedbans">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Test line module: Adds the /TLINE command, used to test how many
# users a /GLINE or /ZLINE etc. would match.
# This module is oper-only.
# To use, TLINE must be in one of your oper class blocks.
-#<module name="m_tline.so">
+#<module name="tline">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Topiclock module: implements server-side topic locking to achieve deeper
# integration with services packages.
-#<module name="m_topiclock.so">
+#<module name="topiclock">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# UHNAMES support module: Adds support for the IRCX style UHNAMES
@@ -1877,23 +1982,23 @@
# each user, saving clients from doing a WHO on the channel.
# If a client does not support UHNAMES it will not enable it, this will
# not break incompatible clients.
-#<module name="m_uhnames.so">
+#<module name="uhnames">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Uninvite module: Adds the /UNINVITE command which lets users remove
# pending invites from channels without waiting for the user to join.
-#<module name="m_uninvite.so">
+#<module name="uninvite">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Userip module: Adds the /USERIP command.
# Allows users to query their own IP, also allows opers to query the IP
# of anyone else.
-#<module name="m_userip.so">
+#<module name="userip">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Vhost module: Adds the VHOST command which allows for adding virtual
# hosts which are accessible using a username and password in the config.
-#<module name="m_vhost.so">
+#<module name="vhost">
#
#-#-#-#-#-#-#-#-#-#-#- VHOST CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
# #
@@ -1902,32 +2007,39 @@
# pass - Password for the vhost. #
# #
# hash - The hash for the specific user (optional) #
-# m_password_hash.so and a hashing module must be loaded #
-# for this to work. #
+# password_hash and a hashing module must be loaded for #
+# this to work. #
# #
# host - Vhost to set. #
#
-#<vhost user="some_username" pass="some_password" host="some.host">
-#<vhost user="foo" password="fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" hash="sha256" host="some.other.host">
+#<vhost user="some_username" pass="some_password" host="some.host.test.cc">
+#<vhost user="foo" password="$2a$10$iTuYLT6BRhRlOgzfsW9oPe62etW.oXwSpyKw5rJit64SGZanLXghO" hash="bcrypt" host="some.other.host.example.com">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Watch module: Adds the WATCH command, which is used by clients to
# maintain notify lists.
-#<module name="m_watch.so">
+#<module name="watch">
#
# Set the maximum number of entries on a user's watch list below.
#<watch maxentries="32">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# WebSocket module: Adds HTML5 WebSocket support.
+# Specify hook="websocket" in a <bind> tag to make that port accept
+# WebSocket connections. Compatible with SSL/TLS.
+# Requires SHA-1 hash support available in the sha1 module.
+#<module name="websocket">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# XLine database: Stores all *Lines (G/Z/K/R/any added by other modules)
# in a file which is re-loaded on restart. This is useful
# for two reasons: it keeps bans so users may not evade them, and on
# bigger networks, server connections will take less time as there will
# be a lot less bans to apply - as most of them will already be there.
-#<module name="m_xline_db.so">
+#<module name="xline_db">
# Specify the filename for the xline database here.
-#<xlinedb filename="data/xline.db">
+#<xlinedb filename="xline.db">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# ____ _ _____ _ _ ____ _ _ _ #
@@ -1936,8 +2048,8 @@
# | _ < __/ (_| | (_| | | | | | | | \__ \ | |_) | | |_|_| #
# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
# #
-# To link servers to InspIRCd, you MUST load the m_spanningtree #
-# module. If you don't do this, server links will NOT work at all. #
+# To link servers to InspIRCd, you MUST load the spanningtree module. #
+# If you don't do this, server links will NOT work at all. #
# This is by design, to allow for the implementation of other linking #
# protocols in modules in the future. #
@@ -1946,4 +2058,4 @@
# tree protocol (see the READ THIS BIT section above).
# You will almost always want to load this.
#
-#<module name="m_spanningtree.so">
+#<module name="spanningtree">
diff --git a/docs/conf/modules/charybdis.conf.example b/docs/conf/modules/charybdis.conf.example
index bd99f7dc2..6f8171b41 100644
--- a/docs/conf/modules/charybdis.conf.example
+++ b/docs/conf/modules/charybdis.conf.example
@@ -1,6 +1,6 @@
-<module name="m_md5.so">
-<module name="m_sha256.so">
-<module name="m_alias.so">
+<module name="md5">
+<module name="sha256">
+<module name="alias">
<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
@@ -15,26 +15,26 @@
<alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
<alias text="ID" replace="PRIVMSG NickServ :IDENTIFY $2" requires="NickServ" uline="yes">
-<module name="m_banexception.so">
-<module name="m_banredirect.so">
-<module name="m_blockcolor.so">
-<module name="m_callerid.so">
+<module name="banexception">
+<module name="banredirect">
+<module name="blockcolor">
+<module name="callerid">
<callerid maxaccepts="16"
operoverride="no"
tracknick="no"
cooldown="60">
-<module name="m_cap.so">
-<module name="m_cban.so">
+<module name="cap">
+<module name="cban">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# CGI:IRC module: Adds support for automatic host changing in CGI:IRC
# (http://cgiirc.sourceforge.net).
-#<module name="m_cgiirc.so">
+#<module name="cgiirc">
#
#-#-#-#-#-#-#-#-#-#-#-# CGIIRC CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
#
-# Optional - If you specify to use m_cgiirc, then you must specify one
+# Optional - If you specify to use cgiirc, then you must specify one
# or more cgihost tags which indicate authorised CGI:IRC servers which
# will be connecting to your network, and an optional cgiirc tag.
# For more information see: http://wiki.inspircd.org/Modules/cgiirc
@@ -68,13 +68,13 @@
# sessions maximum.
#
-<module name="m_chancreate.so">
+<module name="chancreate">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Channel names module: Allows disabling channels which have certain
# characters in the channel name such as bold, colorcodes, etc. which
# can be quite annoying and allow users to on occasion have a channel
# that looks like the name of another channel on the network.
-<module name="m_channames.so">
+<module name="channames">
<channames
# denyrange: characters or range of characters to deny in channel
@@ -85,16 +85,16 @@
# in channel names.
allowrange="">
-<module name="m_channelban.so">
-<module name="m_chghost.so">
+<module name="channelban">
+<module name="chghost">
<hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
-<module name="m_chgident.so">
-<module name="m_chgname.so">
-<module name="m_cloaking.so">
+<module name="chgident">
+<module name="chgname">
+<module name="cloaking">
#
#-#-#-#-#-#-#-#-#-#-#- CLOAKING CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# If you specify the m_cloaking.so module as above, you must define #
+# If you specify the cloaking module as above, you must define #
# cloak keys, and optionally a cloak prefix as shown below. The cloak #
# keys must be shared across the network for correct cloaking. #
# #
@@ -126,8 +126,8 @@
key="secret"
prefix="net-">
-<module name="m_close.so">
-<module name="m_conn_umodes.so">
+<module name="close">
+<module name="conn_umodes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Connectban: Provides IP connection throttling. Any IP range that connects
@@ -141,11 +141,11 @@
#<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128">
# This allows for 10 connections in an hour with a 10 minute ban if that is exceeded.
#
-#<module name="m_connectban.so">
+#<module name="connectban">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Connection throttle module.
-#<module name="m_connflood.so">
+#<module name="connflood">
#
#-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# seconds, maxconns - Amount of connections per <seconds>.
@@ -163,46 +163,46 @@
#<connflood seconds="30" maxconns="3" timeout="30"
# quitmsg="Throttled" bootwait="10">
-<module name="m_deaf.so">
-<module name="m_dnsbl.so">
-<module name="m_gecosban.so">
-<module name="m_globalload.so">
-<module name="m_ident.so">
+<module name="deaf">
+<module name="dnsbl">
+<module name="gecosban">
+<module name="globalload">
+<module name="ident">
<ident timeout="1">
-<module name="m_inviteexception.so">
-<module name="m_joinflood.so">
-<module name="m_knock.so">
-<module name="m_namesx.so">
-<module name="m_operchans.so">
-<module name="m_operlog.so">
-<module name="m_opermodes.so">
-<module name="m_password_hash.so">
-<module name="m_permchannels.so">
-<module name="m_muteban.so">
-<module name="m_redirect.so">
+<module name="inviteexception">
+<module name="joinflood">
+<module name="knock">
+<module name="namesx">
+<module name="operchans">
+<module name="operlog">
+<module name="opermodes">
+<module name="password_hash">
+<module name="permchannels">
+<module name="muteban">
+<module name="redirect">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for glob or wildcard (?/*) matching.
-# You must have at least 1 provider loaded to use m_filter or m_rline
-# modules. This module has no additional requirements, as it uses the
-# matching already present in InspIRCd core.
-#<module name="m_regex_glob.so">
+# You must have at least 1 provider loaded to use the filter or the
+# rline modules. This module has no additional requirements, as it uses
+# the matching already present in InspIRCd core.
+#<module name="regex_glob">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for PCRE (Perl-Compatible Regular
# Expressions). You need libpcre installed to compile and load this
-# module. You must have at least 1 provider loaded to use m_filter or
-# m_rline.
-#<module name="m_regex_pcre.so">
+# module. You must have at least 1 provider loaded to use the filter or
+# the rline module.
+#<module name="regex_pcre">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for POSIX regular expressions.
# You shouldn't need any additional libraries on a POSIX-compatible
# system (ie: any Linux, BSD, but not Windows). You must have at least
-# 1 provider loaded to use m_filter or m_rline.
+# 1 provider loaded to use the filter or the rline module.
# On POSIX-compliant systems, regex syntax can be found by using the
# command: 'man 7 regex'.
-#<module name="m_regex_posix.so">
+#<module name="regex_posix">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Registered users only channel creation
@@ -210,11 +210,11 @@
#
# You probably *DO NOT* want to load this module on a public network.
#
-#<module name="m_regonlycreate.so">
+#<module name="regonlycreate">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Ban users through regular expression patterns
-#<module name="m_rline.so">
+#<module name="rline">
#
#-#-#-#-#-#-#-#-#-#-#-#- RLINE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
#
@@ -224,7 +224,7 @@
# Also, this is where you set what Regular Expression engine is to be
# used. If you ever change it while running, all of your R-Lines will be
# wiped. This is the regex engine used by all R-Lines set, and
-# m_regex_<engine>.so must be loaded, or rline will be nonfunctional
+# regex_<engine> must be loaded, or rline will be nonfunctional
# until you load it or change the engine to one that is loaded.
#
#<rline matchonnickchange="yes" engine="pcre">
@@ -237,66 +237,66 @@
# use glob. For this reason, is recommended to use a real regex engine
# so that at least \s or [[:space:]] is available.
-<module name="m_sasl.so">
-<module name="m_servprotect.so">
-<module name="m_services_account.so">
-<module name="m_sethost.so">
-<module name="m_serverban.so">
-<module name="m_showwhois.so">
+<module name="sasl">
+<module name="servprotect">
+<module name="services_account">
+<module name="sethost">
+<module name="serverban">
+<module name="showwhois">
<showwhois opersonly="yes" showfromopers="yes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# SSL channel mode module: Adds support for SSL-only channels via
-# channel mode +z and the 'z' extban which matches SSL client
+# SSL channel mode module: Adds support for SSL-only channels via
+# channel mode +z and the 'z' extban which matches SSL client
# certificate fingerprints.
# Does not do anything useful without a working SSL module (see below).
-#<module name="m_sslmodes.so">
+#<module name="sslmodes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
# if enabled. You must answer 'yes' in ./configure when asked or
# manually symlink the source for this module from the directory
# src/modules/extra, if you want to enable this, or it will not load.
-#<module name="m_ssl_gnutls.so">
+#<module name="ssl_gnutls">
#
#-#-#-#-#-#-#-#-#-#-#- GNUTLS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# m_ssl_gnutls.so is too complex to describe here, see the wiki: #
+# ssl_gnutls is too complex to describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/ssl_gnutls #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SSL Info module: Allows users to retrieve information about other
# user's peer SSL certificates and keys. This can be used by client
-# scripts to validate users. For this to work, one of m_ssl_gnutls.so
-# or m_ssl_openssl.so must be loaded. This module also adds the
+# scripts to validate users. For this to work, one of ssl_gnutls
+# or ssl_openssl must be loaded. This module also adds the
# "* <user> is using a secure connection" whois line, the ability for
-# opers to use SSL fingerprints to verify their identity and the ability
-# to force opers to use SSL connections in order to oper up.
+# opers to use SSL cert fingerprints to verify their identity and the
+# ability to force opers to use SSL connections in order to oper up.
# It is highly recommended to load this module especially if
# you use SSL on your network.
# For how to use the oper features, please see the first example <oper> tag
# in opers.conf.example.
#
-#<module name="m_sslinfo.so">
+#<module name="sslinfo">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
# if enabled. You must answer 'yes' in ./configure when asked or symlink
# the source for this module from the directory src/modules/extra, if
# you want to enable this, or it will not load.
-#<module name="m_ssl_openssl.so">
+#<module name="ssl_openssl">
#
#-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# m_ssl_openssl.so is too complex to describe here, see the wiki: #
+# ssl_openssl is too complex to describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/ssl_openssl #
-<module name="m_stripcolor.so">
-<module name="m_svshold.so">
-<module name="m_tline.so">
-<module name="m_uhnames.so">
-<module name="m_watch.so">
+<module name="stripcolor">
+<module name="svshold">
+<module name="tline">
+<module name="uhnames">
+<module name="watch">
<watch maxentries="32">
-<module name="m_xline_db.so">
+<module name="xline_db">
-<module name="m_spanningtree.so">
+<module name="spanningtree">
diff --git a/docs/conf/modules/unrealircd.conf.example b/docs/conf/modules/unrealircd.conf.example
index 1ed7b33b2..102307661 100644
--- a/docs/conf/modules/unrealircd.conf.example
+++ b/docs/conf/modules/unrealircd.conf.example
@@ -1,8 +1,8 @@
-<module name="m_md5.so">
-<module name="m_sha256.so">
+<module name="md5">
+<module name="sha256">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Alias module: Allows you to define server-side command aliases.
-<module name="m_alias.so">
+<module name="alias">
<fantasy prefix="!" allowbots="no">
# Aliases
<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
@@ -35,28 +35,28 @@
#<alias text="NICKSERV" format=":IDENTIFY *" replace="PRIVMSG NickServ :IDENTIFY $3-"
# requires="NickServ" uline="yes">
-<module name="m_allowinvite.so">
-<module name="m_alltime.so">
-<module name="m_auditorium.so">
+<module name="allowinvite">
+<module name="alltime">
+<module name="auditorium">
<auditorium showops="yes" operoverride="yes">
-<module name="m_banexception.so">
-<module name="m_blockcaps.so">
+<module name="banexception">
+<module name="blockcaps">
<blockcaps percent="50"
minlen="5"
capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! ">
-<module name="m_blockcolor.so">
-<module name="m_botmode.so">
-<module name="m_censor.so">
+<module name="blockcolor">
+<module name="botmode">
+<module name="censor">
<include file="inspircd.censor.example">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# CGI:IRC module: Adds support for automatic host changing in CGI:IRC
# (http://cgiirc.sourceforge.net).
-#<module name="m_cgiirc.so">
+#<module name="cgiirc">
#
#-#-#-#-#-#-#-#-#-#-#-# CGIIRC CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
#
-# Optional - If you specify to use m_cgiirc, then you must specify one
+# Optional - If you specify to use cgiirc, then you must specify one
# or more cgihost tags which indicate authorised CGI:IRC servers which
# will be connecting to your network, and an optional cgiirc tag.
# For more information see: http://wiki.inspircd.org/Modules/cgiirc
@@ -90,42 +90,41 @@
# sessions maximum.
#
-<module name="m_chanfilter.so">
+<module name="chanfilter">
<chanfilter hidemask="yes">
-<module name="m_check.so">
-<module name="m_chghost.so">
+<module name="check">
+<module name="chghost">
<hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
-<module name="m_chgident.so">
-<module name="m_chgname.so">
-<module name="m_cloaking.so">
+<module name="chgident">
+<module name="chgname">
+<module name="cloaking">
<cloak mode="half"
key="secret"
prefix="net-">
-<module name="m_close.so">
-<module name="m_clones.so">
-<module name="m_commonchans.so">
+<module name="close">
+<module name="clones">
+<module name="commonchans">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Auto join on connect module: Allows you to force users to join one
# or more channels automatically upon connecting to the server.
-#<module name="m_conn_join.so">
+#<module name="conn_join">
#
#-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
#
-# If you have m_conn_join.so loaded, you can configure it using the
-# follow values:
+# If you have the conn_join module loaded, you can configure it below:
#
#<autojoin channel="#one,#two,#three">
-<module name="m_conn_umodes.so">
-<module name="m_cycle.so">
+<module name="conn_umodes">
+<module name="cycle">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Connection throttle module.
-#<module name="m_connflood.so">
+#<module name="connflood">
#
#-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# seconds, maxconns - Amount of connections per <seconds>.
@@ -145,7 +144,7 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# DCCALLOW module: Adds the /DCCALLOW command.
-<module name="m_dccallow.so">
+<module name="dccallow">
#
#-#-#-#-#-#-#-#-#-#-#- DCCALLOW CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# blockchat - Whether to block DCC CHAT as well as DCC SEND
@@ -164,30 +163,32 @@
#
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-<module name="m_deaf.so">
-<module name="m_denychans.so">
+<module name="deaf">
+<module name="denychans">
#<badchan name="#gods*" allowopers="yes" reason="Tortoises!"> #
#<badchan name="#heaven" redirect="#hell" reason="Nice try!"> #
-<module name="m_devoice.so">
+<module name="devoice">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Filter module: Provides message filtering, similar to SPAMFILTER.
-<module name="m_filter.so">
+<module name="filter">
# #
-# This module depends upon a regex provider such as m_regex_pcre or #
-# m_regex_glob to function. You must specify which of these you want #
-# m_filter to use via the tag below. #
+# This module depends upon a regex provider such as regex_pcre or #
+# regex_glob to function. You must specify which of these you want #
+# the filter module to use via the tag below. #
# #
# Valid engines are: #
# #
-# glob - Glob patterns, provided via m_regex_glob.so #
-# pcre - PCRE regexps, provided via m_regex_pcre.so, needs libpcre #
-# tre - TRE regexps, provided via m_regex_tre.so, requires libtre #
-# posix - POSIX regexps, provided via m_regex_posix.so, not availale #
-# on windows, no dependencies on other operating systems. #
+# glob - Glob patterns, provided via regex_glob. #
+# pcre - PCRE regexps, provided via regex_pcre, needs libpcre. #
+# tre - TRE regexps, provided via regex_tre, requires libtre. #
+# posix - POSIX regexps, provided via regex_posix, not available #
+# on Windows, no dependencies on other operating systems. #
+# stdlib - stdlib regexps, provided via regex_stdlib, see comment #
+# at the <module> tag for info on availability. #
# #
-<filteropts engine="glob">
+<filteropts engine="glob">
# #
# Your choice of regex engine must match on all servers network-wide.
#
@@ -197,47 +198,48 @@
#
#-#-#-#-#-#-#-#-#-#-#- FILTER CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# Optional - If you specify to use the m_filter module, then #
+# Optional - If you specify to use the filter module, then #
# specfiy below the path to the filter.conf file, or define some #
# <filter> tags. #
# #
#<include file="filter.conf">
-<module name="m_gecosban.so">
-<module name="m_globops.so">
-<module name="m_globalload.so">
-<module name="m_halfop.so">
-<module name="m_helpop.so">
+<module name="gecosban">
+<module name="globops">
+<module name="globalload">
+<module name="halfop">
+<module name="helpop">
<include file="inspircd.helpop-full.example">
-<module name="m_hidechans.so">
+<module name="hidechans">
<hidechans affectsopers="false">
-<module name="m_hideoper.so">
-<module name="m_ident.so">
+<module name="hideoper">
+<module name="ident">
<ident timeout="1">
-<module name="m_inviteexception.so">
-<module name="m_joinflood.so">
-<module name="m_jumpserver.so">
-<module name="m_knock.so">
-<module name="m_messageflood.so">
-<module name="m_namesx.so">
-<module name="m_nickflood.so">
-<module name="m_noctcp.so">
-<module name="m_nokicks.so">
-<module name="m_nonicks.so">
-<module name="m_nopartmsg.so">
-<module name="m_nonotice.so">
-<module name="m_operchans.so">
+<module name="inviteexception">
+<module name="joinflood">
+<module name="jumpserver">
+<module name="knock">
+<module name="messageflood">
+<module name="namesx">
+<module name="nickflood">
+<module name="noctcp">
+<module name="nokicks">
+<module name="nonicks">
+<module name="nopartmsg">
+<module name="nonotice">
+<module name="operchans">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Oper join module: Auto-joins opers to a channel upon oper-up.
-# This module is oper-only. For the user equivalent, see m_conn_join.
-<module name="m_operjoin.so">
+# This module is oper-only. For the user equivalent, see the conn_join
+# module.
+<module name="operjoin">
#
#-#-#-#-#-#-#-#-#-#-# OPERJOIN CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# If you are using the m_operjoin.so module, specify options here: #
+# If you are using the operjoin module, specify options here: #
# #
# channel - The channel name to join, can also be a comma #
# separated list eg. "#channel1,#channel2". #
@@ -252,52 +254,52 @@
#
#<type name="Helper" autojoin="#help" classes="...">
-<module name="m_operlog.so">
+<module name="operlog">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Oper MOTD module: Provides support for separate message of the day
# on oper-up.
# This module is oper-only.
-#<module name="m_opermotd.so">
+#<module name="opermotd">
#
#-#-#-#-#-#-#-#-#-#-# OPERMOTD CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# If you are using the m_opermotd.so module, specify the motd here #
+# If you are using the opermotd module, specify the motd here #
# #
# onoper - If on, the message is sent on /OPER, otherwise it's #
# only sent when /OPERMOTD is used. #
# #
#<opermotd file="oper.motd" onoper="yes">
-<module name="m_override.so">
+<module name="override">
#-#-#-#-#-#-#-#-#-#-# OVERRIDE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
-# m_override.so is too complex to describe here, see the wiki: #
+# The override module is too complex to describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/override #
-<module name="m_operlevels.so">
-<module name="m_opermodes.so">
-<module name="m_password_hash.so">
-<module name="m_muteban.so">
+<module name="operlevels">
+<module name="opermodes">
+<module name="password_hash">
+<module name="muteban">
-<module name="m_redirect.so">
-<module name="m_regex_glob.so">
+<module name="redirect">
+<module name="regex_glob">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for PCRE (Perl-Compatible Regular
# Expressions). You need libpcre installed to compile and load this
-# module. You must have at least 1 provider loaded to use m_filter or
-# m_rline.
-#<module name="m_regex_pcre.so">
+# module. You must have at least 1 provider loaded to use the filter
+# or the rline module.
+#<module name="regex_pcre">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for POSIX Regular Expressions.
# You shouldn't need any additional libraries on a POSIX-compatible
# system (ie: any Linux, BSD, but not Windows). You must have at least
-# 1 provider loaded to use m_filter or m_rline.
+# 1 provider loaded to use the filter or rline module.
# On POSIX-compliant systems, regex syntax can be found by using the
# command: 'man 7 regex'.
-#<module name="m_regex_posix.so">
+#<module name="regex_posix">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for TRE Regular Expressions.
@@ -305,7 +307,7 @@
# if you are most familiar with the syntax of /spamfilter from there,
# this is the provider you want. You need libtre installed in order
# to compile and load this module.
-#<module name="m_regex_tre.so">
+#<module name="regex_tre">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Registered users only channel creation module. If enabled, only
@@ -313,77 +315,77 @@
#
# You probably *DO NOT* want to load this module on a public network.
#
-#<module name="m_regonlycreate.so">
+#<module name="regonlycreate">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Restricted channels module: Allows only opers to create channels.
#
# You probably *DO NOT* want to load this module on a public network.
#
-#<module name="m_restrictchans.so">
+#<module name="restrictchans">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Restrict message module: Allows users to only message opers.
#
# You probably *DO NOT* want to load this module on a public network.
#
-#<module name="m_restrictmsg.so">
-
-<module name="m_sajoin.so">
-<module name="m_sakick.so">
-<module name="m_samode.so">
-<module name="m_sanick.so">
-<module name="m_sapart.so">
-<module name="m_saquit.so">
-<module name="m_satopic.so">
-<module name="m_servprotect.so">
-<module name="m_seenicks.so">
-<module name="m_setidle.so">
-<module name="m_services_account.so">
-<module name="m_sethost.so">
-<module name="m_setident.so">
-<module name="m_setname.so">
-<module name="m_showwhois.so">
+#<module name="restrictmsg">
+
+<module name="sajoin">
+<module name="sakick">
+<module name="samode">
+<module name="sanick">
+<module name="sapart">
+<module name="saquit">
+<module name="satopic">
+<module name="servprotect">
+<module name="seenicks">
+<module name="setidle">
+<module name="services_account">
+<module name="sethost">
+<module name="setident">
+<module name="setname">
+<module name="showwhois">
<showwhois opersonly="yes" showfromopers="yes">
-<module name="m_shun.so">
+<module name="shun">
<shun enabledcommands="PING PONG QUIT PART JOIN" notifyuser="no" affectopers="no">
-<module name="m_sslmodes.so">
+<module name="sslmodes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
# if enabled. You must answer 'yes' in ./configure when asked or symlink
# the source for this module from the directory src/modules/extra, if
-# you want to enable this, or it will not load.
-#<module name="m_ssl_gnutls.so">
+# you want to enable this, or it will not load.
+#<module name="ssl_gnutls">
#
#-#-#-#-#-#-#-#-#-#-#- GNUTLS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# m_ssl_gnutls.so is too complex to describe here, see the wiki: #
+# ssl_gnutls is too complex to describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/ssl_gnutls #
-<module name="m_sslinfo.so">
+<module name="sslinfo">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
# if enabled. You must answer 'yes' in ./configure when asked or symlink
# the source for this module from the directory src/modules/extra, if
# you want to enable this, or it will not load.
-#<module name="m_ssl_openssl.so">
+#<module name="ssl_openssl">
#
#-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
-# m_ssl_openssl.so is too complex to describe here, see the wiki: #
+# ssl_openssl is too complex to describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/ssl_openssl #
-<module name="m_stripcolor.so">
-<module name="m_svshold.so">
-<module name="m_swhois.so">
-<module name="m_tline.so">
-<module name="m_uhnames.so">
-<module name="m_userip.so">
-<module name="m_watch.so">
+<module name="stripcolor">
+<module name="svshold">
+<module name="swhois">
+<module name="tline">
+<module name="uhnames">
+<module name="userip">
+<module name="watch">
<watch maxentries="32">
-<module name="m_spanningtree.so">
+<module name="spanningtree">
diff --git a/docs/conf/motd.txt.example b/docs/conf/motd.txt.example
index 66fddd344..04f7b11b2 100644
--- a/docs/conf/motd.txt.example
+++ b/docs/conf/motd.txt.example
@@ -10,11 +10,11 @@
Putting the ricer in IRCer since 2007
- //\
+ //\
V \ WELCOME TO AN INSPIRCD NETWORK
\ \_ If you see this, I am probably new.
\,'.`-. If I'm not new, my owner is lazy.
- |\ `. `.
+ |\ `. `.
( \ `. `-. _,.-:\
\ \ `. `-._ __..--' ,-';/
\ `. `-. `-..___..---' _.--' ,'/
@@ -23,7 +23,7 @@
`-_ `-.___ __,--' ,'
`-.__ `----""" __.-'
`--..____..--'
-
+
---- To change, see motd.txt.example -----
/ \
/ * Web: http://www.inspircd.org \
diff --git a/docs/conf/opermotd.txt.example b/docs/conf/opermotd.txt.example
index 110cba8e4..0ac4cfea1 100644
--- a/docs/conf/opermotd.txt.example
+++ b/docs/conf/opermotd.txt.example
@@ -10,11 +10,11 @@
Putting the ricer in IRCer since 2007
- //\
+ //\
V \ WELCOME TO AN INSPIRCD NETWORK
\ \_ If you see this, I am probably new.
\,'.`-. If I'm not new, my owner is lazy.
- |\ `. `.
+ |\ `. `.
( \ `. `-. _,.-:\
\ \ `. `-._ __..--' ,-';/
\ `. `-. `-..___..---' _.--' ,'/
@@ -23,7 +23,7 @@
`-_ `-.___ __,--' ,'
`-.__ `----""" __.-'
`--..____..--'
-
+
-- To change, see opermotd.txt.example ---
/ \
/ * Web: http://www.inspircd.org \
diff --git a/docs/conf/opers.conf.example b/docs/conf/opers.conf.example
index eef8039cb..5e1ec28f5 100644
--- a/docs/conf/opers.conf.example
+++ b/docs/conf/opers.conf.example
@@ -15,7 +15,7 @@
name="Shutdown"
# commands: Oper-only commands that opers of this class can run.
- commands="DIE RESTART REHASH LOADMODULE UNLOADMODULE RELOADMODULE GUNLOADMODULE GRELOADMODULE"
+ commands="DIE RESTART REHASH LOADMODULE UNLOADMODULE RELOADMODULE GLOADMODULE GUNLOADMODULE GRELOADMODULE"
# privs: Special privileges that users with this class may utilise.
# VIEWING:
@@ -39,11 +39,11 @@
# chanmodes: Oper-only channel modes that opers with this class can use.
chanmodes="*">
-<class name="SACommands" commands="SAJOIN SAPART SANICK SAQUIT SATOPIC SAKICK SAMODE">
+<class name="SACommands" commands="SAJOIN SAPART SANICK SAQUIT SATOPIC SAKICK SAMODE OJOIN">
<class name="ServerLink" commands="CONNECT SQUIT RCONNECT RSQUIT MKPASSWD ALLTIME SWHOIS JUMPSERVER LOCKSERV UNLOCKSERV" usermodes="*" chanmodes="*" privs="servers/auspex">
<class name="BanControl" commands="KILL GLINE KLINE ZLINE QLINE ELINE TLINE RLINE CHECK NICKLOCK NICKUNLOCK SHUN CLONES CBAN CLOSE" usermodes="*" chanmodes="*">
<class name="OperChat" commands="WALLOPS GLOBOPS" usermodes="*" chanmodes="*" privs="users/mass-message">
-<class name="HostCloak" commands="SETHOST SETIDENT CHGNAME CHGHOST CHGIDENT SETIDLE" usermodes="*" chanmodes="*" privs="users/auspex">
+<class name="HostCloak" commands="SETHOST SETIDENT SETIDLE CHGNAME CHGHOST CHGIDENT" usermodes="*" chanmodes="*" privs="users/auspex">
#-#-#-#-#-#-#-#-#-#-#-#- OPERATOR COMPOSITION -#-#-#-#-#-#-#-#-#-#-#
@@ -68,7 +68,7 @@
# modes: User modes besides +o that are set on an oper of this type
# when they oper up. Used for snomasks and other things.
- # Requires that m_opermodes.so be loaded.
+ # Requires the opermodes module be loaded.
modes="+s +cCqQ">
<type name="GlobalOp" classes="SACommands OperChat BanControl HostCloak ServerLink" vhost="ircop.omega.example.org">
@@ -97,26 +97,26 @@
host="attila@inspircd.org *@2001:db8::/32"
# ** ADVANCED ** This option is disabled by default.
- # fingerprint: When using the m_sslinfo module, you may specify
+ # fingerprint: When using the sslinfo module, you may specify
# a key fingerprint here. This can be obtained by using the /sslinfo
# command while the module is loaded, and is also noticed on connect.
# This enhances security by verifying that the person opering up has
# a matching SSL client certificate, which is very difficult to
# forge (impossible unless preimage attacks on the hash exist).
- # If m_sslinfo isn't loaded, this option will be ignored.
+ # If the sslinfo module isn't loaded, this option will be ignored.
#fingerprint="67cb9dc013248a829bb2171ed11becd4"
- # autologin: If an SSL fingerprint for this oper is specified, you can
- # have the oper block automatically log in. This moves all security of the
- # oper block to the protection of the client certificate, so be sure that
- # the private key is well-protected! Requires m_sslinfo.
+ # autologin: If an SSL certificate fingerprint for this oper is specified,
+ # you can have the oper block automatically log in. This moves all security
+ # of the oper block to the protection of the client certificate, so be sure
+ # that the private key is well-protected! Requires the sslinfo module.
#autologin="on"
# sslonly: If on, this oper can only oper up if they're using a SSL connection.
# Setting this option adds a decent bit of security. Highly recommended
# if the oper is on wifi, or specifically, unsecured wifi. Note that it
# is redundant to specify this option if you specify a fingerprint.
- # This setting only takes effect if m_sslinfo is loaded.
+ # This setting only takes effect if the sslinfo module is loaded.
#sslonly="yes"
# vhost: Overrides the vhost in the type block. Class and modes may also
@@ -130,8 +130,8 @@
# Operator with a plaintext password and no comments, for easy copy & paste.
<oper
name="Brain"
- password="s3cret"
- host="brain@dialup15.isp.com *@localhost *@example.com *@2001:db8::/32"
+ password="youshouldhashthis"
+ host="brain@dialup15.isp.test.com *@localhost *@example.com *@2001:db8::/32"
#fingerprint="67cb9dc013248a829bb2171ed11becd4"
type="NetAdmin">
@@ -141,18 +141,18 @@
# Remember: This is case sensitive.
name="Adam"
- # hash: What hash this password is hashed with.
- # Requires the module for selected hash (m_md5.so, m_sha256.so
- # or m_ripemd160.so) be loaded and the password hashing module
- # (m_password_hash.so) loaded.
- # Options here are: "md5", "sha256" and "ripemd160", or one of
- # these prefixed with "hmac-", e.g.: "hmac-sha256".
+ # hash: the hash function this password is hashed with. Requires the
+ # module for the selected function (bcrypt, md5, sha1, sha256, or
+ # ripemd160) and the password hashing module (password_hash) to be
+ # loaded.
+ # You may also use any of the above other than bcrypt prefixed with
+ # either "hmac-" or "pbkdf2-hmac-" (requires the pbkdf2 module).
# Create hashed passwords with: /mkpasswd <hash> <password>
- hash="hmac-sha256"
+ hash="bcrypt"
# password: A hash of the password (see above option) hashed
- # with /mkpasswd <hash> <password>. See m_password_hash in modules.conf
- # for more information about password hashing.
+ # with /mkpasswd <hash> <password>. See the password_hash module
+ # in modules.conf for more information about password hashing.
password="qQmv3LcF$Qh63wzmtUqWp9OXnLwe7yv1GcBwHpq59k2a0UrY8xe0"
# host: What hostnames and IPs are allowed to use this operator account.
diff --git a/docs/conf/services/anope.conf.example b/docs/conf/services/anope.conf.example
new file mode 100644
index 000000000..603bb538d
--- /dev/null
+++ b/docs/conf/services/anope.conf.example
@@ -0,0 +1,9 @@
+# This file contains aliases and nickname reservations which are used
+# by Anope. See https://www.anope.org/ for more information on Anope.
+
+# This file inherits from the generic config to avoid repetition.
+<include file="examples/services/generic.conf.example">
+
+# /GLOBAL <message>
+# Sends a global notice.
+<alias text="GLOBAL" format="*" replace="PRIVMSG $requirement :GLOBAL $2-" requires="Global" uline="yes" operonly="yes">
diff --git a/docs/conf/services/atheme.conf.example b/docs/conf/services/atheme.conf.example
new file mode 100644
index 000000000..037087998
--- /dev/null
+++ b/docs/conf/services/atheme.conf.example
@@ -0,0 +1,52 @@
+# This file contains aliases and nickname reservations which are used
+# by Atheme. See http://atheme.net for more information on Atheme.
+
+# This file inherits from the generic config to avoid repetition.
+<include file="examples/services/generic.conf.example">
+
+# Long hand aliases for services pseudoclients.
+<alias text="ALIS" replace="PRIVMSG $requirement :$2-" requires="ALIS" uline="yes">
+<alias text="CHANFIX" replace="PRIVMSG $requirement :$2-" requires="ChanFix" uline="yes">
+<alias text="GAMESERV" replace="PRIVMSG $requirement :$2-" requires="GameServ" uline="yes">
+<alias text="GLOBAL" replace="PRIVMSG $requirement :$2-" requires="Global" uline="yes" operonly="yes">
+<alias text="GROUPSERV" replace="PRIVMSG $requirement :$2-" requires="GroupServ" uline="yes">
+<alias text="HELPSERV" replace="PRIVMSG $requirement :$2-" requires="HelpServ" uline="yes">
+<alias text="INFOSERV" replace="PRIVMSG $requirement :$2-" requires="InfoServ" uline="yes">
+<alias text="PROXYSCAN" replace="PRIVMSG $requirement :$2-" requires="Proxyscan" uline="yes" operonly="yes">
+<alias text="RPGSERV" replace="PRIVMSG $requirement :$2-" requires="RPGServ" uline="yes">
+
+# Short hand aliases for services pseudoclients.
+<alias text="CF" replace="PRIVMSG $requirement :$2-" requires="ChanFix" uline="yes">
+<alias text="GL" replace="PRIVMSG $requirement :$2-" requires="Global" uline="yes" operonly="yes">
+<alias text="GS" replace="PRIVMSG $requirement :$2-" requires="GroupServ" uline="yes">
+<alias text="IS" replace="PRIVMSG $requirement :$2-" requires="InfoServ" uline="yes">
+<alias text="LS" replace="PRIVMSG $requirement :$2-" requires="ALIS" uline="yes">
+<alias text="PS" replace="PRIVMSG $requirement :$2-" requires="Proxyscan" uline="yes" operonly="yes">
+<alias text="RS" replace="PRIVMSG $requirement :$2-" requires="RPGServ" uline="yes">
+
+# These short hand aliases conflict with other pseudoclients. You can enable
+# them but you will need to comment out the uncommented ones above first,
+#<alias text="GS" replace="PRIVMSG $requirement :$2-" requires="GameServ" uline="yes">
+#<alias text="HS" replace="PRIVMSG $requirement :$2-" requires="HelpServ" uline="yes">
+
+# Prevent clients from using the nicknames of services pseudoclients.
+<badnick nick="ALIS" reason="Reserved for a network service">
+<badnick nick="ChanFix" reason="Reserved for a network service">
+<badnick nick="GameServ" reason="Reserved for a network service">
+<badnick nick="GroupServ" reason="Reserved for a network service">
+<badnick nick="HelpServ" reason="Reserved for a network service">
+<badnick nick="InfoServ" reason="Reserved for a network service">
+<badnick nick="Proxyscan" reason="Reserved for a network service">
+<badnick nick="RPGServ" reason="Reserved for a network service">
+<badnick nick="SaslServ" reason="Reserved for a network service">
+
+# Exempt services pseudoclients from filters.
+<exemptfromfilter target="ALIS">
+<exemptfromfilter target="ChanFix">
+<exemptfromfilter target="GameServ">
+<exemptfromfilter target="GroupServ">
+<exemptfromfilter target="HelpServ">
+<exemptfromfilter target="InfoServ">
+<exemptfromfilter target="Proxyscan">
+<exemptfromfilter target="RPGServ">
+<exemptfromfilter target="SaslServ">
diff --git a/docs/conf/services/generic.conf.example b/docs/conf/services/generic.conf.example
new file mode 100644
index 000000000..6904d31d8
--- /dev/null
+++ b/docs/conf/services/generic.conf.example
@@ -0,0 +1,45 @@
+# This file contains aliases and nickname reservations which are used
+# by all common services implementations.
+
+# Long hand aliases for services pseudoclients.
+<alias text="BOTSERV" replace="PRIVMSG $requirement :$2-" requires="BotServ" uline="yes">
+<alias text="CHANSERV" replace="PRIVMSG $requirement :$2-" requires="ChanServ" uline="yes">
+<alias text="HOSTSERV" replace="PRIVMSG $requirement :$2-" requires="HostServ" uline="yes">
+<alias text="MEMOSERV" replace="PRIVMSG $requirement :$2-" requires="MemoServ" uline="yes">
+<alias text="NICKSERV" replace="PRIVMSG $requirement :$2-" requires="NickServ" uline="yes">
+<alias text="OPERSERV" replace="PRIVMSG $requirement :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="STATSERV" replace="PRIVMSG $requirement :$2-" requires="StatServ" uline="yes">
+
+# Short hand aliases for services pseudoclients.
+<alias text="BS" replace="PRIVMSG $requirement :$2-" requires="BotServ" uline="yes">
+<alias text="CS" replace="PRIVMSG $requirement :$2-" requires="ChanServ" uline="yes">
+<alias text="HS" replace="PRIVMSG $requirement :$2-" requires="HostServ" uline="yes">
+<alias text="MS" replace="PRIVMSG $requirement :$2-" requires="MemoServ" uline="yes">
+<alias text="NS" replace="PRIVMSG $requirement :$2-" requires="NickServ" uline="yes">
+<alias text="OS" replace="PRIVMSG $requirement :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="SS" replace="PRIVMSG $requirement :$2-" requires="StatServ" uline="yes">
+
+# /ID [account] <password>
+# Identifies to a services account.
+<alias text="ID" format="*" replace="PRIVMSG $requirement :IDENTIFY $2-" requires="NickServ" uline="yes">
+<alias text="IDENTIFY" format="*" replace="PRIVMSG $requirement :IDENTIFY $2-" requires="NickServ" uline="yes">
+
+# Prevent clients from using the nicknames of services pseudoclients.
+<badnick nick="BotServ" reason="Reserved for a network service">
+<badnick nick="ChanServ" reason="Reserved for a network service">
+<badnick nick="Global" reason="Reserved for a network service">
+<badnick nick="HostServ" reason="Reserved for a network service">
+<badnick nick="MemoServ" reason="Reserved for a network service">
+<badnick nick="NickServ" reason="Reserved for a network service">
+<badnick nick="OperServ" reason="Reserved for a network service">
+<badnick nick="StatServ" reason="Reserved for a network service">
+
+# Exempt services pseudoclients from filters.
+<exemptfromfilter target="BotServ">
+<exemptfromfilter target="ChanServ">
+<exemptfromfilter target="Global">
+<exemptfromfilter target="HostServ">
+<exemptfromfilter target="MemoServ">
+<exemptfromfilter target="NickServ">
+<exemptfromfilter target="OperServ">
+<exemptfromfilter target="StatServ">
diff --git a/extras/m_sqloper.mssql.sql b/extras/m_sqloper.mssql.sql
deleted file mode 100644
index 5056e12e9..000000000
--- a/extras/m_sqloper.mssql.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-CREATE TABLE [dbo].[ircd_opers] (
- [id] int IDENTITY(1, 1) NOT NULL,
- [username] varchar(255) NULL,
- [password] varchar(255) NULL,
- [hostname] varchar(255) NULL,
- [type] varchar(255) NULL,
- PRIMARY KEY CLUSTERED ([id])
-)
diff --git a/extras/m_sqloper.mysql.sql b/extras/m_sqloper.mysql.sql
index 293a2aa70..f43495806 100644
--- a/extras/m_sqloper.mysql.sql
+++ b/extras/m_sqloper.mysql.sql
@@ -1,24 +1,9 @@
--- MySQL dump 9.11
---
--- Host: localhost Database: brain
--- ------------------------------------------------------
--- Server version 4.0.20
-
---
--- Table structure for table `ircd_opers`
---
-
CREATE TABLE ircd_opers (
id bigint(20) NOT NULL auto_increment,
username text,
password text,
hostname text,
type text,
+ active tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (id)
-) TYPE=MyISAM;
-
---
--- Dumping data for table `ircd_opers`
---
-
-
+) ENGINE=MyISAM;
diff --git a/extras/m_sqloper.postgresql.sql b/extras/m_sqloper.postgresql.sql
index fd640949f..4244abc22 100644
--- a/extras/m_sqloper.postgresql.sql
+++ b/extras/m_sqloper.postgresql.sql
@@ -1,14 +1,10 @@
---
--- PostgreSQL database dump
---
-
CREATE TABLE ircd_opers (
id serial NOT NULL,
username text,
"password" text,
hostname text,
- "type" text
+ "type" text,
+ active boolean NOT NULL DEFAULT 1
);
ALTER TABLE ONLY ircd_opers
ADD CONSTRAINT ircd_opers_pkey PRIMARY KEY (id);
-
diff --git a/extras/m_sqloper.sqlite3.sql b/extras/m_sqloper.sqlite3.sql
index 1bb2937b8..1c607e664 100644
--- a/extras/m_sqloper.sqlite3.sql
+++ b/extras/m_sqloper.sqlite3.sql
@@ -3,5 +3,5 @@ id integer primary key,
username text,
password text,
hostname text,
-type text);
-
+type text,
+active integer NOT NULL DEFAULT 1);
diff --git a/src/modes/cmode_o.cpp b/include/aligned_storage.h
index 6e96afa67..7bf0fe0a3 100644
--- a/src/modes/cmode_o.cpp
+++ b/include/aligned_storage.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,34 @@
*/
-#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 insp
{
- prefix = '@';
- levelrequired = OP_VALUE;
- prefixrank = OP_VALUE;
+ template <typename T> class aligned_storage;
}
+
+template <typename T>
+class insp::aligned_storage
+{
+ mutable typename TR1NS::aligned_storage<sizeof(T), TR1NS::alignment_of<T>::value>::type data;
+
+ public:
+ aligned_storage()
+ {
+ }
+
+ aligned_storage(const aligned_storage& other)
+ {
+ }
+
+ T* operator->() const
+ {
+ return static_cast<T*>(static_cast<void*>(&data));
+ }
+
+ operator T*() const
+ {
+ return operator->();
+ }
+};
diff --git a/include/bancache.h b/include/bancache.h
index 7f51ca75e..6e19e1ebe 100644
--- a/include/bancache.h
+++ b/include/bancache.h
@@ -40,24 +40,20 @@ class CoreExport BanCacheHit
*/
time_t Expiry;
- BanCacheHit(const std::string &type, const std::string &reason, time_t seconds)
- : Type(type), Reason(reason), Expiry(ServerInstance->Time() + seconds)
- {
- }
+ BanCacheHit(const std::string& type, const std::string& reason, time_t seconds);
bool IsPositive() const { return (!Reason.empty()); }
};
-/* A container of ban cache items.
- * must be defined after class BanCacheHit.
- */
-typedef TR1NS::unordered_map<std::string, BanCacheHit*, TR1NS::hash<std::string> > BanCacheHash;
-
/** A manager for ban cache, which allocates and deallocates and checks cached bans.
*/
class CoreExport BanCacheManager
{
- BanCacheHash* BanHash;
+ /** A container of ban cache items.
+ */
+ typedef TR1NS::unordered_map<std::string, BanCacheHit*, TR1NS::hash<std::string> > BanCacheHash;
+
+ BanCacheHash BanHash;
bool RemoveIfExpired(BanCacheHash::iterator& it);
public:
@@ -77,9 +73,5 @@ class CoreExport BanCacheManager
*/
void RemoveEntries(const std::string& type, bool positive);
- BanCacheManager()
- {
- this->BanHash = new BanCacheHash();
- }
~BanCacheManager();
};
diff --git a/include/base.h b/include/base.h
index dcbb2e5c7..d8781f796 100644
--- a/include/base.h
+++ b/include/base.h
@@ -202,7 +202,7 @@ class CoreExport CoreException : public std::exception
* Actually no, it does nothing. Never mind.
* @throws Nothing!
*/
- virtual ~CoreException() throw() {};
+ virtual ~CoreException() throw() {}
/** Returns the reason for the exception.
* @return Human readable description of the error
*/
@@ -235,7 +235,9 @@ enum ServiceType {
/** is a data processing provider (MD5, SQL) */
SERVICE_DATA,
/** is an I/O hook provider (SSL) */
- SERVICE_IOHOOK
+ SERVICE_IOHOOK,
+ /** Service managed by a module */
+ SERVICE_CUSTOM
};
/** A structure defining something that a module can provide */
@@ -251,6 +253,10 @@ class CoreExport ServiceProvider : public classbase
ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type);
virtual ~ServiceProvider();
+ /** Register this service in the appropriate registrar
+ */
+ virtual void RegisterService();
+
/** If called, this ServiceProvider won't be registered automatically
*/
void DisableAutoRegister();
diff --git a/include/builtinmodes.h b/include/builtinmodes.h
index e78e68b11..a77734ae3 100644
--- a/include/builtinmodes.h
+++ b/include/builtinmodes.h
@@ -35,20 +35,11 @@ class ModeChannelBan : public ListModeBase
}
};
-/** Channel mode +i
- */
-class ModeChannelInviteOnly : public SimpleChannelModeHandler
-{
- public:
- ModeChannelInviteOnly() : SimpleChannelModeHandler(NULL, "inviteonly", 'i')
- {
- }
-};
-
/** Channel mode +k
*/
class ModeChannelKey : public ParamMode<ModeChannelKey, LocalStringExt>
{
+ static const std::string::size_type maxkeylen = 32;
public:
ModeChannelKey();
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
@@ -67,61 +58,15 @@ class ModeChannelLimit : public ParamMode<ModeChannelLimit, LocalIntExt>
ModeAction OnSet(User* source, Channel* channel, std::string& parameter);
};
-/** Channel mode +m
- */
-class ModeChannelModerated : public SimpleChannelModeHandler
-{
- public:
- ModeChannelModerated() : SimpleChannelModeHandler(NULL, "moderated", 'm')
- {
- }
-};
-
-/** Channel mode +n
- */
-class ModeChannelNoExternal : public SimpleChannelModeHandler
-{
- public:
- ModeChannelNoExternal() : SimpleChannelModeHandler(NULL, "noextmsg", 'n')
- {
- }
-};
-
/** Channel mode +o
*/
class ModeChannelOp : public PrefixMode
{
public:
- ModeChannelOp();
-};
-
-/** Channel mode +p
- */
-class ModeChannelPrivate : public SimpleChannelModeHandler
-{
- public:
- ModeChannelPrivate() : SimpleChannelModeHandler(NULL, "private", 'p')
- {
- }
-};
-
-/** Channel mode +s
- */
-class ModeChannelSecret : public SimpleChannelModeHandler
-{
- public:
- ModeChannelSecret() : SimpleChannelModeHandler(NULL, "secret", 's')
- {
- }
-};
-
-/** Channel mode +t
- */
-class ModeChannelTopicOps : public SimpleChannelModeHandler
-{
- public:
- ModeChannelTopicOps() : SimpleChannelModeHandler(NULL, "topiclock", 't')
+ ModeChannelOp()
+ : PrefixMode(NULL, "op", 'o', OP_VALUE, '@')
{
+ levelrequired = OP_VALUE;
}
};
@@ -130,16 +75,10 @@ class ModeChannelTopicOps : public SimpleChannelModeHandler
class ModeChannelVoice : public PrefixMode
{
public:
- ModeChannelVoice();
-};
-
-/** User mode +i
- */
-class ModeUserInvisible : public SimpleUserModeHandler
-{
- public:
- ModeUserInvisible() : SimpleUserModeHandler(NULL, "invisible", 'i')
+ ModeChannelVoice()
+ : PrefixMode(NULL, "voice", 'v', VOICE_VALUE, '+')
{
+ levelrequired = HALFOP_VALUE;
}
};
@@ -165,7 +104,7 @@ class ModeUserServerNoticeMask : public ModeHandler
* @param user The user whose notice masks to format
* @return The notice mask character sequence
*/
- std::string GetUserParameter(User* user);
+ std::string GetUserParameter(const User* user) const CXX11_OVERRIDE;
};
/** User mode +o
diff --git a/include/caller.h b/include/caller.h
index c3a29e8c2..47f896ef6 100644
--- a/include/caller.h
+++ b/include/caller.h
@@ -189,7 +189,7 @@ template <typename ReturnType, typename Param1, typename Param2, typename Param3
virtual ~HandlerBase8() { }
};
-template <typename HandlerType> class CoreExport caller
+template <typename HandlerType> class caller
{
public:
HandlerType* target;
@@ -201,118 +201,118 @@ template <typename HandlerType> class CoreExport caller
virtual ~caller() { }
};
-template <typename ReturnType> class CoreExport caller0 : public caller< HandlerBase0<ReturnType> >
+template <typename ReturnType> class caller0 : public caller< HandlerBase0<ReturnType> >
{
public:
caller0(HandlerBase0<ReturnType>* initial)
: caller< HandlerBase0<ReturnType> >::caller(initial)
{ }
- virtual ReturnType operator() ()
+ ReturnType operator() ()
{
return this->target->Call();
}
};
-template <typename ReturnType, typename Param1> class CoreExport caller1 : public caller< HandlerBase1<ReturnType, Param1> >
+template <typename ReturnType, typename Param1> class caller1 : public caller< HandlerBase1<ReturnType, Param1> >
{
public:
caller1(HandlerBase1<ReturnType, Param1>* initial)
: caller< HandlerBase1<ReturnType, Param1> >(initial)
{ }
- virtual ReturnType operator() (Param1 param1)
+ ReturnType operator() (Param1 param1)
{
return this->target->Call(param1);
}
};
-template <typename ReturnType, typename Param1, typename Param2> class CoreExport caller2 : public caller< HandlerBase2<ReturnType, Param1, Param2> >
+template <typename ReturnType, typename Param1, typename Param2> class caller2 : public caller< HandlerBase2<ReturnType, Param1, Param2> >
{
public:
caller2(HandlerBase2<ReturnType, Param1, Param2>* initial)
: caller< HandlerBase2<ReturnType, Param1, Param2> >(initial)
{ }
- virtual ReturnType operator() (Param1 param1, Param2 param2)
+ ReturnType operator() (Param1 param1, Param2 param2)
{
return this->target->Call(param1, param2);
}
};
-template <typename ReturnType, typename Param1, typename Param2, typename Param3> class CoreExport caller3 : public caller< HandlerBase3<ReturnType, Param1, Param2, Param3> >
+template <typename ReturnType, typename Param1, typename Param2, typename Param3> class caller3 : public caller< HandlerBase3<ReturnType, Param1, Param2, Param3> >
{
public:
caller3(HandlerBase3<ReturnType, Param1, Param2, Param3>* initial)
: caller< HandlerBase3<ReturnType, Param1, Param2, Param3> >(initial)
{ }
- virtual ReturnType operator() (Param1 param1, Param2 param2, Param3 param3)
+ ReturnType operator() (Param1 param1, Param2 param2, Param3 param3)
{
return this->target->Call(param1, param2, param3);
}
};
-template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4> class CoreExport caller4 : public caller< HandlerBase4<ReturnType, Param1, Param2, Param3, Param4> >
+template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4> class caller4 : public caller< HandlerBase4<ReturnType, Param1, Param2, Param3, Param4> >
{
public:
caller4(HandlerBase4<ReturnType, Param1, Param2, Param3, Param4>* initial)
: caller< HandlerBase4<ReturnType, Param1, Param2, Param3, Param4> >(initial)
{ }
- virtual ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4)
+ ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4)
{
return this->target->Call(param1, param2, param3, param4);
}
};
-template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5> class CoreExport caller5 : public caller< HandlerBase5<ReturnType, Param1, Param2, Param3, Param4, Param5> >
+template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5> class caller5 : public caller< HandlerBase5<ReturnType, Param1, Param2, Param3, Param4, Param5> >
{
public:
caller5(HandlerBase5<ReturnType, Param1, Param2, Param3, Param4, Param5>* initial)
: caller< HandlerBase5<ReturnType, Param1, Param2, Param3, Param4, Param5> >(initial)
{ }
- virtual ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5)
+ ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5)
{
return this->target->Call(param1, param2, param3, param4, param5);
}
};
-template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6> class CoreExport caller6 : public caller< HandlerBase6<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6> >
+template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6> class caller6 : public caller< HandlerBase6<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6> >
{
public:
caller6(HandlerBase6<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6>* initial)
: caller< HandlerBase6<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6> >(initial)
{ }
- virtual ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5, Param6 param6)
+ ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5, Param6 param6)
{
return this->target->Call(param1, param2, param3, param4, param5, param6);
}
};
-template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6, typename Param7> class CoreExport caller7 : public caller< HandlerBase7<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7> >
+template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6, typename Param7> class caller7 : public caller< HandlerBase7<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7> >
{
public:
caller7(HandlerBase7<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7>* initial)
: caller< HandlerBase7<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7> >(initial)
{ }
- virtual ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5, Param6 param6, Param7 param7)
+ ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5, Param6 param6, Param7 param7)
{
return this->target->Call(param1, param2, param3, param4, param5, param6, param7);
}
};
-template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6, typename Param7, typename Param8> class CoreExport caller8 : public caller< HandlerBase8<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8> >
+template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6, typename Param7, typename Param8> class caller8 : public caller< HandlerBase8<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8> >
{
public:
caller8(HandlerBase8<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8>* initial)
: caller< HandlerBase8<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8> >(initial)
{ }
- virtual ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5, Param6 param6, Param7 param7, Param8 param8)
+ ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5, Param6 param6, Param7 param7, Param8 param8)
{
return this->target->Call(param1, param2, param3, param4, param5, param6, param7, param8);
}
diff --git a/include/channels.h b/include/channels.h
index 736ca2e98..be872b7fe 100644
--- a/include/channels.h
+++ b/include/channels.h
@@ -34,8 +34,14 @@
* This class represents a channel, and contains its name, modes, topic, topic set time,
* etc, and an instance of the BanList type.
*/
-class CoreExport Channel : public Extensible, public InviteBase<Channel>
+class CoreExport Channel : public Extensible
{
+ public:
+ /** A map of Memberships on a channel keyed by User pointers
+ */
+ typedef std::map<User*, insp::aligned_storage<Membership> > MemberMap;
+
+ private:
/** Set default modes for the channel on creation
*/
void SetDefaultModes();
@@ -51,9 +57,9 @@ class CoreExport Channel : public Extensible, public InviteBase<Channel>
* This function does not remove the channel from User::chanlist.
* Since the parameter is an iterator to the target, the complexity
* of this function is constant.
- * @param membiter The UserMembIter to remove, must be valid
+ * @param membiter The MemberMap iterator to remove, must be valid
*/
- void DelUser(const UserMembIter& membiter);
+ void DelUser(const MemberMap::iterator& membiter);
public:
/** Creates a channel record and initialises it with default values
@@ -84,7 +90,7 @@ class CoreExport Channel : public Extensible, public InviteBase<Channel>
/** User list.
*/
- UserMembList userlist;
+ MemberMap userlist;
/** Channel topic.
* If this is an empty string, no channel topic is set.
@@ -132,8 +138,11 @@ class CoreExport Channel : public Extensible, public InviteBase<Channel>
/** Sets the channel topic.
* @param user The user setting the topic.
* @param topic The topic to set it to.
+ * @param topicts Timestamp of the new topic.
+ * @param setter Setter string, may be used when the original setter is no longer online.
+ * If omitted or NULL, the setter string is obtained from the user.
*/
- void SetTopic(User* user, const std::string& topic);
+ void SetTopic(User* user, const std::string& topic, time_t topicts, const std::string* setter = NULL);
/** Obtain the channel "user counter"
* This returns the number of users on this channel
@@ -166,7 +175,7 @@ class CoreExport Channel : public Extensible, public InviteBase<Channel>
*
* @return This function returns pointer to a map of User pointers (CUList*).
*/
- const UserMembList* GetUsers() const { return &userlist; }
+ const MemberMap& GetUsers() const { return userlist; }
/** Returns true if the user given is on the given channel.
* @param user The user to look for
@@ -178,18 +187,30 @@ class CoreExport Channel : public Extensible, public InviteBase<Channel>
/** Make src kick user from this channel with the given reason.
* @param src The source of the kick
- * @param user The user being kicked (must be on this channel)
+ * @param victimiter Iterator to the user being kicked, must be valid
+ * @param reason The reason for the kick
+ */
+ void KickUser(User* src, const MemberMap::iterator& victimiter, const std::string& reason);
+
+ /** Make src kick user from this channel with the given reason.
+ * @param src The source of the kick
+ * @param user The user being kicked
* @param reason The reason for the kick
- * @param srcmemb The membership of the user who does the kick, can be NULL
*/
- void KickUser(User* src, User* user, const std::string& reason, Membership* srcmemb = NULL);
+ void KickUser(User* src, User* user, const std::string& reason)
+ {
+ MemberMap::iterator it = userlist.find(user);
+ if (it != userlist.end())
+ KickUser(src, it, reason);
+ }
/** Part a user from this channel with the given reason.
* If the reason field is NULL, no reason will be sent.
* @param user The user who is parting (must be on this channel)
* @param reason The part reason
+ * @return True if the user was on the channel and left, false if they weren't and nothing happened
*/
- void PartUser(User *user, std::string &reason);
+ bool PartUser(User* user, std::string& reason);
/** Join a local user to a channel, with or without permission checks. May be a channel that doesn't exist yet.
* @param user The user to join to the channel.
@@ -207,8 +228,9 @@ class CoreExport Channel : public Extensible, public InviteBase<Channel>
* @param privs Priviliges (prefix mode letters) to give to this user, may be NULL
* @param bursting True if this join is the result of a netburst (passed to modules in the OnUserJoin hook)
* @param created_by_local True if this channel was just created by a local user (passed to modules in the OnUserJoin hook)
+ * @return A newly created Membership object, or NULL if the user was already inside the channel or if the user is a server user
*/
- void ForceJoin(User* user, const std::string* privs = NULL, bool bursting = false, bool created_by_local = false);
+ Membership* ForceJoin(User* user, const std::string* privs = NULL, bool bursting = false, bool created_by_local = false);
/** Write to a channel, from a user, using va_args for text
* @param user User whos details to prefix the line with
@@ -287,12 +309,6 @@ class CoreExport Channel : public Extensible, public InviteBase<Channel>
*/
const char* ChanModes(bool showkey);
- /** Spool the NAMES list for this channel to the given user
- * @param user The user to spool the NAMES list to
- * @param isinside If true, the user is inside the channel, if not then false
- */
- void UserList(User* user, bool isinside = true);
-
/** Get the value of a users prefix on this channel.
* @param user The user to look up
* @return The module or core-defined value of the users prefix.
@@ -319,6 +335,11 @@ class CoreExport Channel : public Extensible, public InviteBase<Channel>
/** Get the status of an "action" type extban
*/
ModResult GetExtBanStatus(User *u, char type);
+
+ /** Write a NOTICE to all local users on the channel
+ * @param text Text to send
+ */
+ void WriteNotice(const std::string& text);
};
inline bool Channel::HasUser(User* user)
diff --git a/include/command_parse.h b/include/command_parse.h
index 70544b0c8..c3d67af23 100644
--- a/include/command_parse.h
+++ b/include/command_parse.h
@@ -29,6 +29,9 @@
*/
class CoreExport CommandParser
{
+ public:
+ typedef TR1NS::unordered_map<std::string, Command*> CommandMap;
+
private:
/** Process a command from a user.
* @param user The user to parse the command for
@@ -36,15 +39,20 @@ class CoreExport CommandParser
*/
void ProcessCommand(LocalUser* user, std::string& cmd);
- public:
/** Command list, a hash_map of command names to Command*
*/
- Commandtable cmdlist;
+ CommandMap cmdlist;
+ public:
/** Default constructor.
*/
CommandParser();
+ /** Get a command name -> Command* map containing all client to server commands
+ * @return A map of command handlers keyed by command names
+ */
+ const CommandMap& GetCommands() const { return cmdlist; }
+
/** Calls the handler for a given command.
* @param commandname The command to find. This should be in uppercase.
* @param parameters Parameter list
@@ -77,7 +85,7 @@ class CoreExport CommandParser
* With one list it is much simpler, and is used in NAMES, WHOIS, PRIVMSG etc.
*
* If there is only one list and there are duplicates in it, then the command handler is only called for
- * unique items. Entries are compared using "irc comparision" (see irc::string).
+ * unique items. Entries are compared using "irc comparison".
* If the usemax parameter is true (the default) the function only parses until it reaches
* ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
*
diff --git a/include/commands/cmd_whowas.h b/include/commands/cmd_whowas.h
index 0a38b44f1..070858cc5 100644
--- a/include/commands/cmd_whowas.h
+++ b/include/commands/cmd_whowas.h
@@ -23,20 +23,167 @@
#include "modules.h"
-/* Forward ref for typedefs */
-class WhoWasGroup;
+namespace WhoWas
+{
+ /** One entry for a nick. There may be multiple entries for a nick.
+ */
+ struct Entry
+ {
+ /** Real host
+ */
+ const std::string host;
-/** A group of users related by nickname
- */
-typedef std::deque<WhoWasGroup*> whowas_set;
+ /** Displayed host
+ */
+ const std::string dhost;
-/** Sets of users in the whowas system
- */
-typedef std::map<irc::string,whowas_set*> whowas_users;
+ /** Ident
+ */
+ const std::string ident;
-/** Sets of time and users in whowas list
- */
-typedef std::deque<std::pair<time_t,irc::string> > whowas_users_fifo;
+ /** Server name
+ */
+ const std::string server;
+
+ /** Full name (GECOS)
+ */
+ const std::string gecos;
+
+ /** Signon time
+ */
+ const time_t signon;
+
+ /** Initialize this Entry with a user
+ */
+ Entry(User* user);
+ };
+
+ /** Everything known about one nick
+ */
+ struct Nick : public insp::intrusive_list_node<Nick>
+ {
+ /** A group of users related by nickname
+ */
+ typedef std::deque<Entry*> List;
+
+ /** Container where each element has information about one occurrence of this nick
+ */
+ List entries;
+
+ /** Time this nick was added to the database
+ */
+ const time_t addtime;
+
+ /** Nickname whose information is stored in this class
+ */
+ const std::string nick;
+
+ /** Constructor to initialize fields
+ */
+ Nick(const std::string& nickname);
+
+ /** Destructor, deallocates all elements in the entries container
+ */
+ ~Nick();
+ };
+
+ class Manager
+ {
+ public:
+ struct Stats
+ {
+ /** Number of currently existing WhoWas::Entry objects
+ */
+ size_t entrycount;
+ };
+
+ /** Add a user to the whowas database. Called when a user quits.
+ * @param user The user to add to the database
+ */
+ void Add(User* user);
+
+ /** Retrieves statistics about the whowas database
+ * @return Whowas statistics as a WhoWas::Manager::Stats struct
+ */
+ Stats GetStats() const;
+
+ /** Expires old entries
+ */
+ void Maintain();
+
+ /** Updates the current configuration which may result in the database being pruned if the
+ * new values are lower than the current ones.
+ * @param NewGroupSize Maximum number of nicks allowed in the database. In case there are this many nicks
+ * in the database and one more is added, the oldest one is removed (FIFO).
+ * @param NewMaxGroups Maximum number of entries per nick
+ * @param NewMaxKeep Seconds how long each nick should be kept
+ */
+ void UpdateConfig(unsigned int NewGroupSize, unsigned int NewMaxGroups, unsigned int NewMaxKeep);
+
+ /** Retrieves all data known about a given nick
+ * @param nick Nickname to find, case insensitive (IRC casemapping)
+ * @return A pointer to a WhoWas::Nick if the nick was found, NULL otherwise
+ */
+ const Nick* FindNick(const std::string& nick) const;
+
+ /** Returns true if WHOWAS is enabled according to the current configuration
+ * @return True if WHOWAS is enabled according to the configuration, false if WHOWAS is disabled
+ */
+ bool IsEnabled() const;
+
+ /** Constructor
+ */
+ Manager();
+
+ /** Destructor
+ */
+ ~Manager();
+
+ private:
+ /** Order in which the users were added into the map, used to remove oldest nick
+ */
+ typedef insp::intrusive_list_tail<Nick> FIFO;
+
+ /** Sets of users in the whowas system
+ */
+ typedef TR1NS::unordered_map<std::string, WhoWas::Nick*, irc::insensitive, irc::StrHashComp> whowas_users;
+
+ /** Primary container, links nicknames tracked by WHOWAS to a list of records
+ */
+ whowas_users whowas;
+
+ /** List of nicknames in the order they were inserted into the map
+ */
+ FIFO whowas_fifo;
+
+ /** Max number of WhoWas entries per user.
+ */
+ unsigned int GroupSize;
+
+ /** Max number of cumulative user-entries in WhoWas.
+ * When max reached and added to, push out oldest entry FIFO style.
+ */
+ unsigned int MaxGroups;
+
+ /** Max seconds a user is kept in WhoWas before being pruned.
+ */
+ unsigned int MaxKeep;
+
+ /** Shrink all data structures to honor the current settings
+ */
+ void Prune();
+
+ /** Remove a nick (and all entries belonging to it) from the database
+ * @param it Iterator to the nick to purge
+ */
+ void PurgeNick(whowas_users::iterator it);
+
+ /** Remove a nick (and all entries belonging to it) from the database
+ * @param nick Nick to purge
+ */
+ void PurgeNick(WhoWas::Nick* nick);
+ };
+}
/** Handle /WHOWAS. These command handlers can be reloaded by the core,
* and handle basic RFC1459 commands. Commands within modules work
@@ -45,69 +192,16 @@ typedef std::deque<std::pair<time_t,irc::string> > whowas_users_fifo;
*/
class CommandWhowas : public Command
{
- private:
- /** Primary container, links nicknames tracked by WHOWAS to a list of records
- */
- whowas_users whowas;
-
- /** List of nicknames in the order they were inserted into the map
- */
- whowas_users_fifo whowas_fifo;
-
public:
- /** Max number of WhoWas entries per user.
+ /** Manager handling all whowas database related tasks
*/
- unsigned int GroupSize;
-
- /** Max number of cumulative user-entries in WhoWas.
- * When max reached and added to, push out oldest entry FIFO style.
- */
- unsigned int MaxGroups;
-
- /** Max seconds a user is kept in WhoWas before being pruned.
- */
- unsigned int MaxKeep;
+ WhoWas::Manager manager;
CommandWhowas(Module* parent);
/** Handle command.
* @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh 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);
- void AddToWhoWas(User* user);
- std::string GetStats();
- void Prune();
- void Maintain();
- ~CommandWhowas();
-};
-
-/** Used to hold WHOWAS information
- */
-class WhoWasGroup
-{
- public:
- /** Real host
- */
- std::string host;
- /** Displayed host
- */
- std::string dhost;
- /** Ident
- */
- std::string ident;
- /** Server name
- */
- std::string server;
- /** Fullname (GECOS)
- */
- std::string gecos;
- /** Signon time
- */
- time_t signon;
-
- /** Initialize this WhoWasFroup with a user
- */
- WhoWasGroup(User* user);
};
diff --git a/include/compat.h b/include/compat.h
index 9302c573f..1e6fc3d45 100644
--- a/include/compat.h
+++ b/include/compat.h
@@ -27,10 +27,14 @@
*/
#if defined _LIBCPP_VERSION || defined _WIN32
# define TR1NS std
+# include <array>
# include <unordered_map>
+# include <type_traits>
#else
# define TR1NS std::tr1
+# include <tr1/array>
# include <tr1/unordered_map>
+# include <tr1/type_traits>
#endif
/**
diff --git a/include/configparser.h b/include/configparser.h
index f46d143ae..02619e759 100644
--- a/include/configparser.h
+++ b/include/configparser.h
@@ -41,7 +41,7 @@ enum ParseFlags
struct ParseStack
{
std::vector<std::string> reading;
- std::map<std::string, std::string> vars;
+ insp::flat_map<std::string, std::string> vars;
ConfigDataHash& output;
ConfigFileCache& FilesOutput;
std::stringstream& errstr;
diff --git a/include/configreader.h b/include/configreader.h
index f3b1f8b74..005d4a37d 100644
--- a/include/configreader.h
+++ b/include/configreader.h
@@ -117,13 +117,13 @@ class ServerLimits
/** Maximum hostname length */
size_t MaxHost;
- /** Creating the class initialises it to the defaults
- * as in 1.1's ./configure script. Reading other values
- * from the config will change these values.
+ /** Read all limits from a config tag. Limits which aren't specified in the tag are set to a default value.
+ * @param tag Configuration tag to read the limits from
*/
- ServerLimits() : NickMax(31), ChanMax(64), MaxModes(20), IdentMax(12),
- MaxQuit(255), MaxTopic(307), MaxKick(255), MaxGecos(128), MaxAway(200),
- MaxLine(512), MaxHost(64) { }
+ ServerLimits(ConfigTag* tag);
+
+ /** Maximum length of a n!u@h mask */
+ size_t GetMaxMask() const { return NickMax + 1 + IdentMax + 1 + MaxHost; }
};
struct CommandLineConf
@@ -165,8 +165,9 @@ struct CommandLineConf
class CoreExport OperInfo : public refcountbase
{
public:
- std::set<std::string> AllowedOperCommands;
- std::set<std::string> AllowedPrivs;
+ typedef insp::flat_set<std::string> PrivSet;
+ PrivSet AllowedOperCommands;
+ PrivSet AllowedPrivs;
/** Allowed user modes from oper classes. */
std::bitset<64> AllowedUserModes;
@@ -227,6 +228,14 @@ class CoreExport ServerConfig
std::string PrependModule(const std::string& fn) const { return FileSystem::ExpandPath(Module, fn); }
};
+ /** Holds a complete list of all connect blocks
+ */
+ typedef std::vector<reference<ConnectClass> > ClassVector;
+
+ /** Index of valid oper blocks and types
+ */
+ typedef insp::flat_map<std::string, reference<OperInfo> > OperIndex;
+
/** Get a configuration tag
* @param tag The name of the tag to get
*/
@@ -234,6 +243,9 @@ class CoreExport ServerConfig
ConfigTagList ConfTags(const std::string& tag);
+ /** An empty configuration tag. */
+ ConfigTag* EmptyTag;
+
/** Error stream, contains error output from any failed configuration parsing.
*/
std::stringstream errstr;
@@ -328,12 +340,6 @@ class CoreExport ServerConfig
*/
bool RestrictBannedUsers;
- /** If this is set to true, then mode lists (e.g
- * MODE \#chan b) are hidden from unprivileged
- * users.
- */
- bool HideModeLists[256];
-
/** The number of seconds the DNS subsystem
* will wait before timing out any request.
*/
@@ -397,6 +403,10 @@ class CoreExport ServerConfig
*/
std::string HideKillsServer;
+ /** Set to hide kills from clients of ulined servers in snotices.
+ */
+ bool HideULineKills;
+
/** The full pathname and filename of the PID
* file as defined in the configuration.
*/
@@ -428,11 +438,6 @@ class CoreExport ServerConfig
*/
bool CycleHostsFromUser;
- /** If set to true, prefixed channel NOTICEs and PRIVMSGs will have the prefix
- * added to the outgoing text for undernet style msg prefixing.
- */
- bool UndernetMsgPrefix;
-
/** If set to true, the full nick!user\@host will be shown in the TOPIC command
* for who set the topic last. If false, only the nick is shown.
*/
@@ -446,11 +451,11 @@ class CoreExport ServerConfig
*/
OperIndex OperTypes;
- /** Default value for <connect:maxchans>, deprecated in 2.2
+ /** Default value for <connect:maxchans>, deprecated in 3.0
*/
unsigned int MaxChans;
- /** Default value for <oper:maxchans>, deprecated in 2.2
+ /** Default value for <oper:maxchans>, deprecated in 3.0
*/
unsigned int OperMaxChans;
@@ -465,6 +470,8 @@ class CoreExport ServerConfig
*/
ServerConfig();
+ ~ServerConfig();
+
/** Get server ID as string with required leading zeroes
*/
const std::string& GetSID() const { return sid; }
@@ -490,10 +497,6 @@ class CoreExport ServerConfig
*/
static std::string Escape(const std::string& str, bool xml = true);
- /** If this value is true, invites will bypass more than just +i
- */
- bool InvBypassModes;
-
/** If this value is true, snotices will not stack when repeats are sent
*/
bool NoSnoticeStack;
diff --git a/include/consolecolors.h b/include/consolecolors.h
index 9b7e0670a..d92be58bc 100644
--- a/include/consolecolors.h
+++ b/include/consolecolors.h
@@ -29,70 +29,70 @@ extern HANDLE g_hStdout;
inline std::ostream& con_green(std::ostream &s)
{
- SetConsoleTextAttribute(g_hStdout, FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor);
- return s;
+ SetConsoleTextAttribute(g_hStdout, FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor);
+ return s;
}
inline std::ostream& con_red(std::ostream &s)
{
- SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_INTENSITY|g_wBackgroundColor);
- return s;
+ SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_INTENSITY|g_wBackgroundColor);
+ return s;
}
inline std::ostream& con_white(std::ostream &s)
{
- SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|g_wBackgroundColor);
- return s;
+ SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|g_wBackgroundColor);
+ return s;
}
inline std::ostream& con_white_bright(std::ostream &s)
{
- SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor);
- return s;
+ SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor);
+ return s;
}
inline std::ostream& con_bright(std::ostream &s)
{
- SetConsoleTextAttribute(g_hStdout, FOREGROUND_INTENSITY|g_wBackgroundColor);
- return s;
+ SetConsoleTextAttribute(g_hStdout, FOREGROUND_INTENSITY|g_wBackgroundColor);
+ return s;
}
inline std::ostream& con_reset(std::ostream &s)
{
- SetConsoleTextAttribute(g_hStdout, g_wOriginalColors);
- return s;
+ SetConsoleTextAttribute(g_hStdout, g_wOriginalColors);
+ return s;
}
#else
inline std::ostream& con_green(std::ostream &s)
{
- return s << "\033[1;32m";
+ return s << "\033[1;32m";
}
inline std::ostream& con_red(std::ostream &s)
{
- return s << "\033[1;31m";
+ return s << "\033[1;31m";
}
inline std::ostream& con_white(std::ostream &s)
{
- return s << "\033[0m";
+ return s << "\033[0m";
}
inline std::ostream& con_white_bright(std::ostream &s)
{
- return s << "\033[1m";
+ return s << "\033[1m";
}
inline std::ostream& con_bright(std::ostream &s)
{
- return s << "\033[1m";
+ return s << "\033[1m";
}
inline std::ostream& con_reset(std::ostream &s)
{
- return s << "\033[0m";
+ return s << "\033[0m";
}
#endif
diff --git a/include/convto.h b/include/convto.h
new file mode 100644
index 000000000..eaf14f6dc
--- /dev/null
+++ b/include/convto.h
@@ -0,0 +1,110 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2006 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/>.
+ */
+
+
+#pragma once
+
+/** Template function to convert any input type to std::string
+ */
+template<typename T> inline std::string ConvNumeric(const T& in)
+{
+ if (in == 0)
+ return "0";
+ T quotient = in;
+ std::string out;
+ while (quotient)
+ {
+ out += "0123456789"[std::abs((long)quotient % 10)];
+ quotient /= 10;
+ }
+ if (in < 0)
+ out += '-';
+ std::reverse(out.begin(), out.end());
+ return out;
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const int in)
+{
+ return ConvNumeric(in);
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const long in)
+{
+ return ConvNumeric(in);
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const char* in)
+{
+ return in;
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(const bool in)
+{
+ return (in ? "1" : "0");
+}
+
+/** Template function to convert any input type to std::string
+ */
+inline std::string ConvToStr(char in)
+{
+ return std::string(1, in);
+}
+
+inline const std::string& ConvToStr(const std::string& in)
+{
+ return in;
+}
+
+/** Template function to convert any input type to std::string
+ */
+template <class T> inline std::string ConvToStr(const T& in)
+{
+ std::stringstream tmp;
+ if (!(tmp << in))
+ return std::string();
+ return tmp.str();
+}
+
+/** Template function to convert any input type to any other type
+ * (usually an integer or numeric type)
+ */
+template<typename T> inline long ConvToInt(const T& in)
+{
+ std::stringstream tmp;
+ if (!(tmp << in))
+ return 0;
+ return atol(tmp.str().c_str());
+}
+
+inline uint64_t ConvToUInt64(const std::string& in)
+{
+ uint64_t ret;
+ std::istringstream tmp(in);
+ if (!(tmp >> ret))
+ return 0;
+ return ret;
+}
diff --git a/include/ctables.h b/include/ctables.h
index a69f5c86f..bc4226ea9 100644
--- a/include/ctables.h
+++ b/include/ctables.h
@@ -163,36 +163,16 @@ class CoreExport CommandBase : public ServiceProvider
* @param maxpara Maximum number of parameters this command may have - extra parameters
* will be tossed into one last space-seperated param.
*/
- CommandBase(Module* me, const std::string &cmd, int minpara = 0, int maxpara = 0) :
- ServiceProvider(me, 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(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0);
- virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- return ROUTE_LOCALONLY;
- }
+ virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
/** Encode a parameter for server->server transmission.
* Used for parameters for which the translation type is TR_CUSTOM.
* @param parameter The parameter to encode. Can be modified in place.
* @param index The parameter index (0 == first parameter).
*/
- virtual void EncodeParameter(std::string& parameter, int index)
- {
- }
-
- /** Decode a parameter from server->server transmission.
- * Not currently used in this version of InspIRCd.
- * Used for parameters for which the translation type is TR_CUSTOM.
- * @param parameter The parameter to decode. Can be modified in place.
- * @param index The parameter index (0 == first parameter).
- */
- virtual void DecodeParameter(std::string& parameter, int index)
- {
- }
+ virtual void EncodeParameter(std::string& parameter, int index);
/** Disable or enable this command.
* @param setting True to disable the command.
@@ -229,11 +209,7 @@ class CoreExport Command : public CommandBase
*/
bool force_manual_route;
- Command(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0)
- : CommandBase(me, cmd, minpara, maxpara)
- , force_manual_route(false)
- {
- }
+ Command(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0);
/** Handle the command from a user.
* @param parameters The parameters for the command.
@@ -242,6 +218,10 @@ class CoreExport Command : public CommandBase
*/
virtual CmdResult Handle(const std::vector<std::string>& parameters, User* user) = 0;
+ /** Register this object in the CommandParser
+ */
+ void RegisterService() CXX11_OVERRIDE;
+
/** Destructor
* Removes this command from the command parser
*/
diff --git a/include/dynref.h b/include/dynref.h
index 02474b67e..6e2e17423 100644
--- a/include/dynref.h
+++ b/include/dynref.h
@@ -22,10 +22,20 @@
#include "base.h"
-class CoreExport dynamic_reference_base : public interfacebase, public intrusive_list_node<dynamic_reference_base>
+class CoreExport dynamic_reference_base : public interfacebase, public insp::intrusive_list_node<dynamic_reference_base>
{
+ public:
+ class CaptureHook
+ {
+ public:
+ /** Called when the target of the dynamic_reference has been acquired
+ */
+ virtual void OnCapture() = 0;
+ };
+
private:
std::string name;
+ CaptureHook* hook;
void resolve();
protected:
ServiceProvider* value;
@@ -35,6 +45,12 @@ class CoreExport dynamic_reference_base : public interfacebase, public intrusive
~dynamic_reference_base();
inline const std::string& GetProvider() { return name; }
void SetProvider(const std::string& newname);
+
+ /** Set handler to call when the target object becomes available
+ * @param h Handler to call
+ */
+ void SetCaptureHook(CaptureHook* h) { hook = h; }
+
void check();
operator bool() { return (value != NULL); }
static void reset_all();
@@ -63,6 +79,16 @@ class dynamic_reference : public dynamic_reference_base
{
return operator->();
}
+
+ const T* operator->() const
+ {
+ return static_cast<T*>(value);
+ }
+
+ const T* operator*() const
+ {
+ return operator->();
+ }
};
template<typename T>
@@ -81,6 +107,16 @@ class dynamic_reference_nocheck : public dynamic_reference_base
{
return operator->();
}
+
+ const T* operator->() const
+ {
+ return static_cast<T*>(value);
+ }
+
+ const T* operator*() const
+ {
+ return operator->();
+ }
};
class ModeHandler;
diff --git a/include/event.h b/include/event.h
new file mode 100644
index 000000000..c9bad7d04
--- /dev/null
+++ b/include/event.h
@@ -0,0 +1,146 @@
+/*
+ * 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
+
+namespace Events
+{
+ class ModuleEventListener;
+ class ModuleEventProvider;
+}
+
+/** Provider of one or more cross-module events.
+ * Modules who wish to provide events for other modules create instances of this class and use
+ * one of the macros below to fire the event, passing the instance of the event provider class
+ * to the macro.
+ * Event providers are identified using a unique identifier string.
+ */
+class Events::ModuleEventProvider : public ServiceProvider, private dynamic_reference_base::CaptureHook
+{
+ public:
+ typedef std::vector<ModuleEventListener*> SubscriberList;
+
+ /** Constructor
+ * @param mod Module providing the event(s)
+ * @param eventid Identifier of the event or event group provided, must be unique
+ */
+ ModuleEventProvider(Module* mod, const std::string& eventid)
+ : ServiceProvider(mod, eventid, SERVICE_DATA)
+ , prov(mod, eventid)
+ {
+ prov.SetCaptureHook(this);
+ }
+
+ /** Get list of objects subscribed to this event
+ * @return List of subscribed objects
+ */
+ const SubscriberList& GetSubscribers() const { return prov->subscribers; }
+
+ friend class ModuleEventListener;
+
+ private:
+ void OnCapture() CXX11_OVERRIDE
+ {
+ // If someone else holds the list from now on, clear mine. See below for more info.
+ if (*prov != this)
+ subscribers.clear();
+ }
+
+ /** Reference to the active provider for this event. In case multiple event providers
+ * exist for the same event, only one of them contains the list of subscribers.
+ * To handle the case when we are not the ones with the list, we get it from the provider
+ * where the dynref points to.
+ */
+ dynamic_reference_nocheck<ModuleEventProvider> prov;
+
+ /** List of objects subscribed to the event(s) provided by us, or empty if multiple providers
+ * exist with the same name and we are not the ones holding the list.
+ */
+ SubscriberList subscribers;
+};
+
+/** Base class for abstract classes describing cross-module events.
+ * Subscribers should NOT inherit directly from this class.
+ */
+class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook
+{
+ /** Reference to the provider, can be NULL if none of the provider modules are loaded
+ */
+ dynamic_reference_nocheck<ModuleEventProvider> prov;
+
+ /** Called by the dynref when the event provider becomes available
+ */
+ void OnCapture() CXX11_OVERRIDE
+ {
+ prov->subscribers.push_back(this);
+ }
+
+ public:
+ /** Constructor
+ * @param mod Module subscribing
+ * @param eventid Identifier of the event to subscribe to
+ */
+ ModuleEventListener(Module* mod, const std::string& eventid)
+ : prov(mod, eventid)
+ {
+ prov.SetCaptureHook(this);
+ // If the dynamic_reference resolved at construction our capture handler wasn't called
+ if (prov)
+ ModuleEventListener::OnCapture();
+ }
+
+ ~ModuleEventListener()
+ {
+ if (prov)
+ stdalgo::erase(prov->subscribers, this);
+ }
+};
+
+/**
+ * Run the given hook provided by a module
+ *
+ * FOREACH_MOD_CUSTOM(accountevprov, AccountEventListener, OnAccountChange, MOD_RESULT, (user, newaccount))
+ */
+#define FOREACH_MOD_CUSTOM(prov, listenerclass, func, params) do { \
+ const Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
+ for (Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
+ { \
+ listenerclass* _t = static_cast<listenerclass*>(*_i); \
+ _t->func params ; \
+ } \
+} while (0);
+
+/**
+ * Run the given hook provided by a module until some module returns MOD_RES_ALLOW or MOD_RES_DENY.
+ * If no module does that, result is set to MOD_RES_PASSTHRU.
+ *
+ * Example: ModResult MOD_RESULT;
+ * FIRST_MOD_RESULT_CUSTOM(httpevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (request));
+ */
+#define FIRST_MOD_RESULT_CUSTOM(prov, listenerclass, func, result, params) do { \
+ result = MOD_RES_PASSTHRU; \
+ const Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
+ for (Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
+ { \
+ listenerclass* _t = static_cast<listenerclass*>(*_i); \
+ result = _t->func params ; \
+ if (result != MOD_RES_PASSTHRU) \
+ break; \
+ } \
+} while (0);
diff --git a/include/extensible.h b/include/extensible.h
index 87fe65ccb..07756fb59 100644
--- a/include/extensible.h
+++ b/include/extensible.h
@@ -19,8 +19,6 @@
#pragma once
-#include <stdint.h>
-
enum SerializeFormat
{
/** Shown to a human (does not need to be unserializable) */
@@ -38,7 +36,20 @@ enum SerializeFormat
class CoreExport ExtensionItem : public ServiceProvider, public usecountbase
{
public:
- ExtensionItem(const std::string& key, Module* owner);
+ /** Extensible subclasses
+ */
+ enum ExtensibleType
+ {
+ EXT_USER,
+ EXT_CHANNEL,
+ EXT_MEMBERSHIP
+ };
+
+ /** Type (subclass) of Extensible that this ExtensionItem is valid for
+ */
+ const ExtensibleType type;
+
+ ExtensionItem(const std::string& key, ExtensibleType exttype, Module* owner);
virtual ~ExtensionItem();
/** Serialize this item into a string
*
@@ -56,6 +67,10 @@ class CoreExport ExtensionItem : public ServiceProvider, public usecountbase
/** Free the item */
virtual void free(void* item) = 0;
+ /** Register this object in the ExtensionManager
+ */
+ void RegisterService() CXX11_OVERRIDE;
+
protected:
/** Get the item from the internal map */
void* get_raw(const Extensible* container) const;
@@ -75,7 +90,7 @@ class CoreExport ExtensionItem : public ServiceProvider, public usecountbase
class CoreExport Extensible : public classbase
{
public:
- typedef std::map<reference<ExtensionItem>,void*> ExtensibleStore;
+ typedef insp::flat_map<reference<ExtensionItem>, void*> ExtensibleStore;
// Friend access for the protected getter/setter
friend class ExtensionItem;
@@ -108,18 +123,27 @@ class CoreExport Extensible : public classbase
class CoreExport ExtensionManager
{
- std::map<std::string, reference<ExtensionItem> > types;
public:
+ typedef std::map<std::string, reference<ExtensionItem> > ExtMap;
+
bool Register(ExtensionItem* item);
void BeginUnregister(Module* module, std::vector<reference<ExtensionItem> >& list);
ExtensionItem* GetItem(const std::string& name);
+
+ /** Get all registered extensions keyed by their names
+ * @return Const map of ExtensionItem pointers keyed by their names
+ */
+ const ExtMap& GetExts() const { return types; }
+
+ private:
+ ExtMap types;
};
/** Base class for items that are NOT synchronized between servers */
class CoreExport LocalExtItem : public ExtensionItem
{
public:
- LocalExtItem(const std::string& key, Module* owner);
+ LocalExtItem(const std::string& key, ExtensibleType exttype, Module* owner);
virtual ~LocalExtItem();
virtual std::string serialize(SerializeFormat format, const Extensible* container, void* item) const;
virtual void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
@@ -130,7 +154,8 @@ template <typename T, typename Del = stdalgo::defaultdeleter<T> >
class SimpleExtItem : public LocalExtItem
{
public:
- SimpleExtItem(const std::string& Key, Module* parent) : LocalExtItem(Key, parent)
+ SimpleExtItem(const std::string& Key, ExtensibleType exttype, Module* parent)
+ : LocalExtItem(Key, exttype, parent)
{
}
@@ -175,17 +200,19 @@ class SimpleExtItem : public LocalExtItem
class CoreExport LocalStringExt : public SimpleExtItem<std::string>
{
public:
- LocalStringExt(const std::string& key, Module* owner);
+ LocalStringExt(const std::string& key, ExtensibleType exttype, Module* owner);
virtual ~LocalStringExt();
std::string serialize(SerializeFormat format, const Extensible* container, void* item) const;
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
};
class CoreExport LocalIntExt : public LocalExtItem
{
public:
- LocalIntExt(const std::string& key, Module* owner);
+ LocalIntExt(const std::string& key, ExtensibleType exttype, Module* owner);
virtual ~LocalIntExt();
std::string serialize(SerializeFormat format, const Extensible* container, void* item) const;
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
intptr_t get(const Extensible* container) const;
intptr_t set(Extensible* container, intptr_t value);
void unset(Extensible* container) { set(container, 0); }
@@ -195,7 +222,7 @@ class CoreExport LocalIntExt : public LocalExtItem
class CoreExport StringExtItem : public ExtensionItem
{
public:
- StringExtItem(const std::string& key, Module* owner);
+ StringExtItem(const std::string& key, ExtensibleType exttype, Module* owner);
virtual ~StringExtItem();
std::string* get(const Extensible* container) const;
std::string serialize(SerializeFormat format, const Extensible* container, void* item) const;
diff --git a/include/flat_map.h b/include/flat_map.h
new file mode 100644
index 000000000..bef1404e4
--- /dev/null
+++ b/include/flat_map.h
@@ -0,0 +1,383 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+#include <vector>
+
+namespace insp
+{
+
+namespace detail
+{
+
+template <typename T, typename Comp>
+class map_pair_compare : public Comp
+{
+ typedef T value_type;
+ typedef typename value_type::first_type key_type;
+
+ public:
+ bool operator()(const value_type& x, const value_type& y) const
+ {
+ return Comp::operator()(x.first, y.first);
+ }
+
+ bool operator()(const value_type& x, const key_type& y) const
+ {
+ return Comp::operator()(x.first, y);
+ }
+
+ bool operator()(const key_type& x, const value_type& y) const
+ {
+ return Comp::operator()(x, y.first);
+ }
+};
+
+template <typename Val, typename Comp>
+class map_value_compare : public std::binary_function<Val, Val, bool>
+{
+ public:
+ // Constructor should be private
+
+ bool operator()(const Val& x, const Val& y) const
+ {
+ Comp c;
+ return c(x.first, y.first);
+ }
+};
+
+template <typename T, typename Comp, typename Key = T, typename ElementComp = Comp>
+class flat_map_base
+{
+ protected:
+ typedef std::vector<T> storage_type;
+ storage_type vect;
+
+ public:
+ typedef typename storage_type::iterator iterator;
+ typedef typename storage_type::const_iterator const_iterator;
+ typedef typename storage_type::reverse_iterator reverse_iterator;
+ typedef typename storage_type::const_reverse_iterator const_reverse_iterator;
+
+ typedef typename storage_type::size_type size_type;
+ typedef typename storage_type::difference_type difference_type;
+ typedef Key key_type;
+ typedef T value_type;
+
+ typedef Comp key_compare;
+ typedef ElementComp value_compare;
+
+ flat_map_base() { }
+
+ flat_map_base(const flat_map_base& other)
+ : vect(other.vect)
+ {
+ }
+
+ size_type size() const { return vect.size(); }
+ bool empty() const { return vect.empty(); }
+ size_type capacity() const { return vect.capacity(); }
+ size_type max_size() const { return vect.max_size(); }
+
+ void clear() { vect.clear(); }
+ void reserve(size_type n) { vect.reserve(n); }
+
+ iterator begin() { return vect.begin(); }
+ iterator end() { return vect.end(); }
+ reverse_iterator rbegin() { return vect.rbegin(); }
+ reverse_iterator rend() { return vect.rend(); }
+
+ const_iterator begin() const { return vect.begin(); }
+ const_iterator end() const { return vect.end(); }
+ const_reverse_iterator rbegin() const { return vect.rbegin(); }
+ const_reverse_iterator rend() const { return vect.rend(); }
+
+ key_compare key_comp() const { return Comp(); }
+
+ iterator erase(iterator it) { return vect.erase(it); }
+ iterator erase(iterator first, iterator last) { return vect.erase(first, last); }
+ size_type erase(const key_type& x)
+ {
+ size_type n = vect.size();
+ std::pair<iterator, iterator> itpair = equal_range(x);
+ vect.erase(itpair.first, itpair.second);
+ return n - vect.size();
+ }
+
+ iterator find(const key_type& x)
+ {
+ value_compare c;
+ iterator it = std::lower_bound(vect.begin(), vect.end(), x, c);
+ if ((it != vect.end()) && (!c(x, *it)))
+ return it;
+ return vect.end();
+ }
+
+ const_iterator find(const key_type& x) const
+ {
+ // Same as above but this time we return a const_iterator
+ value_compare c;
+ const_iterator it = std::lower_bound(vect.begin(), vect.end(), x, c);
+ if ((it != vect.end()) && (!c(x, *it)))
+ return it;
+ return vect.end();
+ }
+
+ std::pair<iterator, iterator> equal_range(const key_type& x)
+ {
+ return std::equal_range(vect.begin(), vect.end(), x, value_compare());
+ }
+
+ std::pair<const_iterator, const_iterator> equal_range(const key_type& x) const
+ {
+ return std::equal_range(vect.begin(), vect.end(), x, value_compare());
+ }
+
+ iterator lower_bound(const key_type& x)
+ {
+ return std::lower_bound(vect.begin(), vect.end(), x, value_compare());
+ }
+
+ const_iterator lower_bound(const key_type& x) const
+ {
+ return std::lower_bound(vect.begin(), vect.end(), x, value_compare());
+ }
+
+ iterator upper_bound(const key_type& x)
+ {
+ return std::upper_bound(vect.begin(), vect.end(), x, value_compare());
+ }
+
+ const_iterator upper_bound(const key_type& x) const
+ {
+ return std::upper_bound(vect.begin(), vect.end(), x, value_compare());
+ }
+
+ size_type count(const key_type& x) const
+ {
+ std::pair<const_iterator, const_iterator> itpair = equal_range(x);
+ return std::distance(itpair.first, itpair.second);
+ }
+
+ protected:
+ std::pair<iterator, bool> insert_single(const value_type& x)
+ {
+ bool inserted = false;
+
+ value_compare c;
+ iterator it = std::lower_bound(vect.begin(), vect.end(), x, c);
+ if ((it == vect.end()) || (c(x, *it)))
+ {
+ inserted = true;
+ it = vect.insert(it, x);
+ }
+ return std::make_pair(it, inserted);
+ }
+
+ iterator insert_multi(const value_type& x)
+ {
+ iterator it = std::lower_bound(vect.begin(), vect.end(), x, value_compare());
+ return vect.insert(it, x);
+ }
+};
+
+} // namespace detail
+
+template <typename T, typename Comp = std::less<T> >
+class flat_set : public detail::flat_map_base<T, Comp>
+{
+ typedef detail::flat_map_base<T, Comp> base_t;
+
+ public:
+ typedef typename base_t::iterator iterator;
+ typedef typename base_t::value_type value_type;
+
+ flat_set() { }
+
+ template <typename InputIterator>
+ flat_set(InputIterator first, InputIterator last)
+ {
+ this->insert(first, last);
+ }
+
+ flat_set(const flat_set& other)
+ : base_t(other)
+ {
+ }
+
+ std::pair<iterator, bool> insert(const value_type& x)
+ {
+ return this->insert_single(x);
+ }
+
+ template <typename InputIterator>
+ void insert(InputIterator first, InputIterator last)
+ {
+ for (; first != last; ++first)
+ this->insert_single(*first);
+ }
+
+ void swap(flat_set& other)
+ {
+ base_t::vect.swap(other.vect);
+ }
+};
+
+template <typename T, typename Comp = std::less<T> >
+class flat_multiset : public detail::flat_map_base<T, Comp>
+{
+ typedef detail::flat_map_base<T, Comp> base_t;
+
+ public:
+ typedef typename base_t::iterator iterator;
+ typedef typename base_t::value_type value_type;
+
+ flat_multiset() { }
+
+ template <typename InputIterator>
+ flat_multiset(InputIterator first, InputIterator last)
+ {
+ this->insert(first, last);
+ }
+
+ flat_multiset(const flat_multiset& other)
+ : base_t(other)
+ {
+ }
+
+ iterator insert(const value_type& x)
+ {
+ return this->insert_multi(x);
+ }
+
+ template <typename InputIterator>
+ void insert(InputIterator first, InputIterator last)
+ {
+ for (; first != last; ++first)
+ insert_multi(*first);
+ }
+
+ void swap(flat_multiset& other)
+ {
+ base_t::vect.swap(other.vect);
+ }
+};
+
+template <typename T, typename U, typename Comp = std::less<T> >
+class flat_map : public detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> >
+{
+ typedef detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> > base_t;
+
+ public:
+ typedef typename base_t::iterator iterator;
+ typedef typename base_t::key_type key_type;
+ typedef typename base_t::value_type value_type;
+ typedef U mapped_type;
+ typedef typename base_t::value_compare value_compare;
+
+ flat_map() { }
+
+ template <typename InputIterator>
+ flat_map(InputIterator first, InputIterator last)
+ {
+ insert(first, last);
+ }
+
+ flat_map(const flat_map& other)
+ : base_t(other)
+ {
+ }
+
+ std::pair<iterator, bool> insert(const value_type& x)
+ {
+ return this->insert_single(x);
+ }
+
+ template <typename InputIterator>
+ void insert(InputIterator first, InputIterator last)
+ {
+ for (; first != last; ++first)
+ this->insert_single(*first);
+ }
+
+ void swap(flat_map& other)
+ {
+ base_t::vect.swap(other.vect);
+ }
+
+ mapped_type& operator[](const key_type& x)
+ {
+ return insert(std::make_pair(x, mapped_type())).first->second;
+ }
+
+ value_compare value_comp() const
+ {
+ return value_compare();
+ }
+};
+
+template <typename T, typename U, typename Comp = std::less<T> >
+class flat_multimap : public detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> >
+{
+ typedef detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> > base_t;
+
+ public:
+ typedef typename base_t::iterator iterator;
+ typedef typename base_t::value_type value_type;
+ typedef U mapped_type;
+ typedef typename base_t::value_compare value_compare;
+
+ flat_multimap() { }
+
+ template <typename InputIterator>
+ flat_multimap(InputIterator first, InputIterator last)
+ {
+ this->insert(first, last);
+ }
+
+ flat_multimap(const flat_multimap& other)
+ : base_t(other)
+ {
+ }
+
+ iterator insert(const value_type& x)
+ {
+ return this->insert_multi(x);
+ }
+
+ template <typename InputIterator>
+ void insert(InputIterator first, InputIterator last)
+ {
+ for (; first != last; ++first)
+ this->insert_multi(*first);
+ }
+
+ void swap(flat_multimap& other)
+ {
+ base_t::vect.swap(other.vect);
+ }
+
+ value_compare value_comp() const
+ {
+ return value_compare();
+ }
+};
+
+} // namespace insp
diff --git a/include/hashcomp.h b/include/hashcomp.h
index 6cd3fc3c0..f3b1ba6e9 100644
--- a/include/hashcomp.h
+++ b/include/hashcomp.h
@@ -40,11 +40,9 @@
* treat [ identical to {, ] identical to }, and \
* as identical to |.
*
- * Our hashing functions are designed to accept
- * std::string and compare/hash them as type irc::string
- * by converting them internally. This makes them
- * backwards compatible with other code which is not
- * aware of irc::string.
+ * There are functors that accept std::string and
+ * compare/hash them as type irc::string by using
+ * mapping arrays internally.
*******************************************************/
/** Seperate from the other casemap tables so that code *can* still exclusively rely on RFC casemapping
@@ -70,43 +68,31 @@ CoreExport extern unsigned const char ascii_case_insensitive_map[256];
*/
CoreExport extern unsigned const char rfc_case_sensitive_map[256];
-template<typename T> const T& SearchAndReplace(T& text, const T& pattern, const T& replace)
-{
- T replacement;
- if ((!pattern.empty()) && (!text.empty()))
- {
- for (std::string::size_type n = 0; n != text.length(); ++n)
- {
- if (text.length() >= pattern.length() && text.substr(n, pattern.length()) == pattern)
- {
- /* Found the pattern in the text, replace it, and advance */
- replacement.append(replace);
- n = n + pattern.length() - 1;
- }
- else
- {
- replacement += text[n];
- }
- }
- }
- text = replacement;
- return text;
-}
-
/** The irc namespace contains a number of helper classes.
*/
namespace irc
{
+ /** Check if two IRC object (e.g. nick or channel) names are equal.
+ * This function uses national_case_insensitive_map to determine equality, which, by default does comparison
+ * according to RFC 1459, treating certain otherwise non-identical characters as identical.
+ * @param s1 First string to compare
+ * @param s2 Second string to compare
+ * @return True if the two names are equal, false otherwise
+ */
+ CoreExport bool equals(const std::string& s1, const std::string& s2);
/** This class returns true if two strings match.
* Case sensitivity is ignored, and the RFC 'character set'
* is adhered to
*/
- struct CoreExport StrHashComp
+ struct StrHashComp
{
/** The operator () does the actual comparison in hash_map
*/
- bool operator()(const std::string& s1, const std::string& s2) const;
+ bool operator()(const std::string& s1, const std::string& s2) const
+ {
+ return equals(s1, s2);
+ }
};
struct insensitive
@@ -170,85 +156,15 @@ namespace irc
/** Joins the contents of a vector to a string.
* @param sequence Zero or more items to join.
- * @separator The character to place between the items.
+ * @param separator The character to place between the items, defaults to ' ' (space).
+ * @return Joined string.
*/
std::string CoreExport stringjoiner(const std::vector<std::string>& sequence, char separator = ' ');
- /** irc::modestacker stacks mode sequences into a list.
- * It can then reproduce this list, clamped to a maximum of MAXMODES
- * values per line.
- */
- class CoreExport modestacker
- {
- private:
- /** The mode sequence and its parameters
- */
- std::deque<std::string> sequence;
-
- /** True if the mode sequence is initially adding
- * characters, false if it is initially removing
- * them
- */
- bool adding;
- public:
-
- /** Construct a new modestacker.
- * @param add True if the stack is adding modes,
- * false if it is removing them
- */
- modestacker(bool add);
-
- /** Push a modeletter and its parameter onto the stack.
- * No checking is performed as to if this mode actually
- * requires a parameter. If you stack invalid mode
- * sequences, they will be tidied if and when they are
- * passed to a mode parser.
- * @param modeletter The mode letter to insert
- * @param parameter The parameter for the mode
- */
- void Push(char modeletter, const std::string &parameter);
-
- /** Push a modeletter without parameter onto the stack.
- * No checking is performed as to if this mode actually
- * requires a parameter. If you stack invalid mode
- * sequences, they will be tidied if and when they are
- * passed to a mode parser.
- * @param modeletter The mode letter to insert
- */
- void Push(char modeletter);
-
- /** Push a '+' symbol onto the stack.
- */
- void PushPlus();
-
- /** Push a '-' symbol onto the stack.
- */
- void PushMinus();
-
- /** Return zero or more elements which form the
- * mode line. This will be clamped to a max of
- * MAXMODES items (MAXMODES-1 mode parameters and
- * one mode sequence string), and max_line_size
- * characters. As specified below, this function
- * should be called in a loop until it returns zero,
- * indicating there are no more modes to return.
- * @param result The vector to populate. This will not
- * be cleared before it is used.
- * @param max_line_size The maximum size of the line
- * to build, in characters, seperate to MAXMODES.
- * @return The number of elements in the deque.
- * The function should be called repeatedly until it
- * returns 0, in case there are multiple lines of
- * mode changes to be obtained.
- */
- int GetStackedLine(std::vector<std::string> &result, int max_line_size = 360);
-
- };
-
/** irc::sepstream allows for splitting token seperated lists.
* Each successive call to sepstream::GetToken() returns
* the next token, until none remain, at which point the method returns
- * an empty string.
+ * false.
*/
class CoreExport sepstream
{
@@ -336,12 +252,6 @@ namespace irc
*/
bool GetToken(std::string &token);
- /** Fetch the next token from the stream as an irc::string
- * @param token The next token available, or an empty string if none remain
- * @return True if tokens are left to be read, false if the last token was just retrieved.
- */
- bool GetToken(irc::string &token);
-
/** Fetch the next token from the stream as an integer
* @param token The next token available, or undefined if none remain
* @return True if tokens are left to be read, false if the last token was just retrieved.
@@ -408,107 +318,4 @@ namespace irc
*/
long GetToken();
};
-
- struct hash
- {
- /** Hash an irc::string using RFC1459 case sensitivity rules
- * @param s A string to hash
- * @return The hash value
- */
- size_t CoreExport operator()(const irc::string &s) const;
- };
-}
-
-/* Define operators for using >> and << with irc::string to an ostream on an istream. */
-/* This was endless fun. No. Really. */
-/* It was also the first core change Ommeh made, if anyone cares */
-
-/** Operator << for irc::string
- */
-inline std::ostream& operator<<(std::ostream &os, const irc::string &str) { return os << str.c_str(); }
-
-/** Operator >> for irc::string
- */
-inline std::istream& operator>>(std::istream &is, irc::string &str)
-{
- std::string tmp;
- is >> tmp;
- str = tmp.c_str();
- return is;
-}
-
-/* Define operators for + and == with irc::string to std::string for easy assignment
- * and comparison
- *
- * Operator +
- */
-inline std::string operator+ (std::string& leftval, irc::string& rightval)
-{
- return leftval + std::string(rightval.c_str());
-}
-
-/* Define operators for + and == with irc::string to std::string for easy assignment
- * and comparison
- *
- * Operator +
- */
-inline irc::string operator+ (irc::string& leftval, std::string& rightval)
-{
- return leftval + irc::string(rightval.c_str());
-}
-
-/* Define operators for + and == with irc::string to std::string for easy assignment
- * and comparison
- *
- * Operator ==
- */
-inline bool operator== (const std::string& leftval, const irc::string& rightval)
-{
- return (leftval.c_str() == rightval);
-}
-
-/* Define operators for + and == with irc::string to std::string for easy assignment
- * and comparison
- *
- * Operator ==
- */
-inline bool operator== (const irc::string& leftval, const std::string& rightval)
-{
- return (leftval == rightval.c_str());
-}
-
-/* Define operators != for irc::string to std::string for easy comparison
- */
-inline bool operator!= (const irc::string& leftval, const std::string& rightval)
-{
- return !(leftval == rightval.c_str());
-}
-
-/* Define operators != for std::string to irc::string for easy comparison
- */
-inline bool operator!= (const std::string& leftval, const irc::string& rightval)
-{
- return !(leftval.c_str() == rightval);
-}
-
-/** Assign an irc::string to a std::string.
- */
-inline std::string assign(const irc::string &other) { return other.c_str(); }
-
-/** Assign a std::string to an irc::string.
- */
-inline irc::string assign(const std::string &other) { return other.c_str(); }
-
-/** Trim the leading and trailing spaces from a std::string.
- */
-inline std::string& trim(std::string &str)
-{
- std::string::size_type start = str.find_first_not_of(" ");
- std::string::size_type end = str.find_last_not_of(" ");
- if (start == std::string::npos || end == std::string::npos)
- str = "";
- else
- str = str.substr(start, end-start+1);
-
- return str;
}
diff --git a/include/inspircd.h b/include/inspircd.h
index db13b2ab3..303d24745 100644
--- a/include/inspircd.h
+++ b/include/inspircd.h
@@ -32,6 +32,7 @@
#include <cstdio>
#include <cstring>
#include <ctime>
+#include <stdint.h>
#include <algorithm>
#include <bitset>
@@ -44,13 +45,27 @@
#include <vector>
#include "intrusive_list.h"
+#include "flat_map.h"
#include "compat.h"
+#include "aligned_storage.h"
#include "typedefs.h"
#include "stdalgo.h"
CoreExport extern InspIRCd* ServerInstance;
+/** Base class for manager classes that are still accessed using -> but are no longer pointers
+ */
+template <typename T>
+struct fakederef
+{
+ T* operator->()
+ {
+ return static_cast<T*>(this);
+ }
+};
+
#include "config.h"
+#include "convto.h"
#include "dynref.h"
#include "consolecolors.h"
#include "caller.h"
@@ -58,6 +73,7 @@ CoreExport extern InspIRCd* ServerInstance;
#include "extensible.h"
#include "fileutils.h"
#include "numerics.h"
+#include "numeric.h"
#include "uid.h"
#include "server.h"
#include "users.h"
@@ -78,83 +94,8 @@ CoreExport extern InspIRCd* ServerInstance;
#include "configreader.h"
#include "inspstring.h"
#include "protocol.h"
-
-/** Returned by some functions to indicate failure.
- */
-#define ERROR -1
-
-/** Template function to convert any input type to std::string
- */
-template<typename T> inline std::string ConvNumeric(const T &in)
-{
- if (in == 0)
- return "0";
- T quotient = in;
- std::string out;
- while (quotient)
- {
- out += "0123456789"[ std::abs( (long)quotient % 10 ) ];
- quotient /= 10;
- }
- if (in < 0)
- out += '-';
- std::reverse(out.begin(), out.end());
- return out;
-}
-
-/** Template function to convert any input type to std::string
- */
-inline std::string ConvToStr(const int in)
-{
- return ConvNumeric(in);
-}
-
-/** Template function to convert any input type to std::string
- */
-inline std::string ConvToStr(const long in)
-{
- return ConvNumeric(in);
-}
-
-/** Template function to convert any input type to std::string
- */
-inline std::string ConvToStr(const char* in)
-{
- return in;
-}
-
-/** Template function to convert any input type to std::string
- */
-inline std::string ConvToStr(const bool in)
-{
- return (in ? "1" : "0");
-}
-
-/** Template function to convert any input type to std::string
- */
-inline std::string ConvToStr(char in)
-{
- return std::string(1, in);
-}
-
-/** Template function to convert any input type to std::string
- */
-template <class T> inline std::string ConvToStr(const T &in)
-{
- std::stringstream tmp;
- if (!(tmp << in)) return std::string();
- return tmp.str();
-}
-
-/** Template function to convert any input type to any other type
- * (usually an integer or numeric type)
- */
-template<typename T> inline long ConvToInt(const T &in)
-{
- std::stringstream tmp;
- if (!(tmp << in)) return 0;
- return atol(tmp.str().c_str());
-}
+#include "bancache.h"
+#include "isupportmanager.h"
/** This class contains various STATS counters
* It is used by the InspIRCd class, which internally
@@ -165,38 +106,38 @@ class serverstats
public:
/** Number of accepted connections
*/
- unsigned long statsAccept;
+ unsigned long Accept;
/** Number of failed accepts
*/
- unsigned long statsRefused;
+ unsigned long Refused;
/** Number of unknown commands seen
*/
- unsigned long statsUnknown;
+ unsigned long Unknown;
/** Number of nickname collisions handled
*/
- unsigned long statsCollisions;
+ unsigned long Collisions;
/** Number of DNS queries sent out
*/
- unsigned long statsDns;
+ unsigned long Dns;
/** Number of good DNS replies received
* NOTE: This may not tally to the number sent out,
* due to timeouts and other latency issues.
*/
- unsigned long statsDnsGood;
+ unsigned long DnsGood;
/** Number of bad (negative) DNS replies received
* NOTE: This may not tally to the number sent out,
* due to timeouts and other latency issues.
*/
- unsigned long statsDnsBad;
+ unsigned long DnsBad;
/** Number of inbound connections seen
*/
- unsigned long statsConnects;
+ unsigned long Connects;
/** Total bytes of data transmitted
*/
- unsigned long statsSent;
+ unsigned long Sent;
/** Total bytes of data received
*/
- unsigned long statsRecv;
+ unsigned long Recv;
#ifdef _WIN32
/** Cpu usage at last sample
*/
@@ -218,33 +159,12 @@ class serverstats
/** The constructor initializes all the counts to zero
*/
serverstats()
- : statsAccept(0), statsRefused(0), statsUnknown(0), statsCollisions(0), statsDns(0),
- statsDnsGood(0), statsDnsBad(0), statsConnects(0), statsSent(0), statsRecv(0)
+ : Accept(0), Refused(0), Unknown(0), Collisions(0), Dns(0),
+ DnsGood(0), DnsBad(0), Connects(0), Sent(0), Recv(0)
{
}
};
-/** This class manages the generation and transmission of ISUPPORT. */
-class CoreExport ISupportManager
-{
-private:
- /** The generated lines which are sent to clients. */
- std::vector<std::string> Lines;
-
-public:
- /** (Re)build the ISUPPORT vector. */
- void Build();
-
- /** Returns the std::vector of ISUPPORT lines. */
- const std::vector<std::string>& GetLines()
- {
- return this->Lines;
- }
-
- /** Send the 005 numerics (ISUPPORT) to a user. */
- void SendTo(LocalUser* user);
-};
-
DEFINE_HANDLER1(IsNickHandler, bool, const std::string&);
DEFINE_HANDLER2(GenRandomHandler, void, char*, size_t);
DEFINE_HANDLER1(IsIdentHandler, bool, const std::string&);
@@ -329,15 +249,15 @@ class CoreExport InspIRCd
/** Mode handler, handles mode setting and removal
*/
- ModeParser* Modes;
+ ModeParser Modes;
/** Command parser, handles client to server commands
*/
- CommandParser* Parser;
+ CommandParser Parser;
/** Thread engine, Handles threading where required
*/
- ThreadEngine* Threads;
+ ThreadEngine Threads;
/** The thread/class used to read config files in REHASH and on startup
*/
@@ -345,21 +265,21 @@ class CoreExport InspIRCd
/** LogManager handles logging.
*/
- LogManager *Logs;
+ LogManager Logs;
/** ModuleManager contains everything related to loading/unloading
* modules.
*/
- ModuleManager* Modules;
+ ModuleManager Modules;
/** BanCacheManager is used to speed up checking of restrictions on connection
* to the IRCd.
*/
- BanCacheManager *BanCache;
+ BanCacheManager BanCache;
/** Stats class, holds miscellaneous stats counters
*/
- serverstats* stats;
+ serverstats stats;
/** Server Config class, holds configuration file data
*/
@@ -368,7 +288,7 @@ class CoreExport InspIRCd
/** Snomask manager - handles routing of snomask messages
* to opers.
*/
- SnomaskManager* SNO;
+ SnomaskManager SNO;
/** Timer manager class, triggers Timer timer events
*/
@@ -380,7 +300,7 @@ class CoreExport InspIRCd
/** User manager. Various methods and data associated with users.
*/
- UserManager *Users;
+ UserManager Users;
/** Channel list, a hash_map containing all channels XXX move to channel manager class
*/
@@ -398,6 +318,10 @@ class CoreExport InspIRCd
*/
ProtocolInterface* PI;
+ /** Default implementation of the ProtocolInterface, does nothing
+ */
+ ProtocolInterface DefaultProtocolInterface;
+
/** Holds extensible for user operquit
*/
StringExtItem OperQuit;
@@ -435,15 +359,6 @@ class CoreExport InspIRCd
*/
int BindPorts(FailedPortList &failed_ports);
- /** Binds a socket on an already open file descriptor
- * @param sockfd A valid file descriptor of an open socket
- * @param port The port number to bind to
- * @param addr The address to bind to (IP only)
- * @param dolisten Should this port be listened on?
- * @return True if the port was bound successfully
- */
- bool BindSocket(int sockfd, int port, const char* addr, bool dolisten = true);
-
/** Find a user in the nick hash.
* If the user cant be found in the nick hash check the uuid hash
* @param nick The nickname to find
@@ -508,11 +423,6 @@ class CoreExport InspIRCd
static const char* Format(const char* formatString, ...) CUSTOM_PRINTF(1, 2);
static const char* Format(va_list &vaList, const char* formatString) CUSTOM_PRINTF(2, 0);
- /** Send an error notice to all local users, opered and unopered
- * @param s The error string to send
- */
- void SendError(const std::string &s);
-
/** Return true if a nickname is valid
* @param n A nickname to verify
* @return True if the nick is valid
@@ -557,7 +467,7 @@ class CoreExport InspIRCd
*/
static bool IsValidMask(const std::string& mask);
- /** Strips all color codes from the given string
+ /** Strips all color and control codes except 001 from the given string
* @param sentence The string to strip from
*/
static void StripColor(std::string &sentence);
@@ -597,9 +507,10 @@ class CoreExport InspIRCd
/** Attempt to write the process id to a given file
* @param filename The PID file to attempt to write to
+ * @param exitonfail If true and the PID fail cannot be written log to stdout and exit, otherwise only log on failure
* @return This function may bail if the file cannot be written
*/
- void WritePID(const std::string &filename);
+ void WritePID(const std::string& filename, bool exitonfail = true);
/** This constructor initialises all the subsystems and reads the config file.
* @param argc The argument count passed to main()
@@ -610,23 +521,6 @@ class CoreExport InspIRCd
*/
InspIRCd(int argc, char** argv);
- /** Send a line of WHOIS data to a user.
- * @param user user to send the line to
- * @param dest user being WHOISed
- * @param numeric Numeric to send
- * @param text Text of the numeric
- */
- void SendWhoisLine(User* user, User* dest, int numeric, const std::string &text);
-
- /** Send a line of WHOIS data to a user.
- * @param user user to send the line to
- * @param dest user being WHOISed
- * @param numeric Numeric to send
- * @param format Format string for the numeric
- * @param ... Parameters for the format string
- */
- void SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...) CUSTOM_PRINTF(5, 6);
-
/** Called to check whether a channel restriction mode applies to a user
* @param User that is attempting some action
* @param Channel that the action is being performed on
@@ -641,8 +535,21 @@ class CoreExport InspIRCd
void Cleanup();
/** Return a time_t as a human-readable string.
+ * @param format The format to retrieve the date/time in. See `man 3 strftime`
+ * for more information. If NULL, "%a %b %d %T %Y" is assumed.
+ * @param utc True to convert the time to string as-is, false to convert it to local time first.
+ * @return A string representing the given date/time.
*/
- static std::string TimeString(time_t curtime);
+ static std::string TimeString(time_t curtime, const char* format = NULL, bool utc = false);
+
+ /** Compare two strings in a timing-safe way. If the lengths of the strings differ, the function
+ * returns false immediately (leaking information about the length), otherwise it compares each
+ * character and only returns after all characters have been compared.
+ * @param one First string
+ * @param two Second string
+ * @return True if the strings match, false if they don't
+ */
+ static bool TimingSafeCompare(const std::string& one, const std::string& two);
/** Begin execution of the server.
* NOTE: this function NEVER returns. Internally,
@@ -678,3 +585,7 @@ inline void stdalgo::culldeleter::operator()(classbase* item)
if (item)
ServerInstance->GlobalCulls.AddItem(item);
}
+
+#include "numericbuilder.h"
+#include "modules/whois.h"
+#include "modules/stats.h"
diff --git a/include/inspsocket.h b/include/inspsocket.h
index 720489b77..751374fdf 100644
--- a/include/inspsocket.h
+++ b/include/inspsocket.h
@@ -90,7 +90,7 @@ class CoreExport SocketTimeout : public Timer
* @param secs_from_now Seconds from now to time out
* @param now The current time
*/
- SocketTimeout(int fd, BufferedSocket* thesock, long secs_from_now, time_t now) : Timer(secs_from_now, now), sock(thesock), sfd(fd) { }
+ SocketTimeout(int fd, BufferedSocket* thesock, long secs_from_now) : Timer(secs_from_now), sock(thesock), sfd(fd) { }
/** Handle tick event
*/
@@ -103,31 +103,184 @@ class CoreExport SocketTimeout : public Timer
*/
class CoreExport StreamSocket : public EventHandler
{
+ public:
+ /** Socket send queue
+ */
+ class SendQueue
+ {
+ public:
+ /** One element of the queue, a continuous buffer
+ */
+ typedef std::string Element;
+
+ /** Sequence container of buffers in the queue
+ */
+ typedef std::deque<Element> Container;
+
+ /** Container iterator
+ */
+ typedef Container::const_iterator const_iterator;
+
+ SendQueue() : nbytes(0) { }
+
+ /** Return whether the queue is empty
+ * @return True if the queue is empty, false otherwise
+ */
+ bool empty() const { return (nbytes == 0); }
+
+ /** Get the number of individual buffers in the queue
+ * @return Number of individual buffers in the queue
+ */
+ Container::size_type size() const { return data.size(); }
+
+ /** Get the number of queued bytes
+ * @return Size in bytes of the data in the queue
+ */
+ size_t bytes() const { return nbytes; }
+
+ /** Get the first buffer of the queue
+ * @return A reference to the first buffer in the queue
+ */
+ const Element& front() const { return data.front(); }
+
+ /** Get an iterator to the first buffer in the queue.
+ * The returned iterator cannot be used to make modifications to the queue,
+ * for that purpose the member functions push_*(), pop_front(), erase_front() and clear() can be used.
+ * @return Iterator referring to the first buffer in the queue, or end() if there are no elements.
+ */
+ const_iterator begin() const { return data.begin(); }
+
+ /** Get an iterator to the (theoretical) buffer one past the end of the queue.
+ * @return Iterator referring to one element past the end of the container
+ */
+ const_iterator end() const { return data.end(); }
+
+ /** Remove the first buffer in the queue
+ */
+ void pop_front()
+ {
+ nbytes -= data.front().length();
+ data.pop_front();
+ }
+
+ /** Remove bytes from the beginning of the first buffer
+ * @param n Number of bytes to remove
+ */
+ void erase_front(Element::size_type n)
+ {
+ nbytes -= n;
+ data.front().erase(0, n);
+ }
+
+ /** Insert a new buffer at the beginning of the queue
+ * @param newdata Data to add
+ */
+ void push_front(const Element& newdata)
+ {
+ data.push_front(newdata);
+ nbytes += newdata.length();
+ }
+
+ /** Insert a new buffer at the end of the queue
+ * @param newdata Data to add
+ */
+ void push_back(const Element& newdata)
+ {
+ data.push_back(newdata);
+ nbytes += newdata.length();
+ }
+
+ /** Clear the queue
+ */
+ void clear()
+ {
+ data.clear();
+ nbytes = 0;
+ }
+
+ void moveall(SendQueue& other)
+ {
+ nbytes += other.bytes();
+ data.insert(data.end(), other.data.begin(), other.data.end());
+ other.clear();
+ }
+
+ private:
+ /** Private send queue. Note that individual strings may be shared.
+ */
+ Container data;
+
+ /** Length, in bytes, of the sendq
+ */
+ size_t nbytes;
+ };
+
+ private:
/** The IOHook that handles raw I/O for this socket, or NULL */
IOHook* iohook;
- /** Private send queue. Note that individual strings may be shared
+ /** Send queue of the socket
*/
- std::deque<std::string> sendq;
- /** Length, in bytes, of the sendq */
- size_t sendq_len;
+ SendQueue sendq;
+
/** Error - if nonempty, the socket is dead, and this is the reason. */
std::string error;
+
+ /** Check if the socket has an error set, if yes, call OnError
+ * @param err Error to pass to OnError()
+ */
+ void CheckError(BufferedSocketError err);
+
+ /** Read data from the socket into the recvq, if successful call OnDataReady()
+ */
+ void DoRead();
+
+ /** Send as much data contained in a SendQueue object as possible.
+ * All data which successfully sent will be removed from the SendQueue.
+ * @param sq SendQueue to flush
+ */
+ void FlushSendQ(SendQueue& sq);
+
+ /** Read incoming data into a receive queue.
+ * @param rq Receive queue to put incoming data into
+ * @return < 0 on error or close, 0 if no new data is ready (but the socket is still connected), > 0 if data was read from the socket and put into the recvq
+ */
+ int ReadToRecvQ(std::string& rq);
+
+ /** Read data from a hook chain recursively, starting at 'hook'.
+ * If 'hook' is NULL, the recvq is filled with data from SocketEngine::Recv(), otherwise it is filled with data from the
+ * next hook in the chain.
+ * @param hook Next IOHook in the chain, can be NULL
+ * @param rq Receive queue to put incoming data into
+ * @return < 0 on error or close, 0 if no new data is ready (but the socket is still connected), > 0 if data was read from
+ the socket and put into the recvq
+ */
+ int HookChainRead(IOHook* hook, std::string& rq);
+
protected:
std::string recvq;
public:
- StreamSocket() : iohook(NULL), sendq_len(0) {}
+ StreamSocket() : iohook(NULL) { }
IOHook* GetIOHook() const;
void AddIOHook(IOHook* hook);
void DelIOHook();
- /** Handle event from socket engine.
- * This will call OnDataReady if there is *new* data in recvq
+
+ /** Flush the send queue
+ */
+ void DoWrite();
+
+ /** Called by the socket engine on a read event
+ */
+ void OnEventHandlerRead() CXX11_OVERRIDE;
+
+ /** Called by the socket engine on a write event
*/
- virtual void HandleEvent(EventType et, int errornum = 0);
- /** Dispatched from HandleEvent */
- virtual void DoRead();
- /** Dispatched from HandleEvent */
- virtual void DoWrite();
+ void OnEventHandlerWrite() CXX11_OVERRIDE;
+
+ /** Called by the socket engine on error
+ * @param errcode Error
+ */
+ void OnEventHandlerError(int errcode) CXX11_OVERRIDE;
/** Sets the error message for this socket. Once set, the socket is dead. */
void SetError(const std::string& err) { if (error.empty()) error = err; }
@@ -150,7 +303,9 @@ class CoreExport StreamSocket : public EventHandler
*/
bool GetNextLine(std::string& line, char delim = '\n');
/** Useful for implementing sendq exceeded */
- inline size_t getSendQSize() const { return sendq_len; }
+ size_t getSendQSize() const;
+
+ SendQueue& GetSendQ() { return sendq; }
/**
* Close the socket, remove from socket engine, etc
@@ -158,6 +313,12 @@ class CoreExport StreamSocket : public EventHandler
virtual void Close();
/** This ensures that close is called prior to destructor */
virtual CullResult cull();
+
+ /** Get the IOHook of a module attached to this socket
+ * @param mod Module whose IOHook to return
+ * @return IOHook belonging to the module or NULL if the module haven't attached an IOHook to this socket
+ */
+ IOHook* GetModHook(Module* mod) const;
};
/**
* BufferedSocket is an extendable socket class which modules
@@ -226,11 +387,10 @@ class CoreExport BufferedSocket : public StreamSocket
virtual ~BufferedSocket();
protected:
- virtual void DoWrite();
+ void OnEventHandlerWrite() CXX11_OVERRIDE;
BufferedSocketError BeginConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned long timeout);
BufferedSocketError BeginConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip);
};
inline IOHook* StreamSocket::GetIOHook() const { return iohook; }
-inline void StreamSocket::AddIOHook(IOHook* hook) { iohook = hook; }
inline void StreamSocket::DelIOHook() { iohook = NULL; }
diff --git a/include/intrusive_list.h b/include/intrusive_list.h
index 134a72267..de013ae38 100644
--- a/include/intrusive_list.h
+++ b/include/intrusive_list.h
@@ -21,9 +21,13 @@
#include <iterator>
+namespace insp
+{
+
struct intrusive_list_def_tag { };
template <typename T, typename Tag = intrusive_list_def_tag> class intrusive_list;
+template <typename T, typename Tag = intrusive_list_def_tag> class intrusive_list_tail;
template <typename T, typename Tag = intrusive_list_def_tag>
class intrusive_list_node
@@ -48,115 +52,20 @@ class intrusive_list_node
}
friend class intrusive_list<T, Tag>;
+ friend class intrusive_list_tail<T, Tag>;
};
-template <typename T, typename Tag>
-class intrusive_list
-{
- public:
- class iterator : public std::iterator<std::bidirectional_iterator_tag, T*>
- {
- T* curr;
-
- public:
- iterator(T* i = NULL)
- : curr(i)
- {
- }
-
- iterator& operator++()
- {
- curr = curr->intrusive_list_node<T, Tag>::ptr_next;
- return *this;
- }
-
- iterator operator++(int)
- {
- iterator ret(*this);
- operator++();
- return ret;
- }
-
- iterator& operator--()
- {
- curr = curr->intrusive_list_node<T, Tag>::ptr_prev;
- return *this;
- }
-
- iterator operator--(int)
- {
- iterator ret(*this);
- operator--();
- return ret;
- }
-
- bool operator==(const iterator& other) const { return (curr == other.curr); }
- bool operator!=(const iterator& other) const { return (curr != other.curr); }
- T* operator*() const { return curr; }
- };
-
- typedef iterator const_iterator;
-
- intrusive_list()
- : listhead(NULL)
- , listsize(0)
- {
- }
-
- bool empty() const
- {
- return (size() == 0);
- }
-
- size_t size() const
- {
- return listsize;
- }
-
- iterator begin() const
- {
- return iterator(listhead);
- }
-
- iterator end() const
- {
- return iterator();
- }
+} // namespace insp
- void pop_front()
- {
- erase(listhead);
- }
-
- T* front() const
- {
- return listhead;
- }
+// Intrusive list where the list only has a pointer to the head element
+#define INSPIRCD_INTRUSIVE_LIST_NAME intrusive_list
+#include "intrusive_list_impl.h"
+#undef INSPIRCD_INTRUSIVE_LIST_NAME
- void push_front(T* x)
- {
- if (listsize++)
- {
- x->intrusive_list_node<T, Tag>::ptr_next = listhead;
- listhead->intrusive_list_node<T, Tag>::ptr_prev = x;
- }
- listhead = x;
- }
-
- void erase(const iterator& it)
- {
- erase(*it);
- }
-
- void erase(T* x)
- {
- if (listhead == x)
- listhead = x->intrusive_list_node<T, Tag>::ptr_next;
- x->intrusive_list_node<T, Tag>::unlink();
- listsize--;
- }
-
- private:
- T* listhead;
- size_t listsize;
-};
+// Intrusive list where the list maintains a pointer to both the head and the tail elements.
+// Additional methods: back(), push_back(), pop_back()
+#define INSPIRCD_INTRUSIVE_LIST_NAME intrusive_list_tail
+#define INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+#include "intrusive_list_impl.h"
+#undef INSPIRCD_INTRUSIVE_LIST_NAME
+#undef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
diff --git a/include/intrusive_list_impl.h b/include/intrusive_list_impl.h
new file mode 100644
index 000000000..1dd36b03a
--- /dev/null
+++ b/include/intrusive_list_impl.h
@@ -0,0 +1,172 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013-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/>.
+ */
+
+
+namespace insp
+{
+
+template <typename T, typename Tag>
+class INSPIRCD_INTRUSIVE_LIST_NAME
+{
+ public:
+ class iterator : public std::iterator<std::bidirectional_iterator_tag, T*>
+ {
+ T* curr;
+
+ public:
+ iterator(T* i = NULL)
+ : curr(i)
+ {
+ }
+
+ iterator& operator++()
+ {
+ curr = curr->intrusive_list_node<T, Tag>::ptr_next;
+ return *this;
+ }
+
+ iterator operator++(int)
+ {
+ iterator ret(*this);
+ operator++();
+ return ret;
+ }
+
+ iterator& operator--()
+ {
+ curr = curr->intrusive_list_node<T, Tag>::ptr_prev;
+ return *this;
+ }
+
+ iterator operator--(int)
+ {
+ iterator ret(*this);
+ operator--();
+ return ret;
+ }
+
+ bool operator==(const iterator& other) const { return (curr == other.curr); }
+ bool operator!=(const iterator& other) const { return (curr != other.curr); }
+ T* operator*() const { return curr; }
+ };
+
+ typedef iterator const_iterator;
+
+ INSPIRCD_INTRUSIVE_LIST_NAME()
+ : listhead(NULL)
+#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+ , listtail(NULL)
+#endif
+ , listsize(0)
+ {
+ }
+
+ bool empty() const
+ {
+ return (size() == 0);
+ }
+
+ size_t size() const
+ {
+ return listsize;
+ }
+
+ iterator begin() const
+ {
+ return iterator(listhead);
+ }
+
+ iterator end() const
+ {
+ return iterator();
+ }
+
+ void pop_front()
+ {
+ erase(listhead);
+ }
+
+ T* front() const
+ {
+ return listhead;
+ }
+
+ void push_front(T* x)
+ {
+ if (listsize++)
+ {
+ x->intrusive_list_node<T, Tag>::ptr_next = listhead;
+ listhead->intrusive_list_node<T, Tag>::ptr_prev = x;
+ }
+#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+ else
+ listtail = x;
+#endif
+ listhead = x;
+ }
+
+#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+ T* back() const
+ {
+ return listtail;
+ }
+
+ void push_back(T* x)
+ {
+ if (listsize++)
+ {
+ x->intrusive_list_node<T, Tag>::ptr_prev = listtail;
+ listtail->intrusive_list_node<T, Tag>::ptr_next = x;
+ }
+ else
+ listhead = x;
+ listtail = x;
+ }
+
+ void pop_back()
+ {
+ erase(listtail);
+ }
+#endif
+
+ void erase(const iterator& it)
+ {
+ erase(*it);
+ }
+
+ void erase(T* x)
+ {
+ if (listhead == x)
+ listhead = x->intrusive_list_node<T, Tag>::ptr_next;
+#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+ if (listtail == x)
+ listtail = x->intrusive_list_node<T, Tag>::ptr_prev;
+#endif
+ x->intrusive_list_node<T, Tag>::unlink();
+ listsize--;
+ }
+
+ private:
+ T* listhead;
+#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+ T* listtail;
+#endif
+ size_t listsize;
+};
+
+} // namespace insp
diff --git a/include/iohook.h b/include/iohook.h
index ce7ca2a1b..e99316b99 100644
--- a/include/iohook.h
+++ b/include/iohook.h
@@ -23,6 +23,8 @@ class StreamSocket;
class IOHookProvider : public ServiceProvider
{
+ const bool middlehook;
+
public:
enum Type
{
@@ -32,21 +34,31 @@ class IOHookProvider : public ServiceProvider
const Type type;
- IOHookProvider(Module* mod, const std::string& Name, Type hooktype = IOH_UNKNOWN)
- : ServiceProvider(mod, Name, SERVICE_IOHOOK), type(hooktype) { }
+ /** Constructor
+ * @param mod Module that owns the IOHookProvider
+ * @param Name Name of the provider
+ * @param hooktype One of IOHookProvider::Type
+ * @param middle True if the IOHook instances created by this hook are subclasses of IOHookMiddle, false otherwise
+ */
+ IOHookProvider(Module* mod, const std::string& Name, Type hooktype = IOH_UNKNOWN, bool middle = false)
+ : ServiceProvider(mod, Name, SERVICE_IOHOOK), middlehook(middle), type(hooktype) { }
+
+ /** Check if the IOHook provided can appear in the non-last position of a hook chain.
+ * That is the case if and only if the IOHook instances created are subclasses of IOHookMiddle.
+ * @return True if the IOHooks provided are subclasses of IOHookMiddle
+ */
+ bool IsMiddle() const { return middlehook; }
- /** Called immediately after a connection is accepted. This is intended for raw socket
- * processing (e.g. modules which wrap the tcp connection within another library) and provides
- * no information relating to a user record as the connection has not been assigned yet.
- * @param sock The socket in question
- * @param client The client IP address and port
- * @param server The server IP address and port
+ /** Called when the provider should hook an incoming connection and act as being on the server side of the connection.
+ * This occurs when a bind block has a hook configured and the listener accepts a connection.
+ * @param sock Socket to hook
+ * @param client Client IP address and port
+ * @param server Server IP address and port
*/
virtual void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) = 0;
- /** Called immediately upon connection of an outbound BufferedSocket which has been hooked
- * by a module.
- * @param sock The socket in question
+ /** Called when the provider should hook an outgoing connection and act as being on the client side of the connection.
+ * @param sock Socket to hook
*/
virtual void OnConnect(StreamSocket* sock) = 0;
};
@@ -59,31 +71,97 @@ class IOHook : public classbase
*/
IOHookProvider* const prov;
+ /** Constructor
+ * @param provider IOHookProvider that creates this object
+ */
IOHook(IOHookProvider* provider)
: prov(provider) { }
/**
- * Called when a hooked stream has data to write, or when the socket
- * engine returns it as writable
- * @param sock The socket in question
- * @param sendq Data to send to the socket
+ * Called when the hooked socket has data to write, or when the socket engine returns it as writable
+ * @param sock Hooked socket
+ * @param sendq Send queue to send data from
* @return 1 if the sendq has been completely emptied, 0 if there is
* still data to send, and -1 if there was an error
*/
- virtual int OnStreamSocketWrite(StreamSocket* sock, std::string& sendq) = 0;
+ virtual int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& sendq) = 0;
- /** Called immediately before any socket is closed. When this event is called, shutdown()
+ /** Called immediately before the hooked socket is closed. When this event is called, shutdown()
* has not yet been called on the socket.
- * @param sock The socket in question
+ * @param sock Hooked socket
*/
virtual void OnStreamSocketClose(StreamSocket* sock) = 0;
/**
- * Called when the stream socket has data to read
- * @param sock The socket that is ready
+ * Called when the hooked socket has data to read
+ * @param sock Hooked socket
* @param recvq The receive queue that new data should be appended to
* @return 1 if new data has been read, 0 if no new data is ready (but the
* socket is still connected), -1 if there was an error or close
*/
virtual int OnStreamSocketRead(StreamSocket* sock, std::string& recvq) = 0;
};
+
+class IOHookMiddle : public IOHook
+{
+ /** Data already processed by the IOHook waiting to go down the chain
+ */
+ StreamSocket::SendQueue sendq;
+
+ /** Data waiting to go up the chain
+ */
+ std::string precvq;
+
+ /** Next IOHook in the chain
+ */
+ IOHook* nexthook;
+
+ protected:
+ /** Get all queued up data which has not yet been passed up the hook chain
+ * @return RecvQ containing the data
+ */
+ std::string& GetRecvQ() { return precvq; }
+
+ /** Get all queued up data which is ready to go down the hook chain
+ * @return SendQueue containing all data waiting to go down the hook chain
+ */
+ StreamSocket::SendQueue& GetSendQ() { return sendq; }
+
+ public:
+ /** Constructor
+ * @param provider IOHookProvider that creates this object
+ */
+ IOHookMiddle(IOHookProvider* provider)
+ : IOHook(provider)
+ , nexthook(NULL)
+ {
+ }
+
+ /** Get all queued up data which is ready to go down the hook chain
+ * @return SendQueue containing all data waiting to go down the hook chain
+ */
+ const StreamSocket::SendQueue& GetSendQ() const { return sendq; }
+
+ /** Get the next IOHook in the chain
+ * @return Next hook in the chain or NULL if this is the last hook
+ */
+ IOHook* GetNextHook() const { return nexthook; }
+
+ /** Set the next hook in the chain
+ * @param hook Hook to set as the next hook in the chain
+ */
+ void SetNextHook(IOHook* hook) { nexthook = hook; }
+
+ /** Check if a hook is capable of being the non-last hook in a hook chain and if so, cast it to an IOHookMiddle object.
+ * @param hook IOHook to check
+ * @return IOHookMiddle referring to the same hook or NULL
+ */
+ static IOHookMiddle* ToMiddleHook(IOHook* hook)
+ {
+ if (hook->prov->IsMiddle())
+ return static_cast<IOHookMiddle*>(hook);
+ return NULL;
+ }
+
+ friend class StreamSocket;
+};
diff --git a/include/isupportmanager.h b/include/isupportmanager.h
new file mode 100644
index 000000000..3a0df78f9
--- /dev/null
+++ b/include/isupportmanager.h
@@ -0,0 +1,45 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 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/>.
+ */
+
+
+#pragma once
+
+/** This class manages the generation and transmission of ISUPPORT. */
+class CoreExport ISupportManager
+{
+ private:
+ /** The generated lines which are sent to clients. */
+ std::vector<Numeric::Numeric> cachedlines;
+
+ public:
+ /** (Re)build the ISUPPORT vector.
+ * Called by the core on boot after all modules have been loaded, and every time when a module is loaded
+ * or unloaded. Calls the On005Numeric hook, letting modules manipulate the ISUPPORT tokens.
+ */
+ void Build();
+
+ /** Returns the cached std::vector of ISUPPORT lines.
+ * @return A list of Numeric::Numeric objects prepared for sending to users
+ */
+ const std::vector<Numeric::Numeric>& GetLines() const { return cachedlines; }
+
+ /** Send the 005 numerics (ISUPPORT) to a user.
+ * @param user The user to send the ISUPPORT numerics to
+ */
+ void SendTo(LocalUser* user);
+};
diff --git a/include/listmode.h b/include/listmode.h
index 75385588b..94af1d524 100644
--- a/include/listmode.h
+++ b/include/listmode.h
@@ -37,7 +37,7 @@ class CoreExport ListModeBase : public ModeHandler
/** Items stored in the channel's list
*/
- typedef std::list<ListItem> ModeList;
+ typedef std::vector<ListItem> ModeList;
private:
class ChanData
@@ -149,13 +149,13 @@ class CoreExport ListModeBase : public ModeHandler
* this mode from the channel.
* See mode.h for more details.
* @param channel The channel to remove all instances of the mode from
- * @param stack The mode stack to add the mode change to
+ * @param changelist Mode change list to populate with the removal of this mode
*/
- virtual void RemoveMode(Channel* channel, irc::modestacker& stack);
+ virtual void RemoveMode(Channel* channel, Modes::ChangeList& changelist);
/** Perform a rehash of this mode's configuration data
*/
- virtual void DoRehash();
+ void DoRehash();
/** Handle the list mode.
* See mode.h
diff --git a/include/logger.h b/include/logger.h
index 2ea280be8..5d4a80d9f 100644
--- a/include/logger.h
+++ b/include/logger.h
@@ -41,14 +41,18 @@ class CoreExport FileWriter
*/
FILE* log;
+ /** The number of write operations after which we should flush.
+ */
+ unsigned int flush;
+
/** Number of write operations that have occured
*/
- int writeops;
+ unsigned int writeops;
public:
/** The constructor takes an already opened logfile.
*/
- FileWriter(FILE* logfile);
+ FileWriter(FILE* logfile, unsigned int flushcount);
/** Write one or more preformatted log lines.
* If the data cannot be written immediately,
@@ -114,7 +118,7 @@ class CoreExport LogStream : public classbase
typedef std::map<FileWriter*, int> FileLogMap;
-class CoreExport LogManager
+class CoreExport LogManager : public fakederef<LogManager>
{
private:
/** Lock variable, set to true when a log is in progress, which prevents further loggging from happening and creating a loop.
diff --git a/include/membership.h b/include/membership.h
index e2c13c7e4..c952d09ae 100644
--- a/include/membership.h
+++ b/include/membership.h
@@ -20,6 +20,8 @@
#pragma once
+uint64_t ConvToUInt64(const std::string& in);
+
/**
* Represents a member of a channel.
* A Membership object is created when a user joins a channel, and destroyed when a user leaves
@@ -27,9 +29,13 @@
* All prefix modes a member has is tracked by this object. Moreover, Memberships are Extensibles
* meaning modules can add arbitrary data to them using extensions (see m_delaymsg for an example).
*/
-class CoreExport Membership : public Extensible, public intrusive_list_node<Membership>
+class CoreExport Membership : public Extensible, public insp::intrusive_list_node<Membership>
{
public:
+ /** Type of the Membership id
+ */
+ typedef uint64_t Id;
+
/** User on the channel
*/
User* const user;
@@ -43,6 +49,20 @@ class CoreExport Membership : public Extensible, public intrusive_list_node<Memb
*/
std::string modes;
+ /** Id of this Membership, set by the protocol module, other components should never read or
+ * write this field.
+ */
+ Id id;
+
+ /** Converts a string to a Membership::Id
+ * @param str The string to convert
+ * @return Raw value of type Membership::Id
+ */
+ static Id IdFromString(const std::string& str)
+ {
+ return ConvToUInt64(str);
+ }
+
/** Constructor, sets the user and chan fields to the parameters, does NOT update any bookkeeping
* information in the User or the Channel.
* Call Channel::JoinUser() or ForceJoin() to make a user join a channel instead of constructing
@@ -50,13 +70,13 @@ class CoreExport Membership : public Extensible, public intrusive_list_node<Memb
*/
Membership(User* u, Channel* c) : user(u), chan(c) {}
- /** Returns true if this member has a given prefix mode set
- * @param m The prefix mode letter to check
+ /** Check if this member has a given prefix mode set
+ * @param pm Prefix mode to check
* @return True if the member has the prefix mode set, false otherwise
*/
- inline bool hasMode(char m) const
+ bool HasMode(const PrefixMode* pm) const
{
- return modes.find(m) != std::string::npos;
+ return (modes.find(pm->GetModeChar()) != std::string::npos);
}
/** Returns the rank of this member.
@@ -90,88 +110,5 @@ class CoreExport Membership : public Extensible, public intrusive_list_node<Memb
* be in rank order, greatest first, as certain IRC clients require
* this when multiple prefixes are used names lists.
*/
- const char* GetAllPrefixChars() const;
-};
-
-template <typename T>
-class InviteBase
-{
- protected:
- /** List of pending Invitations
- */
- intrusive_list<Invitation, T> invites;
-
- public:
- /** Remove and destruct all pending invitations this user or channel has.
- * Must be called before the object is destroyed, also called when the TS of the channel is lowered.
- */
- void ClearInvites();
-
- friend class Invitation;
-};
-
-/**
- * The Invitation class contains all data about a pending invitation.
- * Invitation objects are referenced from the user and the channel they belong to.
- */
-class CoreExport Invitation : public intrusive_list_node<Invitation, Channel>, public intrusive_list_node<Invitation, LocalUser>
-{
- /** Constructs an Invitation, only called by Create()
- * @param c Channel the user is invited to
- * @param u User being invited
- * @param timeout Expiration time for this Invitation
- */
- Invitation(Channel* c, LocalUser* u, time_t timeout) : user(u), chan(c), expiry(timeout) {}
-
- public:
- /** User the invitation is for
- */
- LocalUser* const user;
-
- /** Channel where the user is invited to
- */
- Channel* const chan;
-
- /** Timestamp when this Invitation expires or 0 if it doesn't expire.
- * Invitation::Create() can update this field; see that for more info.
- */
- time_t expiry;
-
- /** Destructor
- * Removes references to this Invitation from the associated user and channel.
- */
- ~Invitation();
-
- /** Create or extend an Invitation.
- * When a user is invited to join a channel either a new Invitation object is created or
- * or the expiration timestamp is updated if there is already a pending Invitation for
- * the given (user, channel) pair and the new expiration time is further than the current.
- * @param c Target channel
- * @param u Target user
- * @param timeout Timestamp when the invite should expire, 0 for no expiration
- */
- static void Create(Channel* c, LocalUser* u, time_t timeout);
-
- /** Finds the Invitation object for the given channel/user pair.
- * @param c Target channel, can be NULL to remove expired entries
- * @param u Target user, cannot be NULL
- * @param check_expired Pass true to remove all expired invites found while searching, false
- * to return with an Invitation even if it's expired
- * @return Invitation object for the given (channel, user) pair if it exists, NULL otherwise
- */
- static Invitation* Find(Channel* c, LocalUser* u, bool check_expired = true);
+ std::string GetAllPrefixChars() const;
};
-
-typedef intrusive_list<Invitation, LocalUser> InviteList;
-
-template<typename T>
-inline void InviteBase<T>::ClearInvites()
-{
- for (typename intrusive_list<Invitation, T>::iterator i = invites.begin(); i != invites.end(); )
- {
- Invitation* inv = *i;
- // Destructing the Invitation invalidates the iterator, so move it now
- ++i;
- delete inv;
- }
-}
diff --git a/include/mode.h b/include/mode.h
index 7c5682135..956b86050 100644
--- a/include/mode.h
+++ b/include/mode.h
@@ -23,6 +23,7 @@
#pragma once
#include "ctables.h"
+#include "modechange.h"
/**
* Holds the values for different type of modes
@@ -110,11 +111,6 @@ class CoreExport ModeHandler : public ServiceProvider
Id modeid;
protected:
- /**
- * The mode parameter translation type
- */
- TranslateType m_paramtype;
-
/** What kind of parameters does the mode take?
*/
ParamSpec parameters_taken;
@@ -172,6 +168,11 @@ class CoreExport ModeHandler : public ServiceProvider
ModeHandler(Module* me, const std::string& name, char modeletter, ParamSpec params, ModeType type, Class mclass = MC_OTHER);
virtual CullResult cull();
virtual ~ModeHandler();
+
+ /** Register this object in the ModeParser
+ */
+ void RegisterService() CXX11_OVERRIDE;
+
/**
* Returns true if the mode is a list mode
*/
@@ -184,6 +185,12 @@ class CoreExport ModeHandler : public ServiceProvider
PrefixMode* IsPrefixMode();
/**
+ * Check whether this mode is a prefix mode
+ * @return non-NULL if this mode is a prefix mode, NULL otherwise
+ */
+ const PrefixMode* IsPrefixMode() const;
+
+ /**
* Check whether this mode handler inherits from ListModeBase
* @return non-NULL if this mode handler inherits from ListModeBase, NULL otherwise
*/
@@ -191,34 +198,41 @@ class CoreExport ModeHandler : public ServiceProvider
/**
* Check whether this mode handler inherits from ListModeBase
+ * @return non-NULL if this mode handler inherits from ListModeBase, NULL otherwise
+ */
+ const ListModeBase* IsListModeBase() const;
+
+ /**
+ * Check whether this mode handler inherits from ParamModeBase
* @return non-NULL if this mode handler inherits from ParamModeBase, NULL otherwise
*/
ParamModeBase* IsParameterMode();
/**
- * Returns the mode's type
+ * Check whether this mode handler inherits from ParamModeBase
+ * @return non-NULL if this mode handler inherits from ParamModeBase, NULL otherwise
*/
- inline ModeType GetModeType() const { return m_type; }
+ const ParamModeBase* IsParameterMode() const;
+
/**
- * Returns the mode's parameter translation type
+ * Returns the mode's type
*/
- inline TranslateType GetTranslateType() const { return m_paramtype; }
+ inline ModeType GetModeType() const { return m_type; }
/**
* Returns true if the mode can only be set/unset by an oper
*/
inline bool NeedsOper() const { return oper; }
/**
- * Returns the number of parameters for the mode. Any non-zero
- * value should be considered to be equivalent to one.
- * @param adding If this is true, the number of parameters required to set the mode should be returned, otherwise the number of parameters required to unset the mode shall be returned.
- * @return The number of parameters the mode expects
+ * Check if the mode needs a parameter for adding or removing
+ * @param adding True to check if the mode needs a parameter when setting, false to check if the mode needs a parameter when unsetting
+ * @return True if the mode needs a parameter for the specified action, false if it doesn't
*/
- int GetNumParams(bool adding);
+ bool NeedsParam(bool adding) const;
/**
* Returns the mode character this handler handles.
* @return The mode character
*/
- inline char GetModeChar() { return mode; }
+ char GetModeChar() const { return mode; }
/** Return the id of this mode which is used in User::modes and
* Channel::modes as the index to determine whether a mode is set.
@@ -227,7 +241,7 @@ class CoreExport ModeHandler : public ServiceProvider
/** For user modes, return the current parameter, if any
*/
- virtual std::string GetUserParameter(User* useor);
+ virtual std::string GetUserParameter(const User* user) const;
/**
* Called when a channel mode change access check for your mode occurs.
@@ -302,9 +316,9 @@ class CoreExport ModeHandler : public ServiceProvider
* so if you inherit from it or your mode can be removed by the default implementation then you do not have to implement
* this function).
* @param channel The channel which the server wants to remove your mode from
- * @param stack The mode stack to add the mode change to
+ * @param changelist Mode change list to populate with the removal of this mode
*/
- virtual void RemoveMode(Channel* channel, irc::modestacker& stack);
+ virtual void RemoveMode(Channel* channel, Modes::ChangeList& changelist);
inline unsigned int GetLevelRequired() const { return levelrequired; }
@@ -344,8 +358,10 @@ class CoreExport PrefixMode : public ModeHandler
* @param Creator The module creating this mode
* @param Name The user-friendly one word name of the prefix mode, e.g.: "op", "voice"
* @param ModeLetter The mode letter of this mode
+ * @param Rank Rank given by this prefix mode, see explanation above
+ * @param PrefixChar Prefix character, or 0 if the mode has no prefix character
*/
- PrefixMode(Module* Creator, const std::string& Name, char ModeLetter);
+ PrefixMode(Module* Creator, const std::string& Name, char ModeLetter, unsigned int Rank = 0, char PrefixChar = 0);
/**
* Handles setting and unsetting the prefix mode.
@@ -365,9 +381,9 @@ class CoreExport PrefixMode : public ModeHandler
/**
* Removes this prefix mode from all users on the given channel
* @param chan The channel which the server wants to remove your mode from
- * @param stack The mode stack to add the mode change to
+ * @param changelist Mode change list to populate with the removal of this mode
*/
- void RemoveMode(Channel* chan, irc::modestacker& stack);
+ void RemoveMode(Channel* channel, Modes::ChangeList& changelist);
/**
* Mode prefix or 0. If this is defined, you should
@@ -452,7 +468,7 @@ class CoreExport ModeWatcher : public classbase
* Get the mode type being watched
* @return The mode type being watched (user or channel)
*/
- ModeType GetModeType();
+ ModeType GetModeType() const { return m_type; }
/**
* Before the mode character is processed by its handler, this method will be called.
@@ -479,14 +495,12 @@ class CoreExport ModeWatcher : public classbase
virtual void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding);
};
-typedef std::multimap<std::string, ModeWatcher*>::iterator ModeWatchIter;
-
/** The mode parser handles routing of modes and handling of mode strings.
* It marshalls, controls and maintains both ModeWatcher and ModeHandler classes,
* parses client to server MODE strings for user and channel modes, and performs
* processing for the 004 mode list numeric, amongst other things.
*/
-class CoreExport ModeParser
+class CoreExport ModeParser : public fakederef<ModeParser>
{
public:
static const ModeHandler::Id MODEID_MAX = 64;
@@ -496,6 +510,10 @@ class CoreExport ModeParser
typedef TR1NS::unordered_map<std::string, ModeHandler*, irc::insensitive, irc::StrHashComp> ModeHandlerMap;
private:
+ /** Type of the container that maps mode names to ModeWatchers
+ */
+ typedef insp::flat_multimap<std::string, ModeWatcher*> ModeWatcherMap;
+
/** Last item in the ModeType enum
*/
static const unsigned int MODETYPE_LAST = 2;
@@ -530,21 +548,16 @@ class CoreExport ModeParser
/** Mode watcher classes
*/
- std::multimap<std::string, ModeWatcher*> modewatchermap;
+ ModeWatcherMap modewatchermap;
- /** Displays the current modes of a channel or user.
- * Used by ModeParser::Process.
- */
- void DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text);
- /** Displays the value of a list mode
- * Used by ModeParser::Process.
+ /** Last processed mode change
*/
- void DisplayListModes(User* user, Channel* chan, std::string &mode_sequence);
+ Modes::ChangeList LastChangeList;
/**
* Attempts to apply a mode change to a user or channel
*/
- ModeAction TryMode(User* user, User* targu, Channel* targc, bool adding, unsigned char mode, std::string &param, bool SkipACL);
+ ModeAction TryMode(User* user, User* targu, Channel* targc, Modes::Change& mcitem, bool SkipACL);
/** Returns a list of user or channel mode characters.
* Used for constructing the parts of the mode list in the 004 numeric.
@@ -570,12 +583,6 @@ class CoreExport ModeParser
* Use GetLastParse() to get this value, to be used for display purposes.
*/
std::string LastParse;
- std::vector<std::string> LastParseParams;
- std::vector<TranslateType> LastParseTranslate;
-
- unsigned int sent[256];
-
- unsigned int seq;
/** Cached mode list for use in 004 numeric
*/
@@ -601,11 +608,19 @@ class CoreExport ModeParser
*/
MODE_MERGE = 1,
- /** If this flag is set then the mode change won't be handed over to
- * the linking module to be sent to other servers, but will be processed
+ /** If this flag is set then the linking module will ignore the mode change
+ * and not send it to other servers. The mode change will be processed
* locally and sent to local user(s) as usual.
*/
- MODE_LOCALONLY = 2
+ MODE_LOCALONLY = 2,
+
+ /** If this flag is set then the mode change will be subject to access checks.
+ * For more information see the documentation of the PrefixMode class,
+ * ModeHandler::levelrequired and ModeHandler::AccessCheck().
+ * Modules may explicitly allow a mode change regardless of this flag by returning
+ * MOD_RES_ALLOW from the OnPreMode hook. Only affects channel mode changes.
+ */
+ MODE_CHECKACCESS = 4
};
ModeParser();
@@ -635,8 +650,6 @@ class CoreExport ModeParser
* @return Last parsed string, as seen by users.
*/
const std::string& GetLastParse() const { return LastParse; }
- const std::vector<std::string>& GetLastParseParams() { return LastParseParams; }
- const std::vector<TranslateType>& GetLastParseTranslate() { return LastParseTranslate; }
/** Add a mode to the mode parser.
* Throws a ModuleException if the mode cannot be added.
@@ -669,14 +682,49 @@ class CoreExport ModeParser
* @return True if the ModeWatcher was deleted correctly
*/
bool DelModeWatcher(ModeWatcher* mw);
- /** Process a set of mode changes from a server or user.
- * @param parameters The parameters of the mode change, in the format
- * they would be from a MODE command.
+
+ /** Process a list of mode changes entirely. If the mode changes do not fit into one MODE line
+ * then multiple MODE lines are generated.
* @param user The source of the mode change, can be a server user.
+ * @param targetchannel Channel to apply the mode change on. NULL if changing modes on a channel.
+ * @param targetuser User to apply the mode change on. NULL if changing modes on a user.
+ * @param changelist Modes to change in form of a Modes::ChangeList.
* @param flags Optional flags controlling how the mode change is processed,
* defaults to MODE_NONE.
*/
- void Process(const std::vector<std::string>& parameters, User* user, ModeProcessFlag flags = MODE_NONE);
+ void Process(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags = MODE_NONE);
+
+ /** Process a single MODE line's worth of mode changes, taking max modes and line length limits
+ * into consideration. Return value indicates how many modes were processed.
+ * @param user The source of the mode change, can be a server user.
+ * @param targetchannel Channel to apply the mode change on. NULL if changing modes on a channel.
+ * @param targetuser User to apply the mode change on. NULL if changing modes on a user.
+ * @param changelist Modes to change in form of a Modes::ChangeList. May not process
+ * the entire list due to MODE line length and max modes limitations.
+ * @param flags Optional flags controlling how the mode change is processed,
+ * defaults to MODE_NONE.
+ * @param beginindex Index of the first element in changelist to process. Mode changes before
+ * the element with this index are ignored.
+ * @return Number of mode changes processed from changelist.
+ */
+ unsigned int ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags = MODE_NONE, unsigned int beginindex = 0);
+
+ /** Turn a list of parameters compatible with the format of the MODE command into
+ * Modes::ChangeList form. All modes are processed, regardless of max modes. Unknown modes
+ * are skipped.
+ * @param user The source of the mode change, can be a server user. Error numerics are sent to
+ * this user.
+ * @param type MODETYPE_USER if this is a user mode change or MODETYPE_CHANNEL if this
+ * is a channel mode change.
+ * @param parameters List of strings describing the mode change to convert to a ChangeList.
+ * Must be using the same format as the parameters of a MODE command.
+ * @param changelist ChangeList object to populate.
+ * @param beginindex Index of the first element that is part of the MODE list in the parameters
+ * container. Defaults to 1.
+ * @param endindex Index of the first element that is not part of the MODE list. By default,
+ * the entire container is considered part of the MODE list.
+ */
+ void ModeParamsToChangeList(User* user, ModeType type, const std::vector<std::string>& parameters, Modes::ChangeList& changelist, unsigned int beginindex = 1, unsigned int endindex = UINT_MAX);
/** Find the mode handler for a given mode name and type.
* @param modename The mode name to search for.
@@ -741,6 +789,13 @@ class CoreExport ModeParser
* @return A map of mode handlers of the given type
*/
const ModeHandlerMap& GetModes(ModeType mt) const { return modehandlersbyname[mt]; }
+
+ /** Show the list of a list mode to a user. Modules can deny the listing.
+ * @param user User to show the list to.
+ * @param chan Channel to show the list of.
+ * @param mh List mode to show the list of.
+ */
+ void ShowListModeList(User* user, Channel* chan, ModeHandler* mh);
};
inline const std::string& ModeParser::GetModeListFor004Numeric()
@@ -753,12 +808,27 @@ inline PrefixMode* ModeHandler::IsPrefixMode()
return (this->type_id == MC_PREFIX ? static_cast<PrefixMode*>(this) : NULL);
}
+inline const PrefixMode* ModeHandler::IsPrefixMode() const
+{
+ return (this->type_id == MC_PREFIX ? static_cast<const PrefixMode*>(this) : NULL);
+}
+
inline ListModeBase* ModeHandler::IsListModeBase()
{
return (this->type_id == MC_LIST ? reinterpret_cast<ListModeBase*>(this) : NULL);
}
+inline const ListModeBase* ModeHandler::IsListModeBase() const
+{
+ return (this->type_id == MC_LIST ? reinterpret_cast<const ListModeBase*>(this) : NULL);
+}
+
inline ParamModeBase* ModeHandler::IsParameterMode()
{
return (this->type_id == MC_PARAM ? reinterpret_cast<ParamModeBase*>(this) : NULL);
}
+
+inline const ParamModeBase* ModeHandler::IsParameterMode() const
+{
+ return (this->type_id == MC_PARAM ? reinterpret_cast<const ParamModeBase*>(this) : NULL);
+}
diff --git a/include/modechange.h b/include/modechange.h
new file mode 100644
index 000000000..e20665790
--- /dev/null
+++ b/include/modechange.h
@@ -0,0 +1,110 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+namespace Modes
+{
+ struct Change;
+ class ChangeList;
+}
+
+/** A single mode to be changed
+ */
+struct Modes::Change
+{
+ bool adding;
+ ModeHandler* mh;
+ std::string param;
+
+ /**
+ * @param handler Mode handler
+ * @param add True if this mode is being set, false if removed
+ * @param parameter Mode parameter
+ */
+ Change(ModeHandler* handler, bool add, const std::string& parameter)
+ : adding(add)
+ , mh(handler)
+ , param(parameter)
+ {
+ }
+};
+
+/** A list of mode changes that can be applied on a Channel or User
+ */
+class Modes::ChangeList
+{
+ public:
+ typedef std::vector<Change> List;
+
+ /** Add a new mode to be changed to this ChangeList
+ * @param handler Mode handler
+ * @param add True if this mode is being set, false if removed
+ * @param parameter Mode parameter
+ */
+ void push(ModeHandler* mh, bool adding, const std::string& param = std::string())
+ {
+ items.push_back(Change(mh, adding, param));
+ }
+
+ /** Add a new mode to this ChangeList which will be set on the target
+ * @param handler Mode handler
+ * @param parameter Mode parameter
+ */
+ void push_add(ModeHandler* mh, const std::string& param = std::string())
+ {
+ push(mh, true, param);
+ }
+
+ /** Add a new mode to this ChangeList which will be unset from the target
+ * @param handler Mode handler
+ * @param parameter Mode parameter
+ */
+ void push_remove(ModeHandler* mh, const std::string& param = std::string())
+ {
+ push(mh, false, param);
+ }
+
+ /** Remove all mode changes from this stack
+ */
+ void clear() { items.clear(); }
+
+ /** Checks whether the ChangeList is empty, equivalent to (size() != 0).
+ * @return True if the ChangeList is empty, false otherwise.
+ */
+ bool empty() const { return items.empty(); }
+
+ /** Get number of mode changes in this ChangeList
+ * @return Number of mode changes in this ChangeList
+ */
+ List::size_type size() const { return items.size(); }
+
+ /** Get the list of mode changes in this ChangeList
+ * @return List of modes added to this ChangeList
+ */
+ const List& getlist() const { return items; }
+
+ /** Get the list of mode changes in this ChangeList
+ * @return List of modes added to this ChangeList
+ */
+ List& getlist() { return items; }
+
+ private:
+ List items;
+};
diff --git a/include/modules.h b/include/modules.h
index a9db6919c..5deed943a 100644
--- a/include/modules.h
+++ b/include/modules.h
@@ -39,7 +39,6 @@
*/
enum ModuleFlags {
VF_NONE = 0, // module is not special at all
- VF_STATIC = 1, // module is static, cannot be /unloadmodule'd
VF_VENDOR = 2, // module is a vendor module (came in the original tarball, not 3rd party)
VF_COMMON = 4, // module needs to be common on all servers in a network to link
VF_OPTCOMMON = 8, // module should be common on all servers for unsurprising behavior
@@ -202,36 +201,6 @@ class CoreExport Version
/** Complex version information, including linking compatability data */
Version(const std::string &desc, int flags, const std::string& linkdata);
-
- virtual ~Version() {}
-};
-
-/** The Event class is a unicast message directed at all modules.
- * When the class is properly instantiated it may be sent to all modules
- * using the Send() method, which will trigger the OnEvent method in
- * all modules passing the object as its parameter.
- */
-class CoreExport Event : public classbase
-{
- public:
- /** This is a pointer to the sender of the message, which can be used to
- * directly trigger events, or to create a reply.
- */
- ModuleRef source;
- /** The event identifier.
- * This is arbitary text which should be used to distinguish
- * one type of event from another.
- */
- const std::string id;
-
- /** Create a new Event
- */
- Event(Module* src, const std::string &eventid);
- /** Send the Event.
- * The return result of an Event::Send() will always be NULL as
- * no replies are expected.
- */
- void Send();
};
class CoreExport DataProvider : public ServiceProvider
@@ -249,9 +218,8 @@ enum Priority { PRIORITY_FIRST, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER }
*/
enum Implementation
{
- I_BEGIN,
I_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart,
- I_OnSendSnotice, I_OnUserPreJoin, I_OnUserPreKick, I_OnUserKick, I_OnOper, I_OnInfo, I_OnWhois,
+ I_OnSendSnotice, I_OnUserPreJoin, I_OnUserPreKick, I_OnUserKick, I_OnOper, I_OnInfo,
I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNick,
I_OnUserMessage, I_OnMode, I_OnSyncUser,
I_OnSyncChannel, I_OnDecodeMetaData, I_OnAcceptConnection, I_OnUserInit,
@@ -260,10 +228,10 @@ enum Implementation
I_OnUnloadModule, I_OnBackgroundTimer, I_OnPreCommand, I_OnCheckReady, I_OnCheckInvite,
I_OnRawMode, I_OnCheckKey, I_OnCheckLimit, I_OnCheckBan, I_OnCheckChannelBan, I_OnExtBanCheck,
I_OnStats, I_OnChangeLocalUserHost, I_OnPreTopicChange,
- I_OnPostTopicChange, I_OnEvent, I_OnGlobalOper, I_OnPostConnect,
+ I_OnPostTopicChange, I_OnPostConnect,
I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete,
I_OnPostOper, I_OnSyncNetwork, I_OnSetAway, I_OnPostCommand, I_OnPostJoin,
- I_OnWhoisLine, I_OnBuildNeighborList, I_OnGarbageCollect, I_OnSetConnectClass,
+ I_OnBuildNeighborList, I_OnGarbageCollect, I_OnSetConnectClass,
I_OnText, I_OnPassCompare, I_OnNamesListItem, I_OnNumeric,
I_OnPreRehash, I_OnModuleRehash, I_OnSendWhoLine, I_OnChangeIdent, I_OnSetUserIP,
I_END
@@ -498,14 +466,6 @@ class CoreExport Module : public classbase, public usecountbase
*/
virtual void OnInfo(User* user);
- /** Called whenever a /WHOIS is performed on a local user.
- * The source parameter contains the details of the user who issued the WHOIS command, and
- * the dest parameter contains the information of the user they are whoising.
- * @param source The user issuing the WHOIS command
- * @param dest The user who is being WHOISed
- */
- virtual void OnWhois(User* source, User* dest);
-
/** Called whenever a user is about to invite another user into a channel, before any processing is done.
* Returning 1 from this function stops the process immediately, causing no
* output to be sent to the user by the core. If you do this you must produce your own numerics,
@@ -525,8 +485,10 @@ class CoreExport Module : public classbase, public usecountbase
* @param dest The user being invited
* @param channel The channel the user is being invited to
* @param timeout The time the invite will expire (0 == never)
+ * @param notifyrank Rank required to get an invite announcement (if enabled)
+ * @param notifyexcepts List of users to not send the default NOTICE invite announcement to
*/
- virtual void OnUserInvite(User* source,User* dest,Channel* channel, time_t timeout);
+ virtual void OnUserInvite(User* source, User* dest, Channel* channel, time_t timeout, unsigned int notifyrank, CUList& notifyexcepts);
/** Called whenever a user is about to PRIVMSG A user or a channel, before any processing is done.
* Returning any nonzero value from this function stops the process immediately, causing no
@@ -558,17 +520,14 @@ class CoreExport Module : public classbase, public usecountbase
*/
virtual void OnBuildNeighborList(User* source, IncludeChanList& include_c, std::map<User*, bool>& exceptions);
- /** Called before any nickchange, local or remote. This can be used to implement Q-lines etc.
- * Please note that although you can see remote nickchanges through this function, you should
- * NOT make any changes to the User if the user is a remote user as this may cause a desnyc.
- * check user->server before taking any action (including returning nonzero from the method).
+ /** Called before local nickname changes. This can be used to implement Q-lines etc.
* If your method returns nonzero, the nickchange is silently forbidden, and it is down to your
* module to generate some meaninful output.
* @param user The username changing their nick
* @param newnick Their new nickname
* @return 1 to deny the change, 0 to allow
*/
- virtual ModResult OnUserPreNick(User* user, const std::string &newnick);
+ virtual ModResult OnUserPreNick(LocalUser* user, const std::string& newnick);
/** Called after any PRIVMSG sent from a user.
* The dest variable contains a User* if target_type is TYPE_USER and a Channel*
@@ -601,15 +560,16 @@ class CoreExport Module : public classbase, public usecountbase
/** Called after every MODE command sent from a user
* Either the usertarget or the chantarget variable contains the target of the modes,
* the actual target will have a non-NULL pointer.
- * The modes vector contains the remainder of the mode string after the target,
- * e.g.: "+wsi" or ["+ooo", "nick1", "nick2", "nick3"].
+ * All changed modes are available in the changelist object.
* @param user The user sending the MODEs
* @param usertarget The target user of the modes, NULL if the target is a channel
* @param chantarget The target channel of the modes, NULL if the target is a user
- * @param modes The actual modes and their parameters if any
- * @param translate The translation types of the mode parameters
+ * @param changelist The changed modes.
+ * @param processflags Flags passed to ModeParser::Process(), see ModeParser::ModeProcessFlags
+ * for the possible flags.
+ * @param output_mode Changed modes, including '+' and '-' characters, not including any parameters
*/
- virtual void OnMode(User* user, User* usertarget, Channel* chantarget, const std::vector<std::string>& modes, const std::vector<TranslateType>& translate);
+ virtual void OnMode(User* user, User* usertarget, Channel* chantarget, const Modes::ChangeList& changelist, ModeParser::ModeProcessFlag processflags, const std::string& output_mode);
/** Allows modules to synchronize data which relates to users during a netburst.
* When this function is called, it will be called from the module which implements
@@ -711,7 +671,7 @@ class CoreExport Module : public classbase, public usecountbase
*/
virtual void OnUserPostNick(User* user, const std::string &oldnick);
- /** Called before any mode change, to allow a single access check for
+ /** Called before a mode change via the MODE command, to allow a single access check for
* a full mode change (use OnRawMode to check individual modes)
*
* Returning MOD_RES_ALLOW will skip prefix level checks, but can be overridden by
@@ -720,9 +680,9 @@ class CoreExport Module : public classbase, public usecountbase
* @param source the user making the mode change
* @param dest the user destination of the umode change (NULL if a channel mode)
* @param channel the channel destination of the mode change
- * @param parameters raw mode parameters; parameters[0] is the user/channel being changed
+ * @param modes Modes being changed, can be edited
*/
- virtual ModResult OnPreMode(User* source, User* dest, Channel* channel, const std::vector<std::string>& parameters);
+ virtual ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes);
/** Called when a 005 numeric is about to be output.
* The module should modify the 005 numeric if needed to indicate its features.
@@ -911,14 +871,10 @@ class CoreExport Module : public classbase, public usecountbase
/** Called on all /STATS commands
* This method is triggered for all /STATS use, including stats symbols handled by the core.
- * @param symbol the symbol provided to /STATS
- * @param user the user issuing the /STATS command
- * @param results A string_list to append results into. You should put all your results
- * into this string_list, rather than displaying them directly, so that your handler will
- * work when remote STATS queries are received.
+ * @param stats Context of the /STATS request, contains requesting user, list of answer rows etc.
* @return 1 to block the /STATS from being processed by the core, 0 to allow it
*/
- virtual ModResult OnStats(char symbol, User* user, string_list &results);
+ virtual ModResult OnStats(Stats::Context& stats);
/** Called whenever a change of a local users displayed host is attempted.
* Return 1 to deny the host change, or 0 to allow it.
@@ -954,12 +910,6 @@ class CoreExport Module : public classbase, public usecountbase
*/
virtual void OnPostTopicChange(User* user, Channel* chan, const std::string &topic);
- /** Called whenever an Event class is sent to all modules by another module.
- * You should *always* check the value of Event::id to determine the event type.
- * @param event The Event class being received
- */
- virtual void OnEvent(Event& event);
-
/** Called whenever a password check is to be made. Replaces the old OldOperCompare API.
* The password field (from the config file) is in 'password' and is to be compared against
* 'input'. This method allows for encryption of passwords (oper, connect:allow, die/restart, etc).
@@ -972,14 +922,6 @@ class CoreExport Module : public classbase, public usecountbase
*/
virtual ModResult OnPassCompare(Extensible* ex, const std::string &password, const std::string &input, const std::string& hashtype);
- /** Called whenever a user is given usermode +o, anywhere on the network.
- * You cannot override this and prevent it from happening as it is already happened and
- * such a task must be performed by another server. You can however bounce modes by sending
- * servermodes out to reverse mode changes.
- * @param user The user who is opering
- */
- virtual void OnGlobalOper(User* user);
-
/** Called after a user has fully connected and all modules have executed OnUserConnect
* This event is informational only. You should not change any user information in this
* event. To do so, use the OnUserConnect method to change the state of local users.
@@ -1007,19 +949,6 @@ class CoreExport Module : public classbase, public usecountbase
*/
virtual ModResult OnSetAway(User* user, const std::string &awaymsg);
- /** Called whenever a line of WHOIS output is sent to a user.
- * You may change the numeric and the text of the output by changing
- * the values numeric and text, but you cannot change the user the
- * numeric is sent to. You may however change the user's User values.
- * @param user The user the numeric is being sent to
- * @param dest The user being WHOISed
- * @param numeric The numeric of the line being sent
- * @param text The text of the numeric, including any parameters
- * @return nonzero to drop the line completely so that the user does not
- * receive it, or zero to allow the line to be sent.
- */
- virtual ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text);
-
/** Called at intervals for modules to garbage-collect any hashes etc.
* Certain data types such as hash_map 'leak' buckets, which must be
* tidied up and freed by copying into a new item every so often. This
@@ -1052,16 +981,17 @@ class CoreExport Module : public classbase, public usecountbase
*/
virtual ModResult OnNamesListItem(User* issuer, Membership* item, std::string& prefixes, std::string& nick);
- virtual ModResult OnNumeric(User* user, unsigned int numeric, const std::string &text);
+ virtual ModResult OnNumeric(User* user, const Numeric::Numeric& numeric);
/** Called whenever a result from /WHO is about to be returned
* @param source The user running the /WHO query
* @param params The parameters to the /WHO query
* @param user The user that this line of the query is about
* @param memb The member shown in this line, NULL if no channel is in this line
- * @param line The raw line to send; modifiable, if empty no line will be returned.
+ * @param numeric Numeric to send; modifiable.
+ * @param Return MOD_RES_PASSTHRU to allow the line to be displayed, MOD_RES_DENY to hide it
*/
- virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line);
+ virtual ModResult OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, Numeric::Numeric& numeric);
/** Called whenever a local user's IP is set for the first time, or when a local user's IP changes due to
* a module like m_cgiirc changing it.
@@ -1074,14 +1004,10 @@ class CoreExport Module : public classbase, public usecountbase
*/
typedef std::vector<Module*> IntModuleList;
-/** An event handler iterator
- */
-typedef IntModuleList::iterator EventHandlerIter;
-
/** ModuleManager takes care of all things module-related
* in the core.
*/
-class CoreExport ModuleManager
+class CoreExport ModuleManager : public fakederef<ModuleManager>
{
public:
typedef std::vector<ServiceProvider*> ServiceList;
@@ -1102,9 +1028,6 @@ class CoreExport ModuleManager
PRIO_STATE_LAST
} prioritizationState;
- /** Internal unload module hook */
- bool CanUnload(Module*);
-
/** Loads all core modules (cmd_*)
*/
void LoadCoreModules(std::map<std::string, ServiceList>& servicemap);
@@ -1114,6 +1037,12 @@ class CoreExport ModuleManager
*/
bool PrioritizeHooks();
+ /** Unregister all user modes or all channel modes owned by a module
+ * @param mod Module whose modes to unregister
+ * @param modetype MODETYPE_USER to unregister user modes, MODETYPE_CHANNEL to unregister channel modes
+ */
+ void UnregisterModes(Module* mod, ModeType modetype);
+
public:
typedef std::map<std::string, Module*> ModuleMap;
@@ -1135,6 +1064,13 @@ class CoreExport ModuleManager
*/
ServiceList* NewServices;
+ /** Expands the name of a module by prepending "m_" and appending ".so".
+ * No-op if the name already has the ".so" extension.
+ * @param modname Module name to expand
+ * @return Module name starting with "m_" and ending with ".so"
+ */
+ static std::string ExpandModName(const std::string& modname);
+
/** Simple, bog-standard, boring constructor.
*/
ModuleManager();
@@ -1161,12 +1097,6 @@ class CoreExport ModuleManager
*/
bool SetPriority(Module* mod, Implementation i, Priority s, Module* which = NULL);
- /** Backwards compat interface */
- inline bool SetPriority(Module* mod, Implementation i, Priority s, Module** dptr)
- {
- return SetPriority(mod, i, s, *dptr);
- }
-
/** Change the priority of all events in a module.
* @param mod The module to set the priority of
* @param s The priority of all events in the module.
@@ -1175,7 +1105,7 @@ class CoreExport ModuleManager
* SetPriority method for this, where you may specify other modules to
* be prioritized against.
*/
- bool SetPriority(Module* mod, Priority s);
+ void SetPriority(Module* mod, Priority s);
/** Attach an event to a module.
* You may later detatch the event with ModuleManager::Detach().
@@ -1231,18 +1161,19 @@ class CoreExport ModuleManager
*/
bool Unload(Module* module);
- /** Run an asynchronous reload of the given module. When the reload is
- * complete, the callback will be run with true if the reload succeeded
- * and false if it did not.
- */
- void Reload(Module* module, HandlerBase1<void, bool>* callback);
-
/** Called by the InspIRCd constructor to load all modules from the config file.
*/
void LoadAll();
void UnloadAll();
void DoSafeUnload(Module*);
+ /** Check if a module can be unloaded and if yes, prepare it for unload
+ * @param mod Module to be unloaded
+ * @return True if the module is unloadable, false otherwise.
+ * If true the module must be unloaded in the current main loop iteration.
+ */
+ bool CanUnload(Module* mod);
+
/** Find a module by name, and return a Module* to it.
* This is preferred over iterating the module lists yourself.
* @param name The module name to look up
@@ -1281,6 +1212,17 @@ class CoreExport ModuleManager
* @return A ModuleMap containing all loaded modules
*/
const ModuleMap& GetModules() const { return Modules; }
+
+ /** Make a service referenceable by dynamic_references
+ * @param name Name that will be used by dynamic_references to find the object
+ * @param service Service to make referenceable by dynamic_references
+ */
+ void AddReferent(const std::string& name, ServiceProvider* service);
+
+ /** Make a service no longer referenceable by dynamic_references
+ * @param service Service to make no longer referenceable by dynamic_references
+ */
+ void DelReferent(ServiceProvider* service);
};
/** Do not mess with these functions unless you know the C preprocessor
@@ -1293,7 +1235,7 @@ class CoreExport ModuleManager
#define MODULE_INIT_SYM_FN_2(x,y) MODULE_INIT_SYM_FN_1(x,y)
#define MODULE_INIT_SYM_FN_1(x,y) inspircd_module_ ## x ## _ ## y
-#ifdef PURE_STATIC
+#ifdef INSPIRCD_STATIC
struct AllCommandList {
typedef Command* (*fn)(Module*);
@@ -1334,7 +1276,8 @@ struct AllModuleList {
break; \
} \
return TRUE; \
- }
+ } \
+ extern "C" DllExport const char inspircd_src_version[] = INSPIRCD_VERSION;
#else
@@ -1343,7 +1286,7 @@ struct AllModuleList {
{ \
return new y; \
} \
- extern "C" DllExport const char inspircd_src_version[] = INSPIRCD_VERSION " " INSPIRCD_REVISION;
+ extern "C" DllExport const char inspircd_src_version[] = INSPIRCD_VERSION;
#endif
#define COMMAND_INIT(c) MODULE_INIT(CommandModule<c>)
diff --git a/include/modules/account.h b/include/modules/account.h
index c00b044e4..0368127a6 100644
--- a/include/modules/account.h
+++ b/include/modules/account.h
@@ -22,16 +22,7 @@
#include <map>
#include <string>
-class AccountEvent : public Event
-{
- public:
- User* const user;
- const std::string account;
- AccountEvent(Module* me, User* u, const std::string& name)
- : Event(me, "account_login"), user(u), account(name)
- {
- }
-};
+#include "event.h"
typedef StringExtItem AccountExtItem;
@@ -39,3 +30,19 @@ inline AccountExtItem* GetAccountExtItem()
{
return static_cast<AccountExtItem*>(ServerInstance->Extensions.GetItem("accountname"));
}
+
+class AccountEventListener : public Events::ModuleEventListener
+{
+ public:
+ AccountEventListener(Module* mod)
+ : ModuleEventListener(mod, "event/account")
+ {
+ }
+
+ /** Called when a user logs in or logs out
+ * @param user User logging in or out
+ * @param newaccount New account name of the user or empty string if the user
+ * logged out
+ */
+ virtual void OnAccountChange(User* user, const std::string& newaccount) = 0;
+};
diff --git a/include/modules/cap.h b/include/modules/cap.h
index 1b33e05bb..86a60c445 100644
--- a/include/modules/cap.h
+++ b/include/modules/cap.h
@@ -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
@@ -20,69 +19,298 @@
#pragma once
-class CapEvent : public Event
+#include "event.h"
+
+namespace Cap
{
- public:
- enum CapEventType
+ static const unsigned int MAX_CAPS = (sizeof(intptr_t) * 8) - 1;
+ static const intptr_t CAP_302_BIT = (intptr_t)1 << MAX_CAPS;
+ static const unsigned int MAX_VALUE_LENGTH = 100;
+
+ typedef intptr_t Ext;
+ class ExtItem : public LocalIntExt
{
- CAPEVENT_REQ,
- CAPEVENT_LS,
- CAPEVENT_LIST,
- CAPEVENT_CLEAR
+ public:
+ ExtItem(Module* mod);
+ std::string serialize(SerializeFormat format, const Extensible* container, void* item) const;
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
};
- CapEventType type;
- std::vector<std::string> wanted;
- std::vector<std::string> ack;
- User* user;
- CapEvent(Module* sender, User* u, CapEventType capevtype) : Event(sender, "cap_request"), type(capevtype), user(u) {}
-};
+ class Capability;
-class GenericCap
-{
- public:
- LocalIntExt ext;
- const std::string cap;
- GenericCap(Module* parent, const std::string &Cap) : ext("cap_" + Cap, parent), cap(Cap)
+ enum Protocol
{
- }
+ /** Supports capability negotiation protocol v3.1, or none
+ */
+ CAP_LEGACY,
+
+ /** Supports capability negotiation v3.2
+ */
+ CAP_302
+ };
+
+ class EventListener : public Events::ModuleEventListener
+ {
+ public:
+ EventListener(Module* mod)
+ : ModuleEventListener(mod, "event/cap")
+ {
+ }
- void HandleEvent(Event& ev)
+ /** Called whenever a new client capability becomes available or unavailable
+ * @param cap Capability being added or removed
+ * @param add If true, the capability is being added, otherwise its being removed
+ */
+ virtual void OnCapAddDel(Capability* cap, bool add) = 0;
+
+ /** Called whenever the value of a cap changes.
+ * @param cap Capability whose value changed
+ */
+ virtual void OnCapValueChange(Capability* cap) { }
+ };
+
+ class Manager : public DataProvider
{
- if (ev.id != "cap_request")
- return;
+ public:
+ Manager(Module* mod)
+ : DataProvider(mod, "capmanager")
+ {
+ }
+
+ /** Register a client capability.
+ * Modules should call Capability::SetActive(true) instead of this method.
+ * @param cap Capability to register
+ */
+ virtual void AddCap(Capability* cap) = 0;
+
+ /** Unregister a client capability.
+ * Modules should call Capability::SetActive(false) instead of this method.
+ * @param cap Capability to unregister
+ */
+ virtual void DelCap(Capability* cap) = 0;
+
+ /** Find a capability by name
+ * @param name Capability to find
+ * @return Capability object pointer if found, NULL otherwise
+ */
+ virtual Capability* Find(const std::string& name) const = 0;
+
+ /** Notify manager when a value of a cap changed
+ * @param cap Cap whose value changed
+ */
+ virtual void NotifyValueChange(Capability* cap) = 0;
+ };
+
+ /** Represents a client capability.
+ *
+ * Capabilities offer extensions to the client to server protocol. They must be negotiated with clients before they have any effect on the protocol.
+ * Each cap must have a unique name that is used during capability negotiation.
+ *
+ * After construction the cap is ready to be used by clients without any further setup, like other InspIRCd services.
+ * The get() method accepts a user as parameter and can be used to check whether that user has negotiated usage of the cap. This is only known for local users.
+ *
+ * The cap module must be loaded for the capability to work. The IsRegistered() method can be used to query whether the cap is actually online or not.
+ * The capability can be deactivated and reactivated with the SetActive() method. Deactivated caps behave as if they don't exist.
+ *
+ * It is possible to implement special behavior by inheriting from this class and overriding some of its methods.
+ */
+ class Capability : public ServiceProvider, private dynamic_reference_base::CaptureHook
+ {
+ typedef size_t Bit;
+
+ /** Bit allocated to this cap, undefined if the cap is unregistered
+ */
+ Bit bit;
+
+ /** Extension containing all caps set by a user. NULL if the cap is unregistered.
+ */
+ ExtItem* extitem;
+
+ /** True if the cap is active. Only active caps are registered in the manager.
+ */
+ bool active;
+
+ /** Reference to the cap manager object
+ */
+ dynamic_reference<Manager> manager;
+
+ void OnCapture() CXX11_OVERRIDE
+ {
+ if (active)
+ SetActive(true);
+ }
+
+ void Unregister()
+ {
+ bit = 0;
+ extitem = NULL;
+ }
+
+ Ext AddToMask(Ext mask) const { return (mask | GetMask()); }
+ Ext DelFromMask(Ext mask) const { return (mask & (~GetMask())); }
+ Bit GetMask() const { return bit; }
+
+ friend class ManagerImpl;
+
+ protected:
+ /** Notify the manager that the value of the capability changed.
+ * Must be called if the value of the cap changes for any reason.
+ */
+ void NotifyValueChange()
+ {
+ if (IsRegistered())
+ manager->NotifyValueChange(this);
+ }
+
+ public:
+ /** Constructor, initializes the capability.
+ * Caps are active by default.
+ * @param mod Module providing the cap
+ * @param Name Raw name of the cap as used in the protocol (CAP LS, etc.)
+ */
+ Capability(Module* mod, const std::string& Name)
+ : ServiceProvider(mod, Name, SERVICE_CUSTOM)
+ , active(true)
+ , manager(mod, "capmanager")
+ {
+ Unregister();
+ }
+
+ ~Capability()
+ {
+ SetActive(false);
+ }
+
+ void RegisterService() CXX11_OVERRIDE
+ {
+ manager.SetCaptureHook(this);
+ SetActive(true);
+ }
- CapEvent *data = static_cast<CapEvent*>(&ev);
- if (data->type == CapEvent::CAPEVENT_REQ)
+ /** Check whether a user has the capability turned on.
+ * This method is safe to call if the cap is unregistered and will return false.
+ * @param user User to check
+ * @return True if the user is using this capability, false otherwise
+ */
+ bool get(User* user) const
{
- for (std::vector<std::string>::iterator it = data->wanted.begin(); it != data->wanted.end(); ++it)
+ if (!IsRegistered())
+ return false;
+ Ext caps = extitem->get(user);
+ return ((caps & GetMask()) != 0);
+ }
+
+ /** Turn the capability on/off for a user. If the cap is not registered this method has no effect.
+ * @param user User to turn the cap on/off for
+ * @param val True to turn the cap on, false to turn it off
+ */
+ void set(User* user, bool val)
+ {
+ if (!IsRegistered())
+ return;
+ Ext curr = extitem->get(user);
+ extitem->set(user, (val ? AddToMask(curr) : DelFromMask(curr)));
+ }
+
+ /** Activate or deactivate the capability.
+ * If activating, the cap is marked as active and if the manager is available the cap is registered in the manager.
+ * If deactivating, the cap is marked as inactive and if it is registered, it will be unregistered.
+ * Users who had the cap turned on will have it turned off automatically.
+ * @param activate True to activate the cap, false to deactivate it
+ */
+ void SetActive(bool activate)
+ {
+ active = activate;
+ if (manager)
{
- if (it->empty())
- continue;
- bool enablecap = ((*it)[0] != '-');
- if (((enablecap) && (*it == cap)) || (*it == "-" + cap))
- {
- // we can handle this, so ACK it, and remove it from the wanted list
- data->ack.push_back(*it);
- data->wanted.erase(it);
- ext.set(data->user, enablecap ? 1 : 0);
- break;
- }
+ if (activate)
+ manager->AddCap(this);
+ else
+ manager->DelCap(this);
}
}
- else if (data->type == CapEvent::CAPEVENT_LS)
+
+ /** Get the name of the capability that's used in the protocol
+ * @return Name of the capability as used in the protocol
+ */
+ const std::string& GetName() const { return name; }
+
+ /** Check whether the capability is active. The cap must be active and registered to be used by users.
+ * @return True if the cap is active, false if it has been deactivated
+ */
+ bool IsActive() const { return active; }
+
+ /** Check whether the capability is registered
+ * The cap must be active and the manager must be available for a cap to be registered.
+ * @return True if the cap is registered in the manager, false otherwise
+ */
+ bool IsRegistered() const { return (extitem != NULL); }
+
+ /** Get the CAP negotiation protocol version of a user.
+ * The cap must be registered for this to return anything other than CAP_LEGACY.
+ * @param user User whose negotiation protocol version to query
+ * @return One of the Capability::Protocol enum indicating the highest supported capability negotiation protocol version
+ */
+ Protocol GetProtocol(LocalUser* user) const
{
- data->wanted.push_back(cap);
+ return ((IsRegistered() && (extitem->get(user) & CAP_302_BIT)) ? CAP_302 : CAP_LEGACY);
}
- else if (data->type == CapEvent::CAPEVENT_LIST)
+
+ /** Called when a user requests to turn this capability on or off.
+ * @param user User requesting to change the state of the cap
+ * @param add True if requesting to turn the cap on, false if requesting to turn it off
+ * @return True to allow the request, false to reject it
+ */
+ virtual bool OnRequest(LocalUser* user, bool add)
{
- if (ext.get(data->user))
- data->wanted.push_back(cap);
+ return true;
}
- else if (data->type == CapEvent::CAPEVENT_CLEAR)
+
+ /** Called when a user requests a list of all capabilities and this capability is about to be included in the list.
+ * The default behavior always includes the cap in the list.
+ * @param user User querying a list capabilities
+ * @return True to add this cap to the list sent to the user, false to not list it
+ */
+ virtual bool OnList(LocalUser* user)
{
- data->ack.push_back("-" + cap);
- ext.set(data->user, 0);
+ return true;
}
- }
-};
+
+ /** Query the value of this capability for a user
+ * @param user User who will get the value of the capability
+ * @return Value to show to the user. If NULL, the capability has no value (default).
+ */
+ virtual const std::string* GetValue(LocalUser* user) const
+ {
+ return NULL;
+ }
+ };
+
+ /** Reference to a cap. The cap may be provided by another module.
+ */
+ class Reference
+ {
+ dynamic_reference_nocheck<Capability> ref;
+
+ public:
+ /** Constructor, initializes the capability reference
+ * @param mod Module creating this object
+ * @param Name Raw name of the cap as used in the protocol (CAP LS, etc.)
+ */
+ Reference(Module* mod, const std::string& Name)
+ : ref(mod, "cap/" + Name)
+ {
+ }
+
+ /** Check whether a user has the referenced capability turned on.
+ * @param user User to check
+ * @return True if the user is using the referenced capability, false otherwise
+ */
+ bool get(LocalUser* user)
+ {
+ if (ref)
+ return ref->get(user);
+ return false;
+ }
+ };
+}
diff --git a/include/modules/dns.h b/include/modules/dns.h
index c76c53805..61abd7144 100644
--- a/include/modules/dns.h
+++ b/include/modules/dns.h
@@ -33,6 +33,8 @@ namespace DNS
QUERY_CNAME = 5,
/* Reverse DNS lookup */
QUERY_PTR = 12,
+ /* TXT */
+ QUERY_TXT = 16,
/* IPv6 AAAA lookup */
QUERY_AAAA = 28
};
@@ -57,6 +59,7 @@ namespace DNS
ERROR_UNKNOWN,
ERROR_UNLOADED,
ERROR_TIMEDOUT,
+ ERROR_MALFORMED,
ERROR_NOT_AN_ANSWER,
ERROR_NONSTANDARD_QUERY,
ERROR_FORMAT_ERROR,
@@ -68,13 +71,9 @@ namespace DNS
ERROR_INVALIDTYPE
};
- const int PORT = 53;
+ typedef uint16_t RequestId;
- /**
- * The maximum value of a dns request id,
- * 16 bits wide, 0xFFFF.
- */
- const int MAX_REQUEST_ID = 0xFFFF;
+ const int PORT = 53;
class Exception : public ModuleException
{
@@ -86,11 +85,11 @@ namespace DNS
{
std::string name;
QueryType type;
- unsigned short qclass;
- Question() : type(QUERY_NONE), qclass(0) { }
- Question(const std::string& n, QueryType t, unsigned short c = 1) : name(n), type(t), qclass(c) { }
- inline bool operator==(const Question& other) const { return name == other.name && type == other.type && qclass == other.qclass; }
+ Question() : type(QUERY_NONE) { }
+ Question(const std::string& n, QueryType t) : name(n), type(t) { }
+ bool operator==(const Question& other) const { return ((name == other.name) && (type == other.type)); }
+ bool operator!=(const Question& other) const { return (!(*this == other)); }
struct hash
{
@@ -107,19 +106,31 @@ namespace DNS
std::string rdata;
time_t created;
- ResourceRecord(const std::string& n, QueryType t, unsigned short c = 1) : Question(n, t, c), ttl(0), created(ServerInstance->Time()) { }
+ ResourceRecord(const std::string& n, QueryType t) : Question(n, t), ttl(0), created(ServerInstance->Time()) { }
ResourceRecord(const Question& question) : Question(question), ttl(0), created(ServerInstance->Time()) { }
};
struct Query
{
- std::vector<Question> questions;
+ Question question;
std::vector<ResourceRecord> answers;
Error error;
bool cached;
Query() : error(ERROR_NONE), cached(false) { }
- Query(const Question& question) : error(ERROR_NONE), cached(false) { questions.push_back(question); }
+ Query(const Question& q) : question(q), error(ERROR_NONE), cached(false) { }
+
+ const ResourceRecord* FindAnswerOfType(QueryType qtype) const
+ {
+ for (std::vector<DNS::ResourceRecord>::const_iterator i = answers.begin(); i != answers.end(); ++i)
+ {
+ const DNS::ResourceRecord& rr = *i;
+ if (rr.type == qtype)
+ return &rr;
+ }
+
+ return NULL;
+ }
};
class ReplySocket;
@@ -139,27 +150,27 @@ namespace DNS
/** A DNS query.
*/
- class Request : public Timer, public Question
+ class Request : public Timer
{
protected:
Manager* const manager;
public:
+ Question question;
/* Use result cache if available */
bool use_cache;
/* Request id */
- unsigned short id;
+ RequestId id;
/* Creator of this request */
Module* const creator;
Request(Manager* mgr, Module* mod, const std::string& addr, QueryType qt, bool usecache = true)
- : Timer((ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5), ServerInstance->Time())
- , Question(addr, qt)
+ : Timer((ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5))
, manager(mgr)
+ , question(addr, qt)
, use_cache(usecache)
, id(0)
, creator(mod)
{
- ServerInstance->Timers.AddTimer(this);
}
virtual ~Request()
@@ -182,7 +193,7 @@ namespace DNS
*/
bool Tick(time_t now)
{
- Query rr(*this);
+ Query rr(this->question);
rr.error = ERROR_TIMEDOUT;
this->OnError(&rr);
delete this;
diff --git a/include/modules/hash.h b/include/modules/hash.h
index da04c45ba..7d46ee74a 100644
--- a/include/modules/hash.h
+++ b/include/modules/hash.h
@@ -26,24 +26,33 @@ class HashProvider : public DataProvider
public:
const unsigned int out_size;
const unsigned int block_size;
- HashProvider(Module* mod, const std::string& Name, int osiz, int bsiz)
- : DataProvider(mod, Name), out_size(osiz), block_size(bsiz) {}
- virtual std::string sum(const std::string& data) = 0;
- inline std::string hexsum(const std::string& data)
+ HashProvider(Module* mod, const std::string& Name, unsigned int osiz = 0, unsigned int bsiz = 0)
+ : DataProvider(mod, "hash/" + Name), out_size(osiz), block_size(bsiz)
{
- return BinToHex(sum(data));
}
- inline std::string b64sum(const std::string& data)
+ virtual std::string GenerateRaw(const std::string& data) = 0;
+
+ virtual std::string ToPrintable(const std::string& raw)
+ {
+ return BinToHex(raw);
+ }
+
+ virtual bool Compare(const std::string& input, const std::string& hash)
+ {
+ return InspIRCd::TimingSafeCompare(Generate(input), hash);
+ }
+
+ std::string Generate(const std::string& data)
{
- return BinToBase64(sum(data), NULL, 0);
+ return ToPrintable(GenerateRaw(data));
}
/** HMAC algorithm, RFC 2104 */
std::string hmac(const std::string& key, const std::string& msg)
{
std::string hmac1, hmac2;
- std::string kbuf = key.length() > block_size ? sum(key) : key;
+ std::string kbuf = key.length() > block_size ? GenerateRaw(key) : key;
kbuf.resize(block_size);
for (size_t n = 0; n < block_size; n++)
@@ -52,7 +61,12 @@ class HashProvider : public DataProvider
hmac2.push_back(static_cast<char>(kbuf[n] ^ 0x36));
}
hmac2.append(msg);
- hmac1.append(sum(hmac2));
- return sum(hmac1);
+ hmac1.append(GenerateRaw(hmac2));
+ return GenerateRaw(hmac1);
+ }
+
+ bool IsKDF() const
+ {
+ return (!block_size);
}
};
diff --git a/include/modules/httpd.h b/include/modules/httpd.h
index 86234d53f..b4b88bed5 100644
--- a/include/modules/httpd.h
+++ b/include/modules/httpd.h
@@ -24,6 +24,7 @@
#pragma once
#include "base.h"
+#include "event.h"
#include <string>
#include <sstream>
@@ -107,7 +108,7 @@ class HttpServerSocket;
/** This class represents a HTTP request.
*/
-class HTTPRequest : public Event
+class HTTPRequest
{
protected:
std::string type;
@@ -134,9 +135,9 @@ class HTTPRequest : public Event
* @param ip The IP address making the web request.
* @param pdata The post data (content after headers) received with the request, up to Content-Length in size
*/
- HTTPRequest(Module* me, const std::string &eventid, const std::string &request_type, const std::string &uri,
+ HTTPRequest(const std::string& request_type, const std::string& uri,
HTTPHeaders* hdr, HttpServerSocket* socket, const std::string &ip, const std::string &pdata)
- : Event(me, eventid), type(request_type), document(uri), ipaddr(ip), postdata(pdata), headers(hdr), sock(socket)
+ : type(request_type), document(uri), ipaddr(ip), postdata(pdata), headers(hdr), sock(socket)
{
}
@@ -237,3 +238,25 @@ class HTTPdAPI : public dynamic_reference<HTTPdAPIBase>
{
}
};
+
+class HTTPACLEventListener : public Events::ModuleEventListener
+{
+ public:
+ HTTPACLEventListener(Module* mod)
+ : ModuleEventListener(mod, "event/http-acl")
+ {
+ }
+
+ virtual ModResult OnHTTPACLCheck(HTTPRequest& req) = 0;
+};
+
+class HTTPRequestEventListener : public Events::ModuleEventListener
+{
+ public:
+ HTTPRequestEventListener(Module* mod)
+ : ModuleEventListener(mod, "event/http-request")
+ {
+ }
+
+ virtual ModResult OnHTTPRequest(HTTPRequest& req) = 0;
+};
diff --git a/include/modules/invite.h b/include/modules/invite.h
new file mode 100644
index 000000000..e53d5202f
--- /dev/null
+++ b/include/modules/invite.h
@@ -0,0 +1,128 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+namespace Invite
+{
+ class APIBase;
+ class API;
+ class Invite;
+
+ typedef insp::intrusive_list<Invite, LocalUser> List;
+}
+
+class Invite::APIBase : public DataProvider
+{
+ public:
+ APIBase(Module* parent);
+
+ /** Create or extend an Invite.
+ * When a user is invited to join a channel either a new Invite object is created or
+ * or the expiration timestamp is updated if there is already a pending Invite for
+ * the given (user, channel) pair and the new expiration time is further than the current.
+ * @param user Target user
+ * @param chan Target channel
+ * @param timeout Timestamp when the invite should expire, 0 for no expiration
+ */
+ virtual void Create(LocalUser* user, Channel* chan, time_t timeout) = 0;
+
+ /** Retrieves the Invite object for the given (user, channel) pair
+ * @param user Target user
+ * @param chan Target channel
+ * @return Invite object for the given (channel, user) pair if it exists, NULL otherwise
+ */
+ virtual Invite* Find(LocalUser* user, Channel* chan) = 0;
+
+ /** Returns the list of channels a user has been invited to but has not yet joined.
+ * @param user User whose invite list to retrieve
+ * @return List of channels the user is invited to or NULL if the list is empty
+ */
+ virtual const List* GetList(LocalUser* user) = 0;
+
+ /** Check if a user is invited to a channel
+ * @param user User to check
+ * @param chan Channel to check
+ * @return True if the user is invited to the channel, false otherwise
+ */
+ bool IsInvited(LocalUser* user, Channel* chan) { return (Find(user, chan) != NULL); }
+
+ /** Removes an Invite if it exists
+ * @param user User whose invite to remove
+ * @param chan Channel to remove the invite to
+ * @return True if the user was invited to the channel and the invite was removed, false if the user wasn't invited
+ */
+ virtual bool Remove(LocalUser* user, Channel* chan) = 0;
+};
+
+class Invite::API : public dynamic_reference<APIBase>
+{
+ public:
+ API(Module* parent)
+ : dynamic_reference<APIBase>(parent, "core_channel_invite")
+ {
+ }
+};
+
+/**
+ * The Invite class contains all data about a pending invite.
+ * Invite objects are referenced from the user and the channel they belong to.
+ */
+class Invite::Invite : public insp::intrusive_list_node<Invite, LocalUser>, public insp::intrusive_list_node<Invite, Channel>
+{
+ public:
+ /** User the invite is for
+ */
+ LocalUser* const user;
+
+ /** Channel where the user is invited to
+ */
+ Channel* const chan;
+
+ /** Check whether the invite will expire or not
+ * @return True if the invite is timed, false if it doesn't expire
+ */
+ bool IsTimed() const { return (expiretimer != NULL); }
+
+ /** Serialize this object
+ * @param format Serialization format
+ * @param show_chans True to include channel in the output, false to include the nick/uuid
+ * @param out Output will be appended to this string
+ */
+ void Serialize(SerializeFormat format, bool show_chans, std::string& out);
+
+ friend class APIImpl;
+
+ private:
+ /** Timer handling expiration. If NULL this invite doesn't expire.
+ */
+ Timer* expiretimer;
+
+ /** Constructor, only available to the module providing the invite API (core_channel).
+ * To create Invites use InviteAPI::Create().
+ * @param user User being invited
+ * @param chan Channel the user is invited to
+ */
+ Invite(LocalUser* user, Channel* chan);
+
+ /** Destructor, only available to the module providing the invite API (core_channel).
+ * To remove Invites use InviteAPI::Remove().
+ */
+ ~Invite();
+};
diff --git a/src/modes/cmode_v.cpp b/include/modules/ircv3.h
index c8ce30ab1..e03ee16fa 100644
--- a/src/modes/cmode_v.cpp
+++ b/include/modules/ircv3.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) 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
@@ -20,17 +17,29 @@
*/
-#include "inspircd.h"
-#include "configreader.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "builtinmodes.h"
+#pragma once
-ModeChannelVoice::ModeChannelVoice() : PrefixMode(NULL, "voice", 'v')
+namespace IRCv3
{
- prefix = '+';
- levelrequired = HALFOP_VALUE;
- prefixrank = VOICE_VALUE;
+ class WriteNeighborsWithCap;
}
+
+class IRCv3::WriteNeighborsWithCap : public User::ForEachNeighborHandler
+{
+ const Cap::Capability& cap;
+ const std::string& msg;
+
+ void Execute(LocalUser* user) CXX11_OVERRIDE
+ {
+ if (cap.get(user))
+ user->Write(msg);
+ }
+
+ public:
+ WriteNeighborsWithCap(User* user, const std::string& message, const Cap::Capability& capability)
+ : cap(capability)
+ , msg(message)
+ {
+ user->ForEachNeighbor(*this, false);
+ }
+};
diff --git a/include/modules/ldap.h b/include/modules/ldap.h
index 75ab16077..aeb3aa335 100644
--- a/include/modules/ldap.h
+++ b/include/modules/ldap.h
@@ -1,8 +1,8 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2013 Adam <Adam@anope.org>
- * Copyright (C) 2003-2013 Anope Team <team@anope.org>
+ * Copyright (C) 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
@@ -78,22 +78,22 @@ struct LDAPAttributes : public std::map<std::string, std::vector<std::string> >
}
};
+enum QueryType
+{
+ QUERY_UNKNOWN,
+ QUERY_BIND,
+ QUERY_SEARCH,
+ QUERY_ADD,
+ QUERY_DELETE,
+ QUERY_MODIFY,
+ QUERY_COMPARE
+};
+
struct LDAPResult
{
std::vector<LDAPAttributes> messages;
std::string error;
- enum QueryType
- {
- QUERY_UNKNOWN,
- QUERY_BIND,
- QUERY_SEARCH,
- QUERY_ADD,
- QUERY_DELETE,
- QUERY_MODIFY,
- QUERY_COMPARE
- };
-
QueryType type;
LDAPQuery id;
@@ -145,55 +145,48 @@ class LDAPProvider : public DataProvider
/** Attempt to bind to the LDAP server as a manager
* @param i The LDAPInterface the result is sent to
- * @return The query ID
*/
- virtual LDAPQuery BindAsManager(LDAPInterface *i) = 0;
+ virtual void BindAsManager(LDAPInterface* i) = 0;
/** Bind to LDAP
* @param i The LDAPInterface the result is sent to
* @param who The binddn
* @param pass The password
- * @return The query ID
*/
- virtual LDAPQuery Bind(LDAPInterface* i, const std::string& who, const std::string& pass) = 0;
+ virtual void Bind(LDAPInterface* i, const std::string& who, const std::string& pass) = 0;
/** Search ldap for the specified filter
* @param i The LDAPInterface the result is sent to
* @param base The base DN to search
* @param filter The filter to apply
- * @return The query ID
*/
- virtual LDAPQuery Search(LDAPInterface* i, const std::string& base, const std::string& filter) = 0;
+ virtual void Search(LDAPInterface* i, const std::string& base, const std::string& filter) = 0;
/** Add an entry to LDAP
* @param i The LDAPInterface the result is sent to
* @param dn The dn of the entry to add
* @param attributes The attributes
- * @return The query ID
*/
- virtual LDAPQuery Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) = 0;
+ virtual void Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) = 0;
/** Delete an entry from LDAP
* @param i The LDAPInterface the result is sent to
* @param dn The dn of the entry to delete
- * @return The query ID
*/
- virtual LDAPQuery Del(LDAPInterface* i, const std::string& dn) = 0;
+ virtual void Del(LDAPInterface* i, const std::string& dn) = 0;
/** Modify an existing entry in LDAP
* @param i The LDAPInterface the result is sent to
* @param base The base DN to modify
* @param attributes The attributes to modify
- * @return The query ID
*/
- virtual LDAPQuery Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) = 0;
+ virtual void Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) = 0;
/** Compare an attribute in LDAP with our value
* @param i The LDAPInterface the result is sent to
* @param dn DN to use for comparing
* @param attr Attr of DN to compare with
* @param val value to compare attr of dn
- * @return the query ID
*/
- virtual LDAPQuery Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) = 0;
+ virtual void Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) = 0;
};
diff --git a/include/modules/reload.h b/include/modules/reload.h
new file mode 100644
index 000000000..dcdbc95e9
--- /dev/null
+++ b/include/modules/reload.h
@@ -0,0 +1,80 @@
+/*
+ * 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 "event.h"
+
+namespace ReloadModule
+{
+ class EventListener;
+ class DataKeeper;
+
+ /** Container for data saved by modules before another module is reloaded.
+ */
+ class CustomData
+ {
+ struct Data
+ {
+ EventListener* handler;
+ void* data;
+ Data(EventListener* Handler, void* moddata) : handler(Handler), data(moddata) { }
+ };
+ typedef std::vector<Data> List;
+ List list;
+
+ public:
+ /** Add data to the saved state of a module.
+ * The provided handler's OnReloadModuleRestore() method will be called when the reload is done with the pointer
+ * provided.
+ * @param handler Handler for restoring the data
+ * @param data Pointer to the data, will be passed back to the provided handler's OnReloadModuleRestore() after the
+ * reload finishes
+ */
+ void add(EventListener* handler, void* data)
+ {
+ list.push_back(Data(handler, data));
+ }
+
+ friend class DataKeeper;
+ };
+
+ class EventListener : public Events::ModuleEventListener
+ {
+ public:
+ EventListener(Module* mod)
+ : ModuleEventListener(mod, "event/reloadmodule")
+ {
+ }
+
+ /** Called whenever a module is about to be reloaded. Use this event to save data related to the module that you want
+ * to be restored after the reload.
+ * @param mod Module to be reloaded
+ * @param cd CustomData instance that can store your data once.
+ */
+ virtual void OnReloadModuleSave(Module* mod, CustomData& cd) = 0;
+
+ /** Restore data after a reload. Only called if data was added in OnReloadModuleSave().
+ * @param mod Reloaded module, if NULL the reload failed and the module no longer exists
+ * @param data Pointer that was passed to CustomData::add() in OnReloadModuleSave() at the time when the module's state
+ * was saved
+ */
+ virtual void OnReloadModuleRestore(Module* mod, void* data) = 0;
+ };
+}
diff --git a/include/modules/sasl.h b/include/modules/sasl.h
index 321711a68..0a7b19a70 100644
--- a/include/modules/sasl.h
+++ b/include/modules/sasl.h
@@ -19,13 +19,15 @@
#pragma once
-class SASLFallback : public Event
+#include "event.h"
+
+class SASLEventListener : public Events::ModuleEventListener
{
public:
- const parameterlist& params;
- SASLFallback(Module* me, const parameterlist& p)
- : Event(me, "sasl_fallback"), params(p)
+ SASLEventListener(Module* mod)
+ : ModuleEventListener(mod, "event/sasl")
{
- Send();
}
+
+ virtual void OnSASLAuth(const parameterlist& params) = 0;
};
diff --git a/include/modules/spanningtree.h b/include/modules/spanningtree.h
index 99f4f9fc4..e71cdf9d0 100644
--- a/include/modules/spanningtree.h
+++ b/include/modules/spanningtree.h
@@ -19,22 +19,23 @@
#pragma once
-struct AddServerEvent : public Event
-{
- const std::string servername;
- AddServerEvent(Module* me, const std::string& name)
- : Event(me, "new_server"), servername(name)
- {
- Send();
- }
-};
+#include "event.h"
-struct DelServerEvent : public Event
+class SpanningTreeEventListener : public Events::ModuleEventListener
{
- const std::string servername;
- DelServerEvent(Module* me, const std::string& name)
- : Event(me, "lost_server"), servername(name)
+ public:
+ SpanningTreeEventListener(Module* mod)
+ : ModuleEventListener(mod, "event/spanningtree")
{
- Send();
}
+
+ /** Fired when a server finishes burst
+ * @param server Server that recently linked and finished burst
+ */
+ virtual void OnServerLink(const Server* server) { }
+
+ /** Fired when a server splits
+ * @param server Server that split
+ */
+ virtual void OnServerSplit(const Server* server) { }
};
diff --git a/include/modules/ssl.h b/include/modules/ssl.h
index 0f58e0b7b..9cc504128 100644
--- a/include/modules/ssl.h
+++ b/include/modules/ssl.h
@@ -138,7 +138,40 @@ class SSLIOHook : public IOHook
*/
reference<ssl_cert> certificate;
+ /** Reduce elements in a send queue by appending later elements to the first element until there are no more
+ * elements to append or a desired length is reached
+ * @param sendq SendQ to work on
+ * @param targetsize Target size of the front element
+ */
+ static void FlattenSendQueue(StreamSocket::SendQueue& sendq, size_t targetsize)
+ {
+ if ((sendq.size() <= 1) || (sendq.front().length() >= targetsize))
+ return;
+
+ // 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 an IOHook.
+ std::string tmp;
+ tmp.reserve(std::min(targetsize, sendq.bytes())+1);
+ do
+ {
+ tmp.append(sendq.front());
+ sendq.pop_front();
+ }
+ while (!sendq.empty() && tmp.length() < targetsize);
+ sendq.push_front(tmp);
+ }
+
public:
+ static SSLIOHook* IsSSL(StreamSocket* sock)
+ {
+ IOHook* const iohook = sock->GetIOHook();
+ if ((iohook) && ((iohook->prov->type == IOHookProvider::IOH_SSL)))
+ return static_cast<SSLIOHook*>(iohook);
+ return NULL;
+ }
+
SSLIOHook(IOHookProvider* hookprov)
: IOHook(hookprov)
{
@@ -165,6 +198,12 @@ class SSLIOHook : public IOHook
return cert->GetFingerprint();
return "";
}
+
+ /**
+ * Get the ciphersuite negotiated with the peer
+ * @param out String where the ciphersuite string will be appended to
+ */
+ virtual void GetCiphersuite(std::string& out) const = 0;
};
/** Helper functions for obtaining SSL client certificates and key fingerprints
@@ -180,11 +219,10 @@ class SSLClientCert
*/
static ssl_cert* GetCertificate(StreamSocket* sock)
{
- IOHook* iohook = sock->GetIOHook();
- if ((!iohook) || (iohook->prov->type != IOHookProvider::IOH_SSL))
+ SSLIOHook* ssliohook = SSLIOHook::IsSSL(sock);
+ if (!ssliohook)
return NULL;
- SSLIOHook* ssliohook = static_cast<SSLIOHook*>(iohook);
return ssliohook->GetCertificate();
}
diff --git a/include/modules/stats.h b/include/modules/stats.h
new file mode 100644
index 000000000..d2f6eabbb
--- /dev/null
+++ b/include/modules/stats.h
@@ -0,0 +1,173 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+namespace Stats
+{
+ class Context;
+ class Row;
+}
+
+class Stats::Row : public Numeric::Numeric
+{
+ public:
+ Row(unsigned int num)
+ : Numeric(num)
+ {
+ }
+};
+
+class Stats::Context
+{
+ /** Source user of the STATS request
+ */
+ User* const source;
+
+ /** List of reply rows
+ */
+ std::vector<Row> rows;
+
+ /** Symbol indicating the type of this STATS request (usually a letter)
+ */
+ const char symbol;
+
+ public:
+ /** Constructor
+ * @param src Source user of the STATS request, can be a local or remote user
+ * @param sym Symbol (letter) indicating the type of the request
+ */
+ Context(User* src, char sym)
+ : source(src)
+ , symbol(sym)
+ {
+ }
+
+ /** Get the source user of the STATS request
+ * @return Source user of the STATS request
+ */
+ User* GetSource() const { return source; }
+
+ /** Get the list of reply rows
+ * @return List of rows generated as reply for the request
+ */
+ const std::vector<Row>& GetRows() const { return rows; }
+
+ /** Get the symbol (letter) indicating what type of STATS was requested
+ * @return Symbol specified by the requesting user
+ */
+ char GetSymbol() const { return symbol; }
+
+ /** Add a row to the reply list
+ * @param row Reply to add
+ */
+ void AddRow(const Row& row) { rows.push_back(row); }
+
+ template <typename T1>
+ void AddRow(unsigned int numeric, T1 p1)
+ {
+ Row n(numeric);
+ n.push(p1);
+ AddRow(n);
+ }
+
+ template <typename T1, typename T2>
+ void AddRow(unsigned int numeric, T1 p1, T2 p2)
+ {
+ Row n(numeric);
+ n.push(p1);
+ n.push(p2);
+ AddRow(n);
+ }
+
+ template <typename T1, typename T2, typename T3>
+ void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3)
+ {
+ Row n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ AddRow(n);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4>
+ void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4)
+ {
+ Row n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ n.push(p4);
+ AddRow(n);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
+ {
+ Row n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ n.push(p4);
+ n.push(p5);
+ AddRow(n);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
+ void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6)
+ {
+ Row n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ n.push(p4);
+ n.push(p5);
+ n.push(p6);
+ AddRow(n);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
+ void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7)
+ {
+ Row n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ n.push(p4);
+ n.push(p5);
+ n.push(p6);
+ n.push(p7);
+ AddRow(n);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
+ void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8)
+ {
+ Row n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ n.push(p4);
+ n.push(p5);
+ n.push(p6);
+ n.push(p7);
+ n.push(p8);
+ AddRow(n);
+ }
+};
diff --git a/include/modules/whois.h b/include/modules/whois.h
new file mode 100644
index 000000000..4f09d268b
--- /dev/null
+++ b/include/modules/whois.h
@@ -0,0 +1,146 @@
+/*
+ * 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 "event.h"
+
+namespace Whois
+{
+ class EventListener;
+ class LineEventListener;
+ class Context;
+}
+
+class Whois::EventListener : public Events::ModuleEventListener
+{
+ public:
+ EventListener(Module* mod)
+ : ModuleEventListener(mod, "event/whois")
+ {
+ }
+
+ /** Called whenever a /WHOIS is performed by a local user.
+ * @param whois Whois context, can be used to send numerics
+ */
+ virtual void OnWhois(Context& whois) = 0;
+};
+
+class Whois::LineEventListener : public Events::ModuleEventListener
+{
+ public:
+ LineEventListener(Module* mod)
+ : ModuleEventListener(mod, "event/whoisline")
+ {
+ }
+
+ /** Called whenever a line of WHOIS output is sent to a user.
+ * You may change the numeric and the text of the output by changing
+ * the values numeric and text, but you cannot change the user the
+ * numeric is sent to.
+ * @param whois Whois context, can be used to send numerics
+ * @param numeric Numeric being sent
+ * @return MOD_RES_DENY to drop the line completely so that the user does not
+ * receive it, or MOD_RES_PASSTHRU to allow the line to be sent.
+ */
+ virtual ModResult OnWhoisLine(Context& whois, Numeric::Numeric& numeric) = 0;
+};
+
+class Whois::Context
+{
+ protected:
+ /** User doing the WHOIS
+ */
+ LocalUser* const source;
+
+ /** User being WHOISed
+ */
+ User* const target;
+
+ public:
+ Context(LocalUser* src, User* targ)
+ : source(src)
+ , target(targ)
+ {
+ }
+
+ /** Returns true if the user is /WHOISing himself
+ * @return True if whois source is the same user as the whois target, false if they are different users
+ */
+ bool IsSelfWhois() const { return (source == target); }
+
+ /** Returns the LocalUser who has done the /WHOIS
+ * @return LocalUser doing the /WHOIS
+ */
+ LocalUser* GetSource() const { return source; }
+
+ /** Returns the target of the /WHOIS
+ * @return User who was /WHOIS'd
+ */
+ User* GetTarget() const { return target; }
+
+ /** Send a line of WHOIS data to the source of the WHOIS
+ */
+ template <typename T1>
+ void SendLine(unsigned int numeric, T1 p1)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(target->nick);
+ n.push(p1);
+ SendLine(n);
+ }
+
+ template <typename T1, typename T2>
+ void SendLine(unsigned int numeric, T1 p1, T2 p2)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(target->nick);
+ n.push(p1);
+ n.push(p2);
+ SendLine(n);
+ }
+
+ template <typename T1, typename T2, typename T3>
+ void SendLine(unsigned int numeric, T1 p1, T2 p2, T3 p3)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(target->nick);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ SendLine(n);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4>
+ void SendLine(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(target->nick);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ n.push(p4);
+ SendLine(n);
+ }
+
+ /** Send a line of WHOIS data to the source of the WHOIS
+ * @param numeric Numeric to send
+ */
+ virtual void SendLine(Numeric::Numeric& numeric) = 0;
+};
diff --git a/include/numeric.h b/include/numeric.h
new file mode 100644
index 000000000..8044fe5bf
--- /dev/null
+++ b/include/numeric.h
@@ -0,0 +1,103 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+#include "numerics.h"
+
+namespace Numeric
+{
+ class Numeric;
+}
+
+class Numeric::Numeric
+{
+ /** Numeric number
+ */
+ unsigned int numeric;
+
+ /** Parameters of the numeric
+ */
+ std::vector<std::string> params;
+
+ /** Source server of the numeric, if NULL (the default) then it is the local server
+ */
+ Server* sourceserver;
+
+ public:
+ /** Constructor
+ * @param num Numeric number (RPL_*, ERR_*)
+ */
+ Numeric(unsigned int num)
+ : numeric(num)
+ , sourceserver(NULL)
+ {
+ }
+
+ /** Add a parameter to the numeric. The parameter will be converted to a string first with ConvToStr().
+ * @param x Parameter to add
+ */
+ template <typename T>
+ Numeric& push(const T& x)
+ {
+ params.push_back(ConvToStr(x));
+ return *this;
+ }
+
+ /** Set the source server of the numeric. The source server defaults to the local server.
+ * @param server Server to set as source
+ */
+ void SetServer(Server* server) { sourceserver = server; }
+
+ /** Get the source server of the numeric
+ * @return Source server or NULL if the source is the local server
+ */
+ Server* GetServer() const { return sourceserver; }
+
+ /** Get the number of the numeric as an unsigned integer
+ * @return Numeric number as an unsigned integer
+ */
+ unsigned int GetNumeric() const { return numeric; }
+
+ /** Get the parameters of the numeric
+ * @return Parameters of the numeric as a const vector of strings
+ */
+ const std::vector<std::string>& GetParams() const { return params; }
+
+ /** Get the parameters of the numeric
+ * @return Parameters of the numeric as a vector of strings
+ */
+ std::vector<std::string>& GetParams() { return params; }
+};
+
+namespace Numerics
+{
+ /** ERR_NOSUCHNICK numeric
+ */
+ class NoSuchNick : public Numeric::Numeric
+ {
+ public:
+ NoSuchNick(const std::string& nick)
+ : Numeric(ERR_NOSUCHNICK)
+ {
+ push(nick);
+ push("No such nick/channel");
+ }
+ };
+}
diff --git a/include/numericbuilder.h b/include/numericbuilder.h
new file mode 100644
index 000000000..17aa9e0c8
--- /dev/null
+++ b/include/numericbuilder.h
@@ -0,0 +1,198 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015-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/>.
+ */
+
+
+#pragma once
+
+namespace Numeric
+{
+ class WriteNumericSink;
+ class WriteRemoteNumericSink;
+
+ template <char Sep, bool SendEmpty, typename Sink>
+ class GenericBuilder;
+
+ template <char Sep = ',', bool SendEmpty = false>
+ class Builder;
+
+ template <unsigned int NumStaticParams, bool SendEmpty, typename Sink>
+ class GenericParamBuilder;
+
+ template <unsigned int NumStaticParams, bool SendEmpty = false>
+ class ParamBuilder;
+}
+
+class Numeric::WriteNumericSink
+{
+ LocalUser* const user;
+
+ public:
+ WriteNumericSink(LocalUser* u)
+ : user(u)
+ {
+ }
+
+ void operator()(Numeric& numeric) const
+ {
+ user->WriteNumeric(numeric);
+ }
+};
+
+class Numeric::WriteRemoteNumericSink
+{
+ User* const user;
+
+ public:
+ WriteRemoteNumericSink(User* u)
+ : user(u)
+ {
+ }
+
+ void operator()(Numeric& numeric) const
+ {
+ user->WriteRemoteNumeric(numeric);
+ }
+};
+
+template <char Sep, bool SendEmpty, typename Sink>
+class Numeric::GenericBuilder
+{
+ Sink sink;
+ Numeric numeric;
+ const std::string::size_type max;
+
+ bool HasRoom(const std::string::size_type additional) const
+ {
+ return (numeric.GetParams().back().size() + additional <= max);
+ }
+
+ public:
+ GenericBuilder(Sink s, unsigned int num, bool addparam = true, size_t additionalsize = 0)
+ : sink(s)
+ , numeric(num)
+ , max(ServerInstance->Config->Limits.MaxLine - ServerInstance->Config->ServerName.size() - additionalsize - 10)
+ {
+ if (addparam)
+ numeric.push(std::string());
+ }
+
+ Numeric& GetNumeric() { return numeric; }
+
+ void Add(const std::string& entry)
+ {
+ if (!HasRoom(entry.size()))
+ Flush();
+ numeric.GetParams().back().append(entry).push_back(Sep);
+ }
+
+ void Add(const std::string& entry1, const std::string& entry2)
+ {
+ if (!HasRoom(entry1.size() + entry2.size()))
+ Flush();
+ numeric.GetParams().back().append(entry1).append(entry2).push_back(Sep);
+ }
+
+ void Flush()
+ {
+ std::string& data = numeric.GetParams().back();
+ if (IsEmpty())
+ {
+ if (!SendEmpty)
+ return;
+ }
+ else
+ {
+ data.erase(data.size()-1);
+ }
+
+ sink(numeric);
+ data.clear();
+ }
+
+ bool IsEmpty() const { return (numeric.GetParams().back().empty()); }
+};
+
+template <char Sep, bool SendEmpty>
+class Numeric::Builder : public GenericBuilder<Sep, SendEmpty, WriteNumericSink>
+{
+ public:
+ Builder(LocalUser* user, unsigned int num, bool addparam = true, size_t additionalsize = 0)
+ : ::Numeric::GenericBuilder<Sep, SendEmpty, WriteNumericSink>(WriteNumericSink(user), num, addparam, additionalsize + user->nick.size())
+ {
+ }
+};
+
+template <unsigned int NumStaticParams, bool SendEmpty, typename Sink>
+class Numeric::GenericParamBuilder
+{
+ Sink sink;
+ Numeric numeric;
+ std::string::size_type currlen;
+ std::string::size_type max;
+
+ bool HasRoom(const std::string::size_type additional) const
+ {
+ return (currlen + additional <= max);
+ }
+
+ public:
+ GenericParamBuilder(Sink s, unsigned int num, size_t additionalsize)
+ : sink(s)
+ , numeric(num)
+ , currlen(0)
+ , max(ServerInstance->Config->Limits.MaxLine - ServerInstance->Config->ServerName.size() - additionalsize - 10)
+ {
+ }
+
+ void AddStatic(const std::string& entry)
+ {
+ max -= (entry.length() + 1);
+ numeric.GetParams().push_back(entry);
+ }
+
+ void Add(const std::string& entry)
+ {
+ if (!HasRoom(entry.size()))
+ Flush();
+
+ currlen += entry.size() + 1;
+ numeric.GetParams().push_back(entry);
+ }
+
+ void Flush()
+ {
+ if ((!SendEmpty) && (IsEmpty()))
+ return;
+
+ sink(numeric);
+ currlen = 0;
+ numeric.GetParams().erase(numeric.GetParams().begin() + NumStaticParams, numeric.GetParams().end());
+ }
+
+ bool IsEmpty() const { return (numeric.GetParams().size() <= NumStaticParams); }
+};
+
+template <unsigned int NumStaticParams, bool SendEmpty>
+class Numeric::ParamBuilder : public GenericParamBuilder<NumStaticParams, SendEmpty, WriteNumericSink>
+{
+ public:
+ ParamBuilder(LocalUser* user, unsigned int num)
+ : ::Numeric::GenericParamBuilder<NumStaticParams, SendEmpty, WriteNumericSink>(WriteNumericSink(user), num, user->nick.size())
+ {
+ }
+};
diff --git a/include/numerics.h b/include/numerics.h
index 2418730d2..0d5537278 100644
--- a/include/numerics.h
+++ b/include/numerics.h
@@ -35,7 +35,7 @@
* Please note that the list may not be exhaustive, it'll be done when I have
* nothing better to do with my time. -- w00t (jul 13, 2008)
*/
-enum Numerics
+enum
{
/*
* Reply range of numerics.
@@ -73,7 +73,8 @@ enum Numerics
RPL_MAPUSERS = 270, // insp-specific
RPL_AWAY = 301,
-
+ RPL_USERHOST = 302,
+ RPL_ISON = 303,
RPL_SYNTAX = 304, // insp-specific
RPL_UNAWAY = 305,
@@ -98,10 +99,12 @@ enum Numerics
RPL_TOPIC = 332,
RPL_TOPICTIME = 333, // not RFC, extremely common though
+ RPL_USERIP = 340,
RPL_INVITING = 341,
RPL_INVITELIST = 346, // insp-specific (stolen from ircu)
RPL_ENDOFINVITELIST = 347, // insp-specific (stolen from ircu)
RPL_VERSION = 351,
+ RPL_WHOREPLY = 352,
RPL_NAMREPLY = 353,
RPL_LINKS = 364,
RPL_ENDOFLINKS = 365,
@@ -134,6 +137,7 @@ enum Numerics
ERR_NOTEXTTOSEND = 412,
ERR_UNKNOWNCOMMAND = 421,
ERR_NOMOTD = 422,
+ ERR_NONICKNAMEGIVEN = 431,
ERR_ERRONEUSNICKNAME = 432,
ERR_NICKNAMEINUSE = 433,
ERR_NORULES = 434, // unrealircd
@@ -144,6 +148,7 @@ enum Numerics
ERR_NOTREGISTERED = 451,
ERR_NEEDMOREPARAMS = 461,
ERR_ALREADYREGISTERED = 462,
+ ERR_YOUREBANNEDCREEP = 465,
ERR_UNKNOWNMODE = 472,
/*
@@ -190,6 +195,9 @@ enum Numerics
ERR_CHANOPEN = 713,
ERR_KNOCKONCHAN = 714,
+ RPL_OTHERUMODEIS = 803, // insp-specific
+ RPL_OTHERSNOMASKIS = 804, // insp-specific
+
ERR_WORDFILTERED = 936, // insp-specific, would be nice if we could get rid of this..
ERR_CANTUNLOADMODULE = 972, // insp-specific
RPL_UNLOADEDMODULE = 973, // insp-specific
diff --git a/include/parammode.h b/include/parammode.h
index b0005262e..b00082bd6 100644
--- a/include/parammode.h
+++ b/include/parammode.h
@@ -56,7 +56,7 @@ class ParamMode : public ParamModeBase
*/
ParamMode(Module* Creator, const std::string& Name, char modeletter, ParamSpec ps = PARAM_SETONLY)
: ParamModeBase(Creator, Name, modeletter, ps)
- , ext("parammode_" + Name, Creator)
+ , ext("parammode_" + Name, ExtensionItem::EXT_CHANNEL, Creator)
{
}
diff --git a/include/protocol.h b/include/protocol.h
index 01eb145f1..2e512f11a 100644
--- a/include/protocol.h
+++ b/include/protocol.h
@@ -98,33 +98,12 @@ class CoreExport ProtocolInterface
*/
virtual void SendMetaData(const std::string& key, const std::string& data) { }
- /** Send a topic change for a channel
- * @param channel The channel to change the topic for.
- * @param topic The new topic to use for the channel.
- */
- virtual void SendTopic(Channel* channel, std::string &topic) { }
-
- /** Send mode changes for an object.
- * @param source The source of the mode change
- * @param usertarget The target user, NULL if the target is a channel
- * @param chantarget The target channel, NULL if the target is a user
- * @param modedata The mode changes to send.
- * @param translate A list of translation types
- */
- virtual void SendMode(User* source, User* usertarget, Channel* chantarget, const parameterlist& modedata, const std::vector<TranslateType>& translate) { }
-
/** Send a notice to users with a given snomask.
* @param snomask The snomask required for the message to be sent.
* @param text The message to send.
*/
virtual void SendSNONotice(char snomask, const std::string& text) { }
- /** Send raw data to a remote client.
- * @param target The user to push data to.
- * @param rawline The raw IRC protocol line to deliver (":me NOTICE you :foo", whatever).
- */
- virtual void PushToClient(User* target, const std::string &rawline) { }
-
/** Send a message to a channel.
* @param target The channel to message.
* @param status The status character (e.g. %) required to recieve.
diff --git a/include/server.h b/include/server.h
index 3d8854734..e54a379bc 100644
--- a/include/server.h
+++ b/include/server.h
@@ -26,9 +26,10 @@ class CoreExport Server : public classbase
*/
const std::string name;
- /** The description of this server
+ /** The description of this server.
+ * This can be updated by the protocol module (for remote servers) or by a rehash (for the local server).
*/
- const std::string description;
+ std::string description;
/** True if this server is ulined
*/
@@ -38,6 +39,10 @@ class CoreExport Server : public classbase
*/
bool silentuline;
+ /** Allow ConfigReaderThread to update the description on a rehash
+ */
+ friend class ConfigReaderThread;
+
public:
Server(const std::string& srvname, const std::string& srvdesc)
: name(srvname), description(srvdesc), uline(false), silentuline(false) { }
diff --git a/include/snomasks.h b/include/snomasks.h
index df4faab47..bd08773e9 100644
--- a/include/snomasks.h
+++ b/include/snomasks.h
@@ -72,7 +72,7 @@ class Snomask
* Modules and the core can enable and disable snomask characters. If they do,
* then sending snomasks using these characters becomes possible.
*/
-class CoreExport SnomaskManager
+class CoreExport SnomaskManager : public fakederef<SnomaskManager>
{
Snomask masks[26];
diff --git a/include/socket.h b/include/socket.h
index c292b7010..427ee9fe7 100644
--- a/include/socket.h
+++ b/include/socket.h
@@ -127,7 +127,6 @@ namespace irc
}
}
-#include "iohook.h"
#include "socketengine.h"
/** This class handles incoming connections on client ports.
* It will create a new User for every valid connection
@@ -142,28 +141,35 @@ class CoreExport ListenSocket : public EventHandler
/** Human-readable bind description */
std::string bind_desc;
- /** The IOHook provider which handles connections on this socket,
- * NULL if there is none.
+ class IOHookProvRef : public dynamic_reference_nocheck<IOHookProvider>
+ {
+ public:
+ IOHookProvRef()
+ : dynamic_reference_nocheck<IOHookProvider>(NULL, std::string())
+ {
+ }
+ };
+
+ typedef TR1NS::array<IOHookProvRef, 2> IOHookProvList;
+
+ /** IOHook providers for handling connections on this socket,
+ * may be empty.
*/
- dynamic_reference_nocheck<IOHookProvider> iohookprov;
+ IOHookProvList iohookprovs;
/** Create a new listening socket
*/
ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_to);
- /** Handle an I/O event
- */
- void HandleEvent(EventType et, int errornum = 0);
/** Close the socket
*/
~ListenSocket();
- /** Handles sockets internals crap of a connection, convenience wrapper really
+ /** Handles new connections, called by the socket engine
*/
- void AcceptInternal();
+ void OnEventHandlerRead() CXX11_OVERRIDE;
/** Inspects the bind block belonging to this socket to set the name of the IO hook
* provider which this socket will use for incoming connections.
- * @return True if the IO hook provider was found or none was given, false otherwise.
*/
- bool ResetIOHookProvider();
+ void ResetIOHookProvider();
};
diff --git a/include/socketengine.h b/include/socketengine.h
index 895457b89..b00643952 100644
--- a/include/socketengine.h
+++ b/include/socketengine.h
@@ -29,22 +29,13 @@
#include "socket.h"
#include "base.h"
-/** Types of event an EventHandler may receive.
- * EVENT_READ is a readable file descriptor,
- * and EVENT_WRITE is a writeable file descriptor.
- * EVENT_ERROR can always occur, and indicates
- * a write error or read error on the socket,
- * e.g. EOF condition or broken pipe.
- */
-enum EventType
-{
- /** Read event */
- EVENT_READ = 0,
- /** Write event */
- EVENT_WRITE = 1,
- /** Error event */
- EVENT_ERROR = 2
-};
+#ifndef _WIN32
+#include <sys/uio.h>
+#endif
+
+#ifndef IOV_MAX
+#define IOV_MAX 1024
+#endif
/**
* Event mask for SocketEngine events
@@ -93,7 +84,7 @@ enum EventMask
* EINPROGRESS. An event MAY be sent at any time that writes will not
* block.
*
- * Before calling HandleEvent, a socket engine MAY change the state of
+ * Before calling OnEventHandler*(), a socket engine MAY change the state of
* the FD back to FD_WANT_EDGE_WRITE if it is simpler (for example, if a
* one-shot notification was registered). If further writes are needed,
* it is the responsibility of the event handler to change the state to
@@ -107,7 +98,7 @@ enum EventMask
*/
FD_WANT_EDGE_WRITE = 0x80,
/** Request a one-shot poll-style write notification. The socket will
- * return to the FD_WANT_NO_WRITE state before HandleEvent is called.
+ * return to the FD_WANT_NO_WRITE state before OnEventHandler*() is called.
*/
FD_WANT_SINGLE_WRITE = 0x100,
@@ -115,17 +106,17 @@ enum EventMask
FD_WANT_WRITE_MASK = 0x1F0,
/** Add a trial read. During the next DispatchEvents invocation, this
- * will call HandleEvent with EVENT_READ unless reads are known to be
+ * will call OnEventHandlerRead() unless reads are known to be
* blocking.
*/
FD_ADD_TRIAL_READ = 0x1000,
/** Assert that reads are known to block. This cancels FD_ADD_TRIAL_READ.
- * Reset by SE before running EVENT_READ
+ * Reset by SE before running OnEventHandlerRead().
*/
FD_READ_WILL_BLOCK = 0x2000,
/** Add a trial write. During the next DispatchEvents invocation, this
- * will call HandleEvent with EVENT_WRITE unless writes are known to be
+ * will call OnEventHandlerWrite() unless writes are known to be
* blocking.
*
* This could be used to group several writes together into a single
@@ -134,7 +125,7 @@ enum EventMask
*/
FD_ADD_TRIAL_WRITE = 0x4000,
/** Assert that writes are known to block. This cancels FD_ADD_TRIAL_WRITE.
- * Reset by SE before running EVENT_WRITE
+ * Reset by SE before running OnEventHandlerWrite().
*/
FD_WRITE_WILL_BLOCK = 0x8000,
@@ -145,17 +136,14 @@ enum EventMask
/** This class is a basic I/O handler class.
* Any object which wishes to receive basic I/O events
* from the socketengine must derive from this class and
- * implement the HandleEvent() method. The derived class
+ * implement the OnEventHandler*() methods. The derived class
* must then be added to SocketEngine using the method
* SocketEngine::AddFd(), after which point the derived
- * class will receive events to its HandleEvent() method.
- * The derived class should also implement one of Readable()
- * and Writeable(). In the current implementation, only
- * Readable() is used. If this returns true, the socketengine
- * inserts a readable socket. If it is false, the socketengine
- * inserts a writeable socket. The derived class should never
- * change the value this function returns without first
- * deleting the socket from the socket engine. The only
+ * class will receive events to its OnEventHandler*() methods.
+ * The event mask passed to SocketEngine::AddFd() determines
+ * what events the EventHandler gets notified about and with
+ * what semantics. SocketEngine::ChangeEventMask() can be
+ * called to update the event mask later. The only
* requirement beyond this for an event handler is that it
* must have a file descriptor. What this file descriptor
* is actually attached to is completely up to you.
@@ -199,16 +187,20 @@ class CoreExport EventHandler : public classbase
*/
virtual ~EventHandler() {}
- /** Process an I/O event.
- * You MUST implement this function in your derived
- * class, and it will be called whenever read or write
- * events are received.
- * @param et either one of EVENT_READ for read events,
- * EVENT_WRITE for write events and EVENT_ERROR for
- * error events.
- * @param errornum The error code which goes with an EVENT_ERROR.
+ /** Called by the socket engine in case of a read event
*/
- virtual void HandleEvent(EventType et, int errornum = 0) = 0;
+ virtual void OnEventHandlerRead() = 0;
+
+ /** Called by the socket engine in case of a write event.
+ * The default implementation does nothing.
+ */
+ virtual void OnEventHandlerWrite();
+
+ /** Called by the socket engine in case of an error event.
+ * The default implementation does nothing.
+ * @param errornum Error code
+ */
+ virtual void OnEventHandlerError(int errornum);
friend class SocketEngine;
};
@@ -252,11 +244,19 @@ class CoreExport SocketEngine
*/
Statistics() : lastempty(0), TotalEvents(0), ReadEvents(0), WriteEvents(0), ErrorEvents(0) { }
- /** Increase the counters for bytes sent/received in this second.
- * @param len_in Bytes received, 0 if updating number of bytes written.
- * @param len_out Bytes sent, 0 if updating number of bytes read.
+ /** Update counters for network data received.
+ * This should be called after every read-type syscall.
+ * @param len_in Number of bytes received, or -1 for error, as typically
+ * returned by a read-style syscall.
+ */
+ void UpdateReadCounters(int len_in);
+
+ /** Update counters for network data sent.
+ * This should be called after every write-type syscall.
+ * @param len_out Number of bytes sent, or -1 for error, as typically
+ * returned by a read-style syscall.
*/
- void Update(size_t len_in, size_t len_out);
+ void UpdateWriteCounters(int len_out);
/** Get data transfer statistics.
* @param kbitspersec_in Filled with incoming traffic in this second in kbit/s.
@@ -306,6 +306,12 @@ class CoreExport SocketEngine
}
public:
+#ifndef _WIN32
+ typedef iovec IOVector;
+#else
+ typedef WindowsIOVec IOVector;
+#endif
+
/** Constructor.
* The constructor transparently initializes
* the socket engine which the ircd is using.
@@ -327,7 +333,7 @@ public:
/** Add an EventHandler object to the engine. Use AddFd to add a file
* descriptor to the engine and have the socket engine monitor it. You
* must provide an object derived from EventHandler which implements
- * HandleEvent().
+ * the required OnEventHandler*() methods.
* @param eh An event handling object to add
* @param event_mask The initial event mask for the object
*/
@@ -386,8 +392,7 @@ public:
* this doesn't wait long, only a couple of milliseconds. It returns the
* number of events which occurred during this call. This method will
* dispatch events to their handlers by calling their
- * EventHandler::HandleEvent() methods with the necessary EventType
- * value.
+ * EventHandler::OnEventHandler*() methods.
* @return The number of events which have occured.
*/
static int DispatchEvents();
@@ -434,6 +439,27 @@ public:
*/
static int Send(EventHandler* fd, const void *buf, size_t len, int flags);
+ /** Abstraction for vector write function writev().
+ * This function should emulate its namesake system call exactly.
+ * @param fd EventHandler to send data with
+ * @param iov Array of IOVectors containing the buffers to send and their lengths in the platform's
+ * native format.
+ * @param count Number of elements in iov.
+ * @return This method should return exactly the same values as the system call it emulates.
+ */
+ static int WriteV(EventHandler* fd, const IOVector* iov, int count);
+
+#ifdef _WIN32
+ /** Abstraction for vector write function writev() that accepts a POSIX format iovec.
+ * This function should emulate its namesake system call exactly.
+ * @param fd EventHandler to send data with
+ * @param iov Array of iovecs containing the buffers to send and their lengths in POSIX format.
+ * @param count Number of elements in iov.
+ * @return This method should return exactly the same values as the system call it emulates.
+ */
+ static int WriteV(EventHandler* fd, const iovec* iov, int count);
+#endif
+
/** Abstraction for BSD sockets recv(2).
* This function should emulate its namesake system call exactly.
* @param fd This version of the call takes an EventHandler instead of a bare file descriptor.
diff --git a/include/stdalgo.h b/include/stdalgo.h
index afbd763fb..f4465963a 100644
--- a/include/stdalgo.h
+++ b/include/stdalgo.h
@@ -64,6 +64,73 @@ namespace stdalgo
}
}
+ namespace string
+ {
+ /** Get underlying C string of the string passed as parameter. Useful in template functions.
+ * @param str C string
+ * @return Same as input
+ */
+ inline const char* tocstr(const char* str)
+ {
+ return str;
+ }
+
+ /** Get underlying C string of the string passed as parameter. Useful in template functions.
+ * @param str std::string object
+ * @return str.c_str()
+ */
+ inline const char* tocstr(const std::string& str)
+ {
+ return str.c_str();
+ }
+
+ /** Check if two strings are equal case insensitively.
+ * @param str1 First string to compare.
+ * @param str2 Second string to compare.
+ * @return True if the strings are equal case-insensitively, false otherwise.
+ */
+ template <typename S1, typename S2>
+ inline bool equalsci(const S1& str1, const S2& str2)
+ {
+ return (!strcasecmp(tocstr(str1), tocstr(str2)));
+ }
+
+ /** Replace first occurrence of a substring ('target') in a string ('str') with another string ('replacement').
+ * @param str String to perform replacement in
+ * @param target String to replace
+ * @param replacement String to put in place of 'target'
+ * @return True if 'target' was replaced with 'replacement', false if it was not found in 'str'.
+ */
+ template<typename CharT, typename Traits, typename Alloc>
+ inline bool replace(std::basic_string<CharT, Traits, Alloc>& str, const std::basic_string<CharT, Traits, Alloc>& target, const std::basic_string<CharT, Traits, Alloc>& replacement)
+ {
+ const typename std::basic_string<CharT, Traits, Alloc>::size_type p = str.find(target);
+ if (p == std::basic_string<CharT, Traits, Alloc>::npos)
+ return false;
+ str.replace(p, target.size(), replacement);
+ return true;
+ }
+
+ /** Replace all occurrences of a string ('target') in a string ('str') with another string ('replacement').
+ * @param str String to perform replacement in
+ * @param target String to replace
+ * @param replacement String to put in place of 'target'
+ */
+ template<typename CharT, typename Traits, typename Alloc>
+ inline void replace_all(std::basic_string<CharT, Traits, Alloc>& str, const std::basic_string<CharT, Traits, Alloc>& target, const std::basic_string<CharT, Traits, Alloc>& replacement)
+ {
+ if (target.empty())
+ return;
+
+ typename std::basic_string<CharT, Traits, Alloc>::size_type p = 0;
+ while ((p = str.find(target, p)) != std::basic_string<CharT, Traits, Alloc>::npos)
+ {
+ str.replace(p, target.size(), replacement);
+ p += replacement.size();
+ }
+ }
+ }
+
/**
* Deleter that uses operator delete to delete the item
*/
@@ -94,4 +161,34 @@ namespace stdalgo
{
std::for_each(cont.begin(), cont.end(), defaultdeleter<T>());
}
+
+ /**
+ * Remove an element from a container
+ * @param cont Container to remove the element from
+ * @param val Value of the element to look for and remove
+ * @return True if the element was found and removed, false otherwise
+ */
+ template <template<typename, typename> class Cont, typename T, typename Alloc>
+ inline bool erase(Cont<T, Alloc>& cont, const T& val)
+ {
+ const typename Cont<T, Alloc>::iterator it = std::find(cont.begin(), cont.end(), val);
+ if (it != cont.end())
+ {
+ cont.erase(it);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if an element with the given value is in a container. Equivalent to (std::find(cont.begin(), cont.end(), val) != cont.end()).
+ * @param cont Container to find the element in
+ * @param val Value of the element to look for
+ * @return True if the element was found in the container, false otherwise
+ */
+ template <template<typename, typename> class Cont, typename T, typename Alloc>
+ inline bool isin(const Cont<T, Alloc>& cont, const T& val)
+ {
+ return (std::find(cont.begin(), cont.end(), val) != cont.end());
+ }
}
diff --git a/include/threadengine.h b/include/threadengine.h
index 39f150566..fec1bbb96 100644
--- a/include/threadengine.h
+++ b/include/threadengine.h
@@ -26,8 +26,6 @@
#include "config.h"
#include "base.h"
-class ThreadData;
-
/** Derive from this class to implement your own threaded sections of
* code. Be sure to keep your code thread-safe and not prone to deadlocks
* and race conditions if you MUST use threading!
@@ -38,6 +36,15 @@ class CoreExport Thread
/** Set to true when the thread is to exit
*/
bool ExitFlag;
+
+ /** Opaque thread state managed by the ThreadEngine
+ */
+ ThreadEngine::ThreadState state;
+
+ /** ThreadEngine manages Thread::state
+ */
+ friend class ThreadEngine;
+
protected:
/** Get thread's current exit status.
* (are we being asked to exit?)
@@ -47,19 +54,12 @@ class CoreExport Thread
return ExitFlag;
}
public:
- /** Opaque thread state managed by threading engine
- */
- ThreadData* state;
-
/** Set Creator to NULL at this point
*/
- Thread() : ExitFlag(false), state(NULL)
+ Thread() : ExitFlag(false)
{
}
- /* If the thread is running, you MUST join BEFORE deletion */
- virtual ~Thread();
-
/** Override this method to put your actual
* threaded code here.
*/
diff --git a/include/threadengines/threadengine_pthread.h b/include/threadengines/threadengine_pthread.h
index 253e8d223..ca3354260 100644
--- a/include/threadengines/threadengine_pthread.h
+++ b/include/threadengines/threadengine_pthread.h
@@ -36,14 +36,12 @@
class CoreExport ThreadEngine
{
public:
-
- /** Constructor.
- */
- ThreadEngine();
-
- /** Destructor
+ /** Per-thread state, present in each Thread object, managed by the ThreadEngine
*/
- virtual ~ThreadEngine();
+ struct ThreadState
+ {
+ pthread_t pthread_id;
+ };
/** Create a new thread. This takes an already allocated
* Thread* pointer and initializes it to use this threading
@@ -53,20 +51,17 @@ class CoreExport ThreadEngine
*/
void Start(Thread* thread_to_init);
- /** Returns the thread engine's name for display purposes
- * @return The thread engine name
+ /** Stop a thread gracefully.
+ * First, this function asks the thread to terminate by calling Thread::SetExitFlag().
+ * Next, it waits until the thread terminates (on the operating system level). Finally,
+ * all OS-level resources associated with the thread are released. The Thread instance
+ * passed to the function is NOT freed.
+ * When this function returns, the thread is stopped and you can destroy it or restart it
+ * at a later point.
+ * Stopping a thread that is not running is a bug.
+ * @param thread The thread to stop.
*/
- const std::string GetName()
- {
- return "posix-thread";
- }
-};
-
-class CoreExport ThreadData
-{
- public:
- pthread_t pthread_id;
- void FreeThread(Thread* toFree);
+ void Stop(Thread* thread);
};
/** The Mutex class represents a mutex, which can be used to keep threads
@@ -79,7 +74,7 @@ class CoreExport ThreadData
*/
class CoreExport Mutex
{
- private:
+ protected:
pthread_mutex_t putex;
public:
/** Constructor.
@@ -108,33 +103,20 @@ class CoreExport Mutex
}
};
-class ThreadQueueData
+class ThreadQueueData : public Mutex
{
- pthread_mutex_t mutex;
pthread_cond_t cond;
public:
ThreadQueueData()
{
- pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
}
~ThreadQueueData()
{
- pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
- void Lock()
- {
- pthread_mutex_lock(&mutex);
- }
-
- void Unlock()
- {
- pthread_mutex_unlock(&mutex);
- }
-
void Wakeup()
{
pthread_cond_signal(&cond);
@@ -142,7 +124,7 @@ class ThreadQueueData
void Wait()
{
- pthread_cond_wait(&cond, &mutex);
+ pthread_cond_wait(&cond, &putex);
}
};
diff --git a/include/threadengines/threadengine_win32.h b/include/threadengines/threadengine_win32.h
index 59848bd44..aac7b8b97 100644
--- a/include/threadengines/threadengine_win32.h
+++ b/include/threadengines/threadengine_win32.h
@@ -38,10 +38,12 @@ class Thread;
class CoreExport ThreadEngine
{
public:
-
- ThreadEngine();
-
- virtual ~ThreadEngine();
+ /** Per-thread state, present in each Thread object, managed by the ThreadEngine
+ */
+ struct ThreadState
+ {
+ HANDLE handle;
+ };
static DWORD WINAPI Entry(void* parameter);
@@ -53,20 +55,17 @@ class CoreExport ThreadEngine
*/
void Start(Thread* thread_to_init);
- /** Returns the thread engine's name for display purposes
- * @return The thread engine name
+ /** Stop a thread gracefully.
+ * First, this function asks the thread to terminate by calling Thread::SetExitFlag().
+ * Next, it waits until the thread terminates (on the operating system level). Finally,
+ * all OS-level resources associated with the thread are released. The Thread instance
+ * passed to the function is NOT freed.
+ * When this function returns, the thread is stopped and you can destroy it or restart it
+ * at a later point.
+ * Stopping a thread that is not running is a bug.
+ * @param thread The thread to stop.
*/
- const std::string GetName()
- {
- return "windows-thread";
- }
-};
-
-class CoreExport ThreadData
-{
- public:
- HANDLE handle;
- void FreeThread(Thread* toFree);
+ void Stop(Thread* thread);
};
/** The Mutex class represents a mutex, which can be used to keep threads
@@ -100,9 +99,8 @@ class CoreExport Mutex
}
};
-class ThreadQueueData
+class ThreadQueueData : public Mutex
{
- CRITICAL_SECTION mutex;
HANDLE event;
public:
ThreadQueueData()
@@ -110,23 +108,11 @@ class ThreadQueueData
event = CreateEvent(NULL, false, false, NULL);
if (event == NULL)
throw CoreException("CreateEvent() failed in ThreadQueueData::ThreadQueueData()!");
- InitializeCriticalSection(&mutex);
}
~ThreadQueueData()
{
CloseHandle(event);
- DeleteCriticalSection(&mutex);
- }
-
- void Lock()
- {
- EnterCriticalSection(&mutex);
- }
-
- void Unlock()
- {
- LeaveCriticalSection(&mutex);
}
void Wakeup()
@@ -136,9 +122,9 @@ class ThreadQueueData
void Wait()
{
- LeaveCriticalSection(&mutex);
+ Unlock();
WaitForSingleObject(event, INFINITE);
- EnterCriticalSection(&mutex);
+ Lock();
}
};
diff --git a/include/timer.h b/include/timer.h
index 503fa82a2..a597427e3 100644
--- a/include/timer.h
+++ b/include/timer.h
@@ -50,15 +50,9 @@ class CoreExport Timer
public:
/** Default constructor, initializes the triggering time
* @param secs_from_now The number of seconds from now to trigger the timer
- * @param now The time now
* @param repeating Repeat this timer every secs_from_now seconds if set to true
*/
- Timer(unsigned int secs_from_now, time_t now, bool repeating = false)
- {
- trigger = now + secs_from_now;
- secs = secs_from_now;
- repeat = repeating;
- }
+ Timer(unsigned int secs_from_now, bool repeating = false);
/** Default destructor, removes the timer from the timer manager
*/
@@ -117,14 +111,14 @@ class CoreExport Timer
}
};
-typedef std::multimap<time_t, Timer*> TimerMap;
-
/** This class manages sets of Timers, and triggers them at their defined times.
* This will ensure timers are not missed, as well as removing timers that have
* expired and allowing the addition of new ones.
*/
class CoreExport TimerManager
{
+ typedef std::multimap<time_t, Timer*> TimerMap;
+
/** A list of all pending timers
*/
TimerMap Timers;
diff --git a/include/typedefs.h b/include/typedefs.h
index 336084c55..879ef0627 100644
--- a/include/typedefs.h
+++ b/include/typedefs.h
@@ -31,6 +31,7 @@ class Extensible;
class FakeUser;
class InspIRCd;
class Invitation;
+class IOHookProvider;
class LocalUser;
class Membership;
class Module;
@@ -54,25 +55,9 @@ struct ModResult;
typedef TR1NS::unordered_map<std::string, User*, irc::insensitive, irc::StrHashComp> user_hash;
typedef TR1NS::unordered_map<std::string, Channel*, irc::insensitive, irc::StrHashComp> chan_hash;
-/** A list holding local users, this is the type of UserManager::local_users
- */
-typedef intrusive_list<LocalUser> LocalUserList;
-
/** A list of failed port bindings, used for informational purposes on startup */
typedef std::vector<std::pair<std::string, std::string> > FailedPortList;
-/** Holds a complete list of all allow and deny tags from the configuration file (connection classes)
- */
-typedef std::vector<reference<ConnectClass> > ClassVector;
-
-/** Typedef for the list of user-channel records for a user
- */
-typedef intrusive_list<Membership> UserChanList;
-
-/** Shorthand for an iterator into a UserChanList
- */
-typedef UserChanList::iterator UCListIter;
-
/** List of channels to consider when building the neighbor list of a user
*/
typedef std::vector<Membership*> IncludeChanList;
@@ -94,23 +79,9 @@ typedef ConfigDataHash::const_iterator ConfigIter;
/** Iterator pair, used for tag-name ranges */
typedef std::pair<ConfigIter,ConfigIter> ConfigTagList;
-/** Index of valid oper blocks and types */
-typedef std::map<std::string, reference<OperInfo> > OperIndex;
-
/** Files read by the configuration */
typedef std::map<std::string, file_cache> ConfigFileCache;
-/** A hash of commands used by the core
- */
-typedef TR1NS::unordered_map<std::string, Command*> Commandtable;
-
-/** Membership list of a channel */
-typedef std::map<User*, Membership*> UserMembList;
-/** Iterator of UserMembList */
-typedef UserMembList::iterator UserMembIter;
-/** const Iterator of UserMembList */
-typedef UserMembList::const_iterator UserMembCIter;
-
/** Generic user list, used for exceptions */
typedef std::set<User*> CUList;
@@ -128,7 +99,7 @@ typedef std::map<std::string, XLineFactory*> XLineFactMap;
/** A map of XLines indexed by string
*/
-typedef std::map<irc::string, XLine *> XLineLookup;
+typedef std::map<std::string, XLine*, irc::insensitive_swo> XLineLookup;
/** A map of XLineLookup maps indexed by string
*/
@@ -141,3 +112,8 @@ typedef XLineContainer::iterator ContainerIter;
/** An interator in an XLineLookup
*/
typedef XLineLookup::iterator LookupIter;
+
+namespace Stats
+{
+ class Context;
+}
diff --git a/include/usermanager.h b/include/usermanager.h
index a419916f2..1b1b0b600 100644
--- a/include/usermanager.h
+++ b/include/usermanager.h
@@ -21,7 +21,7 @@
#include <list>
-class CoreExport UserManager
+class CoreExport UserManager : public fakederef<UserManager>
{
public:
struct CloneCounts
@@ -39,6 +39,10 @@ class CoreExport UserManager
*/
typedef std::vector<User*> OperList;
+ /** A list holding local users
+ */
+ typedef insp::intrusive_list<LocalUser> LocalList;
+
private:
/** Map of IP addresses for clone counting
*/
@@ -48,6 +52,15 @@ class CoreExport UserManager
*/
const CloneCounts zeroclonecounts;
+ /** Local client list, a list containing only local clients
+ */
+ LocalList local_users;
+
+ /** Last used already sent id, used when sending messages to neighbors to help determine whether the message has
+ * been sent to a particular user or not. See User::ForEachNeighbor() for more info.
+ */
+ already_sent_t already_sent_id;
+
public:
/** Constructor, initializes variables
*/
@@ -57,19 +70,14 @@ class CoreExport UserManager
*/
~UserManager();
- /** Client list, a hash_map containing all clients, local and remote
+ /** Nickname string -> User* map. Contains all users, including unregistered ones.
*/
user_hash clientlist;
- /** Client list stored by UUID. Contains all clients, and is updated
- * automatically by the constructor and destructor of User.
+ /** UUID -> User* map. Contains all users, including unregistered ones.
*/
user_hash uuidlist;
- /** Local client list, a list containing only local clients
- */
- LocalUserList local_users;
-
/** Oper list, a vector containing all local and remote opered users
*/
OperList all_opers;
@@ -79,13 +87,8 @@ class CoreExport UserManager
*/
unsigned int unregistered_count;
- /**
- * Reset the already_sent IDs so we don't wrap it around and drop a message
- * Also removes all expired invites
- */
- void GarbageCollect();
-
- /** Perform background user events such as PING checks
+ /** Perform background user events for all local users such as PING checks, registration timeouts,
+ * penalty management and recvq processing for users who have data in their recvq due to throttling.
*/
void DoBackgroundUserStuff();
@@ -95,22 +98,24 @@ class CoreExport UserManager
*/
bool AllModulesReportReady(LocalUser* user);
- /** Add a client to the system.
- * This will create a new User, insert it into the user_hash,
- * initialize it as not yet registered, and add it to the socket engine.
- * @param socket The socket id (file descriptor) this user is on
- * @param via The socket that this user connected using
+ /** Handle a client connection.
+ * Creates a new LocalUser object, inserts it into the appropriate containers,
+ * initializes it as not yet registered, and adds it to the socket engine.
+ *
+ * The new user may immediately be quit after being created, for example if the user limit
+ * is reached or if the user is banned.
+ * @param socket File descriptor of the connection
+ * @param via Listener socket that this user connected to
* @param client The IP address and client port of the user
* @param server The server IP address and port used by the user
- * @return This function has no return value, but a call to AddClient may remove the user.
*/
void AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
- /** Disconnect a user gracefully
+ /** Disconnect a user gracefully.
+ * When this method returns the user provided will be quit, but the User object will continue to be valid and will be deleted at the end of the current main loop iteration.
* @param user The user to remove
* @param quitreason The quit reason to show to normal users
* @param operreason The quit reason to show to opers, can be NULL if same as quitreason
- * @return Although this function has no return type, on exit the user provided will no longer exist.
*/
void QuitUser(User* user, const std::string& quitreason, const std::string* operreason = NULL);
@@ -126,6 +131,10 @@ class CoreExport UserManager
*/
void RemoveCloneCounts(User *user);
+ /** Rebuild clone counts. Required when <cidr> settings change.
+ */
+ void RehashCloneCounts();
+
/** Return the number of local and global clones of this user
* @param user The user to get the clone counts for
* @return The clone counts of this user. The returned reference is volatile - you
@@ -169,9 +178,19 @@ class CoreExport UserManager
*/
user_hash& GetUsers() { return clientlist; }
+ /** Get a list containing all local users
+ * @return A const list of local users
+ */
+ const LocalList& GetLocalUsers() const { return local_users; }
+
/** Send a server notice to all local users
* @param text The text format string to send
* @param ... The format arguments
*/
void ServerNoticeAll(const char* text, ...) CUSTOM_PRINTF(2, 3);
+
+ /** Retrieves the next already sent id, guaranteed to be not equal to any user's already_sent field
+ * @return Next already_sent id
+ */
+ already_sent_t NextAlreadySentId();
};
diff --git a/include/users.h b/include/users.h
index 0f8154bef..4939feb1e 100644
--- a/include/users.h
+++ b/include/users.h
@@ -135,6 +135,11 @@ struct CoreExport ConnectClass : public refcountbase
*/
bool resolvehostnames;
+ /**
+ * If non-empty the server ports which this user has to be using
+ */
+ insp::flat_set<int> ports;
+
/** Create a new connect class with no settings.
*/
ConnectClass(ConfigTag* tag, char type, const std::string& mask);
@@ -248,6 +253,22 @@ class CoreExport User : public Extensible
std::bitset<ModeParser::MODEID_MAX> modes;
public:
+ /** To execute a function for each local neighbor of a user, inherit from this class and
+ * pass an instance of it to User::ForEachNeighbor().
+ */
+ class ForEachNeighborHandler
+ {
+ public:
+ /** Method to execute for each local neighbor of a user.
+ * Derived classes must implement this.
+ * @param user Current neighbor
+ */
+ virtual void Execute(LocalUser* user) = 0;
+ };
+
+ /** List of Memberships for this user
+ */
+ typedef insp::intrusive_list<Membership> ChanList;
/** Hostname of connection.
* This should be valid as per RFC1035.
@@ -302,7 +323,7 @@ class CoreExport User : public Extensible
/** Channels this user is on
*/
- UserChanList chans;
+ ChanList chans;
/** The server the user is connected to.
*/
@@ -397,19 +418,21 @@ class CoreExport User : public Extensible
*/
bool IsNoticeMaskSet(unsigned char sm);
- /** Create a displayable mode string for this users umodes
- * @param showparameters The mode string
+ /** Get the mode letters of modes set on the user as a string.
+ * @param includeparams True to get the parameters of the modes as well. Defaults to false.
+ * @return Mode letters of modes set on the user and optionally the parameters of those modes, if any.
+ * The returned string always begins with a '+' character. If the user has no modes set, "+" is returned.
*/
- const char* FormatModes(bool showparameters = false);
+ std::string GetModeLetters(bool includeparams = false) const;
/** Returns true if a specific mode is set
* @param m The user mode
* @return True if the mode is set
*/
- bool IsModeSet(unsigned char m);
- bool IsModeSet(ModeHandler* mh);
- bool IsModeSet(ModeHandler& mh) { return IsModeSet(&mh); }
- bool IsModeSet(UserModeReference& moderef);
+ bool IsModeSet(unsigned char m) const;
+ bool IsModeSet(const ModeHandler* mh) const;
+ bool IsModeSet(const ModeHandler& mh) const { return IsModeSet(&mh); }
+ bool IsModeSet(UserModeReference& moderef) const;
/** Set a specific usermode to on or off
* @param m The user mode
@@ -439,11 +462,10 @@ class CoreExport User : public Extensible
/** Returns true or false if a user can set a privileged user or channel mode.
* This is done by looking up their oper type from User::oper, then referencing
* this to their oper classes, and checking the modes they can set.
- * @param mode The mode the check
- * @param type ModeType (MODETYPE_CHANNEL or MODETYPE_USER).
+ * @param mh Mode to check
* @return True if the user can set or unset this mode.
*/
- virtual bool HasModePermission(unsigned char mode, ModeType type);
+ virtual bool HasModePermission(const ModeHandler* mh) const;
/** Creates a usermask with real host.
* Takes a buffer to use and fills the given buffer with the hostmask in the format user\@host
@@ -462,15 +484,6 @@ class CoreExport User : public Extensible
*/
void Oper(OperInfo* info);
- /** Force a nickname change.
- * If the nickname change fails (for example, because the nick in question
- * already exists) this function will return false, and you must then either
- * output an error message, or quit the user for nickname collision.
- * @param newnick The nickname to change to
- * @return True if the nickchange was successful.
- */
- bool ForceNickChange(const std::string& newnick, time_t newts = 0) { return ChangeNick(newnick, true, newts); }
-
/** Oper down.
* This will clear the +o usermode and unset the user's oper type
*/
@@ -512,9 +525,114 @@ class CoreExport User : public Extensible
*/
void WriteNotice(const std::string& text) { this->WriteCommand("NOTICE", ":" + text); }
- void WriteNumeric(unsigned int numeric, const char* text, ...) CUSTOM_PRINTF(3, 4);
+ /** Send a NOTICE message from the local server to the user.
+ * @param text Text to send
+ */
+ virtual void WriteRemoteNotice(const std::string& text);
+
+ virtual void WriteRemoteNumeric(const Numeric::Numeric& numeric);
+
+ template <typename T1>
+ void WriteRemoteNumeric(unsigned int numeric, T1 p1)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(p1);
+ WriteRemoteNumeric(n);
+ }
+
+ template <typename T1, typename T2>
+ void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(p1);
+ n.push(p2);
+ WriteRemoteNumeric(n);
+ }
+
+ template <typename T1, typename T2, typename T3>
+ void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ WriteRemoteNumeric(n);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4>
+ void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ n.push(p4);
+ WriteRemoteNumeric(n);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ n.push(p4);
+ n.push(p5);
+ WriteRemoteNumeric(n);
+ }
+
+ void WriteNumeric(const Numeric::Numeric& numeric);
- void WriteNumeric(unsigned int numeric, const std::string &text);
+ template <typename T1>
+ void WriteNumeric(unsigned int numeric, T1 p1)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(p1);
+ WriteNumeric(n);
+ }
+
+ template <typename T1, typename T2>
+ void WriteNumeric(unsigned int numeric, T1 p1, T2 p2)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(p1);
+ n.push(p2);
+ WriteNumeric(n);
+ }
+
+ template <typename T1, typename T2, typename T3>
+ void WriteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ WriteNumeric(n);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4>
+ void WriteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ n.push(p4);
+ WriteNumeric(n);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ void WriteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
+ {
+ Numeric::Numeric n(numeric);
+ n.push(p1);
+ n.push(p2);
+ n.push(p3);
+ n.push(p4);
+ n.push(p5);
+ WriteNumeric(n);
+ }
/** Write text to this user, appending CR/LF and prepending :nick!user\@host of the user provided in the first parameter.
* @param user The user to prepend the :nick!user\@host of
@@ -541,26 +659,15 @@ class CoreExport User : public Extensible
*/
void WriteCommon(const char* text, ...) CUSTOM_PRINTF(2, 3);
- /** Write a quit message to all common users, as in User::WriteCommonExcept but with a specific
- * quit message for opers only.
- * @param normal_text Normal user quit message
- * @param oper_text Oper only quit message
+ /** Execute a function once for each local neighbor of this user. By default, the neighbors of a user are the users
+ * who have at least one common channel with the user. Modules are allowed to alter the set of neighbors freely.
+ * This function is used for example to send something conditionally to neighbors, or to send different messages
+ * to different users depending on their oper status.
+ * @param handler Function object to call, inherited from ForEachNeighborHandler.
+ * @param include_self True to include this user in the set of neighbors, false otherwise.
+ * Modules may override this. Has no effect if this user is not local.
*/
- void WriteCommonQuit(const std::string &normal_text, const std::string &oper_text);
-
- /** Dump text to a user target, splitting it appropriately to fit
- * @param linePrefix text to prefix each complete line with
- * @param textStream the text to send to the user
- */
- void SendText(const std::string& linePrefix, std::stringstream& textStream);
-
- /** Write to the user, routing the line if the user is remote.
- */
- virtual void SendText(const std::string& line) = 0;
-
- /** Write to the user, routing the line if the user is remote.
- */
- void SendText(const char* text, ...) CUSTOM_PRINTF(2, 3);
+ void ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self = true);
/** Return true if the user shares at least one channel with another user
* @param other The other user to compare the channel list against
@@ -597,11 +704,10 @@ class CoreExport User : public Extensible
bool ChangeName(const std::string& gecos);
/** Change a user's nick
- * @param newnick The new nick
- * @param force True if the change is being forced (should not be blocked by modes like +N)
+ * @param newnick The new nick. If equal to the users uuid, the nick change always succeeds.
* @return True if the change succeeded
*/
- bool ChangeNick(const std::string& newnick, bool force = false, time_t newts = 0);
+ bool ChangeNick(const std::string& newnick, time_t newts = 0);
/** Remove this user from all channels they are on, and delete any that are now empty.
* This is used by QUIT, and will not send part messages!
@@ -632,7 +738,7 @@ class CoreExport UserIOHandler : public StreamSocket
typedef unsigned int already_sent_t;
-class CoreExport LocalUser : public User, public InviteBase<LocalUser>, public intrusive_list_node<LocalUser>
+class CoreExport LocalUser : public User, public insp::intrusive_list_node<LocalUser>
{
public:
LocalUser(int fd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
@@ -711,7 +817,6 @@ class CoreExport LocalUser : public User, public InviteBase<LocalUser>, public i
*/
unsigned int CommandFloodPenalty;
- static already_sent_t already_sent_id;
already_sent_t already_sent;
/** Check if the user matches a G or K line, and disconnect them if they do.
@@ -735,30 +840,14 @@ class CoreExport LocalUser : public User, public InviteBase<LocalUser>, public i
void SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline = true);
- void SendText(const std::string& line);
void Write(const std::string& text);
void Write(const char*, ...) CUSTOM_PRINTF(2, 3);
- /** Returns the list of channels this user has been invited to but has not yet joined.
- * @return A list of channels the user is invited to
+ /** Send a NOTICE message from the local server to the user.
+ * The message will be sent even if the user is connected to a remote server.
+ * @param text Text to send
*/
- InviteList& GetInviteList();
-
- /** Returns true if a user is invited to a channel.
- * @param chan A channel to look up
- * @return True if the user is invited to the given channel
- */
- bool IsInvited(Channel* chan) { return (Invitation::Find(chan, this) != NULL); }
-
- /** Removes a channel from a users invite list.
- * This member function is called on successfully joining an invite only channel
- * to which the user has previously been invited, to clear the invitation.
- * @param chan The channel to remove the invite to
- * @return True if the user was invited to the channel and the invite was erased, false if the user wasn't invited
- */
- bool RemoveInvite(Channel* chan);
-
- void RemoveExpiredInvites();
+ void WriteRemoteNotice(const std::string& text) CXX11_OVERRIDE;
/** Returns true or false for if a user can execute a privilaged oper command.
* This is done by looking up their oper type from User::oper, then referencing
@@ -781,20 +870,24 @@ class CoreExport LocalUser : public User, public InviteBase<LocalUser>, public i
/** Returns true or false if a user can set a privileged user or channel mode.
* This is done by looking up their oper type from User::oper, then referencing
* this to their oper classes, and checking the modes they can set.
- * @param mode The mode the check
- * @param type ModeType (MODETYPE_CHANNEL or MODETYPE_USER).
+ * @param mh Mode to check
* @return True if the user can set or unset this mode.
*/
- bool HasModePermission(unsigned char mode, ModeType type);
+ bool HasModePermission(const ModeHandler* mh) const;
+
+ /** Change nick to uuid, unset REG_NICK and send a nickname overruled numeric.
+ * This is called when another user (either local or remote) needs the nick of this user and this user
+ * isn't registered.
+ */
+ void OverruleNick();
};
-class CoreExport RemoteUser : public User
+class RemoteUser : public User
{
public:
RemoteUser(const std::string& uid, Server* srv) : User(uid, srv, USERTYPE_REMOTE)
{
}
- virtual void SendText(const std::string& line);
};
class CoreExport FakeUser : public User
@@ -812,7 +905,6 @@ class CoreExport FakeUser : public User
}
virtual CullResult cull();
- virtual void SendText(const std::string& line);
virtual const std::string& GetFullHost();
virtual const std::string& GetFullRealHost();
};
@@ -834,12 +926,12 @@ inline FakeUser* IS_SERVER(User* u)
return u->usertype == USERTYPE_SERVER ? static_cast<FakeUser*>(u) : NULL;
}
-inline bool User::IsModeSet(ModeHandler* mh)
+inline bool User::IsModeSet(const ModeHandler* mh) const
{
return (modes[mh->GetId()]);
}
-inline bool User::IsModeSet(UserModeReference& moderef)
+inline bool User::IsModeSet(UserModeReference& moderef) const
{
if (!moderef)
return false;
diff --git a/include/xline.h b/include/xline.h
index fe044d0f2..c2ede29df 100644
--- a/include/xline.h
+++ b/include/xline.h
@@ -520,8 +520,7 @@ class CoreExport XLineManager
* will be expired and removed before the list is displayed.
* @param type The type of stats to show
* @param numeric The numeric to give to each result line
- * @param user The username making the query
- * @param results The string_list to receive the results
+ * @param stats Stats context
*/
- void InvokeStats(const std::string &type, int numeric, User* user, string_list &results);
+ void InvokeStats(const std::string& type, unsigned int numeric, Stats::Context& stats);
};
diff --git a/make/calcdep.pl b/make/calcdep.pl
index 376d19573..99355efa4 100755
--- a/make/calcdep.pl
+++ b/make/calcdep.pl
@@ -19,9 +19,21 @@
#
+BEGIN {
+ require 5.10.0;
+ unless (-f 'configure') {
+ print "Error: $0 must be run from the main source directory!\n";
+ exit 1;
+ }
+}
+
use strict;
-use warnings;
-use POSIX qw(getcwd);
+use warnings FATAL => qw(all);
+
+use constant {
+ BUILDPATH => $ENV{BUILDPATH},
+ SOURCEPATH => $ENV{SOURCEPATH}
+};
sub find_output;
sub gendep($);
@@ -36,21 +48,16 @@ run;
exit 0;
sub run() {
- my $build = $ENV{BUILDPATH};
- mkdir $build;
- chdir $build or die "Could not open build directory: $!";
+ mkdir BUILDPATH;
+ chdir BUILDPATH or die "Could not open build directory: $!";
unlink 'include';
- symlink "$ENV{SOURCEPATH}/include", 'include';
+ symlink "${\SOURCEPATH}/include", 'include';
mkdir $_ for qw/bin modules obj/;
-# BSD make has a horribly annoying bug resulting in an extra chdir of the make process
-# Create symlinks to work around it
- symlink "../$_", "obj/$_" for qw/bin coremods modules obj/;
- $build = getcwd();
open MAKE, '>real.mk' or die "Could not write real.mk: $!";
- chdir "$ENV{SOURCEPATH}/src";
+ chdir "${\SOURCEPATH}/src";
- if ($ENV{PURE_STATIC}) {
+ if ($ENV{INSPIRCD_STATIC}) {
run_static();
} else {
run_dynamic();
@@ -59,7 +66,6 @@ sub run() {
}
sub run_dynamic() {
- my $build = $ENV{BUILDPATH};
print MAKE <<END;
# DO NOT EDIT THIS FILE
# It is autogenerated by make/calcdep.pl, and will be overwritten
@@ -71,64 +77,56 @@ bad-target:
\@echo "in order to set the correct environment variables"
\@exit 1
-all: inspircd coremods modules
+all: inspircd modules
END
- my(@core_deps, @cmodlist, @modlist);
+ my(@core_deps, @modlist);
for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, "threadengines/threadengine_pthread.cpp") {
my $out = find_output $file;
dep_cpp $file, $out, 'gen-o';
next if $file =~ m#^socketengines/# && $file ne "socketengines/socketengine_$ENV{SOCKETENGINE}.cpp";
- push @core_deps, $out;
- }
-
- opendir my $coremoddir, 'coremods';
- for my $file (sort readdir $coremoddir) {
- next if $file =~ /^\./;
- if ($file =~ /^core_/ && -d "coremods/$file" && dep_dir "coremods/$file", "modules/$file") {
- mkdir "$build/obj/$file";
- push @cmodlist, "modules/$file.so";
- }
- if ($file =~ /^core_.*\.cpp$/) {
- my $out = dep_so "coremods/$file";
- push @cmodlist, $out;
+ # Having a module in the src directory is a bad idea because it will be linked to the core binary
+ if ($file =~ /^(m|core)_.*\.cpp/) {
+ my $correctsubdir = ($file =~ /^m_/ ? "modules" : "coremods");
+ print "Error: module $file is in the src directory, put it in src/$correctsubdir instead!\n";
+ exit 1;
}
+ push @core_deps, $out;
}
- opendir my $moddir, 'modules';
- for my $file (sort readdir $moddir) {
- next if $file =~ /^\./;
- if (-e "modules/extra/$file" && !-l "modules/$file") {
- # Incorrect symlink?
- print "Replacing symlink for $file found in modules/extra\n";
- rename "modules/$file", "modules/$file~";
- symlink "extra/$file", "modules/$file";
- }
- if ($file =~ /^m_/ && -d "modules/$file" && dep_dir "modules/$file", "modules/$file") {
- mkdir "$build/obj/$file";
- push @modlist, "modules/$file.so";
- }
- if ($file =~ /^m_.*\.cpp$/) {
- my $out = dep_so "modules/$file";
- push @modlist, $out;
+ foreach my $directory (qw(coremods modules)) {
+ opendir(my $moddir, $directory);
+ for my $file (sort readdir $moddir) {
+ next if $file =~ /^\./;
+ if ($directory eq 'modules' && -e "modules/extra/$file" && !-l "modules/$file") {
+ # Incorrect symlink?
+ print "Replacing symlink for $file found in modules/extra\n";
+ rename "modules/$file", "modules/$file~";
+ symlink "extra/$file", "modules/$file";
+ }
+ if ($file =~ /^(?:core|m)_/ && -d "$directory/$file" && dep_dir "$directory/$file", "modules/$file") {
+ mkdir "${\BUILDPATH}/obj/$file";
+ push @modlist, "modules/$file.so";
+ }
+ if ($file =~ /^.*\.cpp$/) {
+ my $out = dep_so "$directory/$file";
+ push @modlist, $out;
+ }
}
}
my $core_mk = join ' ', @core_deps;
- my $cmods = join ' ', @cmodlist;
my $mods = join ' ', @modlist;
print MAKE <<END;
bin/inspircd: $core_mk
- @\$(SOURCEPATH)/make/unit-cc.pl core-ld\$(VERBOSE) \$\@ \$^ \$>
+ @\$(SOURCEPATH)/make/unit-cc.pl core-ld \$\@ \$^ \$>
inspircd: bin/inspircd
-coremods: $cmods
-
modules: $mods
-.PHONY: all bad-target inspircd coremods modules
+.PHONY: all bad-target inspircd modules
END
}
@@ -153,7 +151,7 @@ END
<modules/*.cpp>, <modules/m_*/*.cpp>, "threadengines/threadengine_pthread.cpp") {
my $out = find_output $file, 1;
if ($out =~ m#obj/([^/]+)/[^/]+.o$#) {
- mkdir "$ENV{BUILDPATH}/obj/$1";
+ mkdir "${\BUILDPATH}/obj/$1";
}
dep_cpp $file, $out, 'gen-o';
next if $file =~ m#^socketengines/# && $file ne "socketengines/socketengine_$ENV{SOCKETENGINE}.cpp";
@@ -166,10 +164,10 @@ END
print MAKE <<END;
obj/ld-extra.cmd: $core_src
- \@\$(SOURCEPATH)/make/unit-cc.pl gen-ld\$(VERBOSE) \$\@ \$^ \$>
+ \@\$(SOURCEPATH)/make/unit-cc.pl gen-ld \$\@ \$^ \$>
-bin/inspircd: obj/ld-extra.cmd $core_mk
- \@\$(SOURCEPATH)/make/unit-cc.pl static-ld\$(VERBOSE) \$\@ \$^ \$>
+bin/inspircd: $core_mk obj/ld-extra.cmd
+ \@\$(SOURCEPATH)/make/unit-cc.pl static-ld \$\@ \$^ \$>
inspircd: bin/inspircd
@@ -233,7 +231,7 @@ sub dep_cpp($$$) {
gendep $file;
print MAKE "$out: $file $f2dep{$file}\n";
- print MAKE "\t@\$(SOURCEPATH)/make/unit-cc.pl $type\$(VERBOSE) \$\@ \$(SOURCEPATH)/src/$file \$>\n";
+ print MAKE "\t@\$(SOURCEPATH)/make/unit-cc.pl $type \$\@ \$(SOURCEPATH)/src/$file \$>\n";
}
sub dep_so($) {
@@ -258,7 +256,7 @@ sub dep_dir($$) {
if (@ofiles) {
my $ofiles = join ' ', @ofiles;
print MAKE "$outdir.so: $ofiles\n";
- print MAKE "\t@\$(SOURCEPATH)/make/unit-cc.pl link-dir\$(VERBOSE) \$\@ \$^ \$>\n";
+ print MAKE "\t@\$(SOURCEPATH)/make/unit-cc.pl link-dir \$\@ ${\SOURCEPATH}/src/$dir \$^ \$>\n";
return 1;
} else {
return 0;
diff --git a/make/common.pm b/make/common.pm
new file mode 100644
index 000000000..6ca280bec
--- /dev/null
+++ b/make/common.pm
@@ -0,0 +1,131 @@
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+# Copyright (C) 2013-2017 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/>.
+#
+
+
+BEGIN {
+ require 5.10.0;
+}
+
+package make::common;
+
+use feature ':5.10';
+use strict;
+use warnings FATAL => qw(all);
+
+use Exporter qw(import);
+use File::Path qw(mkpath);
+use File::Spec::Functions qw(rel2abs);
+
+use make::console;
+
+our @EXPORT = qw(create_directory
+ get_cpu_count
+ get_version
+ read_config_file
+ write_config_file);
+
+sub create_directory($$) {
+ my ($location, $permissions) = @_;
+ return eval {
+ mkpath($location, 0, $permissions);
+ return 1;
+ } // 0;
+}
+
+sub get_version {
+ state %version;
+ return %version if %version;
+
+ # Attempt to retrieve version information from src/version.sh
+ chomp(my $vf = `sh src/version.sh 2>/dev/null`);
+ if ($vf =~ /^InspIRCd-([0-9]+)\.([0-9]+)\.([0-9]+)(?:-(\w+))?$/) {
+ %version = ( MAJOR => $1, MINOR => $2, PATCH => $3, LABEL => $4 );
+ }
+
+ # Attempt to retrieve missing version information from Git
+ chomp(my $gr = `git describe --tags 2>/dev/null`);
+ if ($gr =~ /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\d+-g(\w+))?$/) {
+ $version{MAJOR} //= $1;
+ $version{MINOR} //= $2;
+ $version{PATCH} //= $3;
+ $version{LABEL} = $4 if defined $4;
+ }
+
+ # If the user has specified a distribution label then we use it in
+ # place of the label from src/version.sh or Git.
+ $version{LABEL} = shift // $version{LABEL};
+
+ # If any of these fields are missing then the user has deleted the
+ # version file and is not running from Git. Fill in the fields with
+ # dummy data so we don't get into trouble with undef values later.
+ $version{MAJOR} //= '0';
+ $version{MINOR} //= '0';
+ $version{PATCH} //= '0';
+
+ # If there is no label then the user is using a stable release which
+ # does not have a label attached.
+ if (defined $version{LABEL}) {
+ $version{FULL} = "$version{MAJOR}.$version{MINOR}.$version{PATCH}-$version{LABEL}"
+ } else {
+ $version{LABEL} = 'release';
+ $version{FULL} = "$version{MAJOR}.$version{MINOR}.$version{PATCH}"
+ }
+
+ return %version;
+}
+
+sub get_cpu_count {
+ my $count = 1;
+ if ($^O =~ /bsd/) {
+ $count = `sysctl -n hw.ncpu 2>/dev/null` || 1;
+ } elsif ($^O eq 'darwin') {
+ $count = `sysctl -n hw.activecpu 2>/dev/null` || 1;
+ } elsif ($^O eq 'linux') {
+ $count = `getconf _NPROCESSORS_ONLN 2>/dev/null` || 1;
+ } elsif ($^O eq 'solaris') {
+ $count = `psrinfo -p 2>/dev/null` || 1;
+ }
+ chomp($count);
+ return $count;
+}
+
+sub read_config_file($) {
+ my $path = shift;
+ my %config;
+ open(my $fh, $path) or return %config;
+ while (my $line = <$fh>) {
+ next if $line =~ /^\s*($|\#)/;
+ my ($key, $value) = ($line =~ /^(\S+)(?:\s(.*))?$/);
+ $config{$key} = $value;
+ }
+ close $fh;
+ return %config;
+}
+
+sub write_config_file($%) {
+ my $path = shift;
+ my %config = @_;
+ open(my $fh, '>', $path) or print_error "unable to write to $path: $!";
+ while (my ($key, $value) = each %config) {
+ $value //= '';
+ say $fh "$key $value";
+ }
+ close $fh;
+}
+
+1;
diff --git a/make/configure.pm b/make/configure.pm
index 905233835..dbbbf6509 100644
--- a/make/configure.pm
+++ b/make/configure.pm
@@ -1,7 +1,7 @@
#
# InspIRCd -- Internet Relay Chat Daemon
#
-# Copyright (C) 2012-2014 Peter Powell <petpow@saberuk.com>
+# Copyright (C) 2012-2017 Peter Powell <petpow@saberuk.com>
# Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
# Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
# Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
@@ -22,27 +22,42 @@
BEGIN {
- require 5.8.0;
+ require 5.10.0;
}
package make::configure;
+use feature ':5.10';
use strict;
use warnings FATAL => qw(all);
-use Cwd 'getcwd';
-use Exporter 'import';
-use File::Basename 'basename';
-
-use make::utilities;
-
-our @EXPORT = qw(cmd_clean cmd_help cmd_update
- read_configure_cache write_configure_cache
- get_compiler_info find_compiler
- run_test test_file test_header
- dump_hash get_property parse_templates);
-
-sub __get_socketengines() {
+use Cwd qw(getcwd);
+use Exporter qw(import);
+use File::Basename qw(basename dirname);
+use File::Spec::Functions qw(catfile);
+
+use make::common;
+use make::console;
+
+use constant CONFIGURE_DIRECTORY => '.configure';
+use constant CONFIGURE_CACHE_FILE => catfile(CONFIGURE_DIRECTORY, 'cache.cfg');
+use constant CONFIGURE_CACHE_VERSION => '1';
+use constant CONFIGURE_ERROR_PIPE => $ENV{INSPIRCD_VERBOSE} ? '' : '1>/dev/null 2>/dev/null';
+
+our @EXPORT = qw(CONFIGURE_CACHE_FILE
+ CONFIGURE_CACHE_VERSION
+ cmd_clean
+ cmd_help
+ cmd_update
+ run_test
+ test_file
+ test_header
+ write_configure_cache
+ get_compiler_info
+ find_compiler
+ parse_templates);
+
+sub __get_socketengines {
my @socketengines;
foreach (<src/socketengines/socketengine_*.cpp>) {
s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
@@ -53,12 +68,12 @@ sub __get_socketengines() {
# TODO: when buildtool is done this can be mostly removed with
# the remainder being merged into parse_templates.
-sub __get_template_settings($$) {
+sub __get_template_settings($$$) {
# These are actually hash references
- my ($config, $compiler) = @_;
+ my ($config, $compiler, $version) = @_;
- # Start off by populating with the config
+ # Start off by populating with the config
my %settings = %$config;
# Compiler information
@@ -67,20 +82,28 @@ sub __get_template_settings($$) {
}
# Version information
- my %version = get_version();
- while (my ($key, $value) = each %version) {
+ while (my ($key, $value) = each %{$version}) {
$settings{'VERSION_' . $key} = $value;
}
# Miscellaneous information
+ $settings{CONFIGURE_DIRECTORY} = CONFIGURE_DIRECTORY;
+ $settings{CONFIGURE_CACHE_FILE} = CONFIGURE_CACHE_FILE;
$settings{SYSTEM_NAME} = lc $^O;
chomp($settings{SYSTEM_NAME_VERSION} = `uname -sr 2>/dev/null`);
return %settings;
}
+sub __test_compiler($) {
+ my $compiler = shift;
+ return 0 unless run_test("`$compiler`", !system "$compiler -v ${\CONFIGURE_ERROR_PIPE}");
+ return 0 unless run_test("`$compiler`", test_file($compiler, 'compiler.cpp', '-fno-rtti'), 'compatible');
+ return 1;
+}
+
sub cmd_clean {
- unlink '.config.cache';
+ unlink CONFIGURE_CACHE_FILE;
}
sub cmd_help {
@@ -111,11 +134,11 @@ PATH OPTIONS
[$PWD/run/data]
--log-dir=[dir] The location where the log files are stored.
[$PWD/run/logs]
+ --manual-dir=[dir] The location where the manual files are stored.
+ [$PWD/run/manuals]
--module-dir=[dir] The location where the loadable modules are
stored.
[$PWD/run/modules]
- --build-dir=[dir] The location to store files in while building.
-
EXTRA MODULE OPTIONS
@@ -129,11 +152,15 @@ MISC OPTIONS
--clean Remove the configuration cache file and start
the interactive configuration wizard.
--disable-interactive Disables the interactive configuration wizard.
+ --distribution-label=[text] Sets a distribution specific version label in
+ the build configuration.
+ --gid=[id|name] Sets the group to run InspIRCd as.
--help Show this message and exit.
- --uid=[name] Sets the user to run InspIRCd as.
--socketengine=[name] Sets the socket engine to be used. Possible
values are $SELIST.
- --update Updates the build environment.
+ --uid=[id|name] Sets the user to run InspIRCd as.
+ --update Updates the build environment with the settings
+ from the cache.
FLAGS
@@ -143,168 +170,107 @@ FLAGS
will search for c++, g++, clang++ or icpc.
If you have any problems with configuring InspIRCd then visit our IRC channel
-at irc.ChatSpike.net #InspIRCd.
+at irc.inspircd.org #InspIRCd for support.
EOH
exit 0;
}
sub cmd_update {
- unless (-f '.config.cache') {
- print "You have not run $0 before. Please do this before trying to update the build files.\n";
- exit 1;
- }
- print "Updating...\n";
- my %config = read_configure_cache();
+ print_error "You have not run $0 before. Please do this before trying to update the generated files." unless -f CONFIGURE_CACHE_FILE;
+ say 'Updating...';
+ my %config = read_config_file(CONFIGURE_CACHE_FILE);
my %compiler = get_compiler_info($config{CXX});
- parse_templates(\%config, \%compiler);
- print "Update complete!\n";
+ my %version = get_version $config{DISTRIBUTION};
+ parse_templates(\%config, \%compiler, \%version);
+ say 'Update complete!';
exit 0;
}
-sub read_configure_cache {
- my %cfg = ();
- open(CACHE, '.config.cache') or return %cfg;
- while (my $line = <CACHE>) {
- next if $line =~ /^\s*($|\#)/;
- my ($key, $value) = ($line =~ /^(\S+)="(.*)"$/);
- $cfg{$key} = $value;
- }
- close(CACHE);
- return %cfg;
-}
-
-sub write_configure_cache(%) {
- my %cfg = @_;
- open(CACHE, ">.config.cache") or return 0;
- while (my ($key, $value) = each %cfg) {
- $value = "" unless defined $value;
- print CACHE "$key=\"$value\"\n";
- }
- close(CACHE);
- return 1;
-}
-
-sub get_compiler_info($) {
- my $binary = shift;
- my $version = `$binary -v 2>&1`;
- if ($version =~ /(?:clang|llvm)\sversion\s(\d+\.\d+)/i) {
- return (
- NAME => 'Clang',
- VERSION => $1,
- UNSUPPORTED => $1 lt '3.0',
- REASON => 'Clang 2.9 and older do not have adequate C++ support.'
- );
- } elsif ($version =~ /gcc\sversion\s(\d+\.\d+)/i) {
- return (
- NAME => 'GCC',
- VERSION => $1,
- UNSUPPORTED => $1 lt '4.1',
- REASON => 'GCC 4.0 and older do not have adequate C++ support.'
- );
- } elsif ($version =~ /(?:icc|icpc)\sversion\s(\d+\.\d+).\d+\s\(gcc\sversion\s(\d+\.\d+).\d+/i) {
- return (
- NAME => 'ICC',
- VERSION => $1,
- UNSUPPORTED => $2 lt '4.1',
- REASON => "ICC $1 (GCC $2 compatibility mode) does not have adequate C++ support."
- );
- }
- return (
- NAME => $binary,
- VERSION => '0.0'
- );
-}
-
-sub find_compiler {
- foreach my $compiler ('c++', 'g++', 'clang++', 'icpc') {
- return $compiler unless system "$compiler -v > /dev/null 2>&1";
- if ($^O eq 'Darwin') {
- return $compiler unless system "xcrun $compiler -v > /dev/null 2>&1";
- }
- }
- return "";
-}
-
-sub run_test($$) {
- my ($what, $result) = @_;
- print "Checking whether $what is available... ";
- print $result ? "yes\n" : "no\n";
+sub run_test($$;$) {
+ my ($what, $result, $adjective) = @_;
+ $adjective //= 'available';
+ print_format "Checking whether <|GREEN $what|> is $adjective ... ";
+ print_format $result ? "<|GREEN yes|>\n" : "<|RED no|>\n";
return $result;
}
sub test_file($$;$) {
- my ($cc, $file, $args) = @_;
+ my ($compiler, $file, $args) = @_;
my $status = 0;
- $args ||= '';
- $status ||= system "$cc -o __test_$file make/test/$file $args >/dev/null 2>&1";
- $status ||= system "./__test_$file >/dev/null 2>&1";
- unlink "./__test_$file";
+ $args //= '';
+ $status ||= system "$compiler -o __test_$file make/test/$file $args ${\CONFIGURE_ERROR_PIPE}";
+ $status ||= system "./__test_$file ${\CONFIGURE_ERROR_PIPE}";
+ unlink "./__test_$file";
return !$status;
}
sub test_header($$;$) {
- my ($cc, $header, $args) = @_;
- $args ||= '';
- open(CC, "| $cc -E - $args >/dev/null 2>&1") or return 0;
- print CC "#include <$header>";
- close(CC);
+ my ($compiler, $header, $args) = @_;
+ $args //= '';
+ open(my $fh, "| $compiler -E - $args ${\CONFIGURE_ERROR_PIPE}") or return 0;
+ print $fh "#include <$header>";
+ close $fh;
return !$?;
}
-sub get_property($$;$)
-{
- my ($file, $property, $default) = @_;
- open(MODULE, $file) or return $default;
- while (<MODULE>) {
- if ($_ =~ /^\/\* \$(\S+): (.+) \*\/$/) {
- next unless $1 eq $property;
- close(MODULE);
- return translate_functions($2, $file);
- }
+sub write_configure_cache(%) {
+ unless (-e CONFIGURE_DIRECTORY) {
+ print_format "Creating <|GREEN ${\CONFIGURE_DIRECTORY}|> ...\n";
+ create_directory CONFIGURE_DIRECTORY, 0750 or print_error "unable to create ${\CONFIGURE_DIRECTORY}: $!";
}
- close(MODULE);
- return defined $default ? $default : '';
+
+ print_format "Writing <|GREEN ${\CONFIGURE_CACHE_FILE}|> ...\n";
+ my %config = @_;
+ write_config_file CONFIGURE_CACHE_FILE, %config;
}
-sub dump_hash() {
- print "\n\e[1;32mPre-build configuration is complete!\e[0m\n\n";
- print "\e[0mBase install path:\e[1;32m\t\t$main::config{BASE_DIR}\e[0m\n";
- print "\e[0mConfig path:\e[1;32m\t\t\t$main::config{CONFIG_DIR}\e[0m\n";
- print "\e[0mData path:\e[1;32m\t\t\t$main::config{DATA_DIR}\e[0m\n";
- print "\e[0mLog path:\e[1;32m\t\t\t$main::config{LOG_DIR}\e[0m\n";
- print "\e[0mModule path:\e[1;32m\t\t\t$main::config{MODULE_DIR}\e[0m\n";
- print "\e[0mCompiler:\e[1;32m\t\t\t$main::cxx{NAME} $main::cxx{VERSION}\e[0m\n";
- print "\e[0mSocket engine:\e[1;32m\t\t\t$main::config{SOCKETENGINE}\e[0m\n";
- print "\e[0mGnuTLS support:\e[1;32m\t\t\t$main::config{USE_GNUTLS}\e[0m\n";
- print "\e[0mOpenSSL support:\e[1;32m\t\t$main::config{USE_OPENSSL}\e[0m\n";
+sub get_compiler_info($) {
+ my $binary = shift;
+ my %info = (NAME => 'Unknown', VERSION => '0.0');
+ return %info if system "$binary -o __compiler_info make/test/compiler_info.cpp ${\CONFIGURE_ERROR_PIPE}";
+ open(my $fh, '-|', './__compiler_info 2>/dev/null');
+ while (my $line = <$fh>) {
+ $info{$1} = $2 if $line =~ /^([A-Z]+)\s(.+)$/;
+ }
+ close $fh;
+ unlink './__compiler_info';
+ return %info;
}
-sub parse_templates($$) {
+sub find_compiler {
+ my @compilers = qw(c++ g++ clang++ icpc);
+ foreach my $compiler (shift // @compilers) {
+ return $compiler if __test_compiler $compiler;
+ return "xcrun $compiler" if $^O eq 'darwin' && __test_compiler "xcrun $compiler";
+ }
+}
+
+sub parse_templates($$$) {
# These are actually hash references
- my ($config, $compiler) = @_;
+ my ($config, $compiler, $version) = @_;
# Collect settings to be used when generating files
- my %settings = __get_template_settings($config, $compiler);
+ my %settings = __get_template_settings($config, $compiler, $version);
# Iterate through files in make/template.
foreach (<make/template/*>) {
- print "Parsing $_...\n";
- open(TEMPLATE, $_);
+ print_format "Parsing <|GREEN $_|> ...\n";
+ open(my $fh, $_) or print_error "unable to read $_: $!";
my (@lines, $mode, @platforms, %targets);
# First pass: parse template variables and directives.
- while (my $line = <TEMPLATE>) {
+ while (my $line = <$fh>) {
chomp $line;
# Does this line match a variable?
while ($line =~ /(@(\w+?)@)/) {
my ($variable, $name) = ($1, $2);
if (defined $settings{$name}) {
- $line =~ s/$variable/$settings{$name}/;
+ $line =~ s/\Q$variable\E/$settings{$name}/;
} else {
- print STDERR "Warning: unknown template variable '$name' in $_!\n";
+ print_warning "unknown template variable '$name' in $_!";
last;
}
}
@@ -328,21 +294,21 @@ sub parse_templates($$) {
$targets{DEFAULT} = $2;
}
} else {
- print STDERR "Warning: unknown template command '$1' in $_!\n";
+ print_warning "unknown template command '$1' in $_!";
push @lines, $line;
}
next;
}
push @lines, $line;
}
- close(TEMPLATE);
+ close $fh;
# Only proceed if this file should be templated on this platform.
if ($#platforms < 0 || grep { $_ eq $^O } @platforms) {
# Add a default target if the template has not defined one.
unless (scalar keys %targets) {
- $targets{DEFAULT} = basename $_;
+ $targets{DEFAULT} = catfile(CONFIGURE_DIRECTORY, basename $_);
}
# Second pass: parse makefile junk and write files.
@@ -413,7 +379,7 @@ sub parse_templates($$) {
# HACK: silently ignore if lower case as these are probably make commands.
push @final_lines, $line;
} else {
- print STDERR "Warning: unknown template command '$1' in $_!\n";
+ print_warning "unknown template command '$1' in $_!";
push @final_lines, $line;
}
next;
@@ -422,13 +388,20 @@ sub parse_templates($$) {
push @final_lines, $line;
}
+ # Create the directory if it doesn't already exist.
+ my $directory = dirname $target;
+ unless (-e $directory) {
+ print_format "Creating <|GREEN $directory|> ...\n";
+ create_directory $directory, 0750 or print_error "unable to create $directory: $!";
+ };
+
# Write the template file.
- print "Writing $target...\n";
- open(TARGET, ">$target");
+ print_format "Writing <|GREEN $target|> ...\n";
+ open(my $fh, '>', $target) or print_error "unable to write $target: $!";
foreach (@final_lines) {
- print TARGET $_, "\n";
+ say $fh $_;
}
- close(TARGET);
+ close $fh;
# Set file permissions.
if (defined $mode) {
diff --git a/make/console.pm b/make/console.pm
new file mode 100644
index 000000000..0d3c1b38d
--- /dev/null
+++ b/make/console.pm
@@ -0,0 +1,159 @@
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+# Copyright (C) 2014-2017 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/>.
+#
+
+
+package make::console;
+
+BEGIN {
+ require 5.10.0;
+}
+
+use feature ':5.10';
+use strict;
+use warnings FATAL => qw(all);
+
+use Class::Struct qw(struct);
+use Exporter qw(import);
+use File::Path qw(mkpath);
+use File::Spec::Functions qw(rel2abs);
+
+our @EXPORT = qw(command
+ execute_command
+ print_format
+ print_error
+ print_warning
+ prompt_bool
+ prompt_dir
+ prompt_string);
+
+my %FORMAT_CODES = (
+ DEFAULT => "\e[0m",
+ BOLD => "\e[1m",
+ UNDERLINE => "\e[4m",
+
+ RED => "\e[1;31m",
+ GREEN => "\e[1;32m",
+ YELLOW => "\e[1;33m",
+ BLUE => "\e[1;34m"
+);
+
+my %commands;
+
+struct 'command' => {
+ 'callback' => '$',
+ 'description' => '$',
+};
+
+sub __console_format($$) {
+ my ($name, $data) = @_;
+ return $data unless -t STDOUT;
+ return $FORMAT_CODES{uc $name} . $data . $FORMAT_CODES{DEFAULT};
+}
+
+sub print_format($;$) {
+ my $message = shift;
+ my $stream = shift // *STDOUT;
+ while ($message =~ /(<\|(\S+)\s(.*?)\|>)/) {
+ my $formatted = __console_format $2, $3;
+ $message =~ s/\Q$1\E/$formatted/;
+ }
+ print { $stream } $message;
+}
+
+sub print_error {
+ print_format "<|RED Error:|> ", *STDERR;
+ for my $line (@_) {
+ print_format "$line\n", *STDERR;
+ }
+ exit 1;
+}
+
+sub print_warning {
+ print_format "<|YELLOW Warning:|> ", *STDERR;
+ for my $line (@_) {
+ print_format "$line\n", *STDERR;
+ }
+}
+
+sub prompt_bool($$$) {
+ my ($interactive, $question, $default) = @_;
+ my $answer = prompt_string($interactive, $question, $default ? 'y' : 'n');
+ return $answer =~ /y/i;
+}
+
+sub prompt_dir($$$;$) {
+ my ($interactive, $question, $default, $create_now) = @_;
+ my ($answer, $create);
+ do {
+ $answer = rel2abs(prompt_string($interactive, $question, $default));
+ $create = prompt_bool($interactive && !-d $answer, "$answer does not exist. Create it?", 'y');
+ if ($create && $create_now) {
+ unless (create_directory $answer, 0750) {
+ print_warning "unable to create $answer: $!\n";
+ $create = 0;
+ }
+ }
+ } while (!$create);
+ return $answer;
+}
+
+sub prompt_string($$$) {
+ my ($interactive, $question, $default) = @_;
+ return $default unless $interactive;
+ print_format "$question\n";
+ print_format "[<|GREEN $default|>] => ";
+ chomp(my $answer = <STDIN>);
+ say '';
+ return $answer ? $answer : $default;
+}
+
+sub command($$$) {
+ my ($name, $description, $callback) = @_;
+ $commands{$name} = command->new;
+ $commands{$name}->callback($callback);
+ $commands{$name}->description($description);
+}
+
+sub command_alias($$) {
+ my ($source, $target) = @_;
+ command $source, undef, sub(@) {
+ execute_command $target, @_;
+ };
+}
+
+sub execute_command(@) {
+ my $command = defined $_[0] ? lc shift : 'help';
+ if ($command eq 'help') {
+ print_format "<|GREEN Usage:|> $0 <<|UNDERLINE COMMAND|>> [<|UNDERLINE OPTIONS...|>]\n\n";
+ print_format "<|GREEN Commands:|>\n";
+ for my $key (sort keys %commands) {
+ next unless defined $commands{$key}->description;
+ my $name = sprintf "%-15s", $key;
+ my $description = $commands{$key}->description;
+ print_format " <|BOLD $name|> # $description\n";
+ }
+ exit 0;
+ } elsif (!$commands{$command}) {
+ print_error "no command called <|BOLD $command|> exists!",
+ "See <|BOLD $0 help|> for a list of commands.";
+ } else {
+ return $commands{$command}->callback->(@_);
+ }
+}
+
+1;
diff --git a/make/directive.pm b/make/directive.pm
new file mode 100644
index 000000000..2e9e7ed61
--- /dev/null
+++ b/make/directive.pm
@@ -0,0 +1,271 @@
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+# Copyright (C) 2016 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/>.
+#
+
+
+package make::directive;
+
+BEGIN {
+ require 5.10.0;
+}
+
+use feature ':5.10';
+use strict;
+use warnings FATAL => qw(all);
+
+use File::Basename qw(basename);
+use Exporter qw(import);
+
+use make::configure;
+use make::console;
+
+use constant DIRECTIVE_ERROR_PIPE => $ENV{INSPIRCD_VERBOSE} ? '' : '2>/dev/null';
+
+our @EXPORT = qw(get_directive
+ execute_functions);
+
+sub get_directive($$;$)
+{
+ my ($file, $property, $default) = @_;
+ open(my $fh, $file) or return $default;
+
+ my $value = '';
+ while (<$fh>) {
+ if ($_ =~ /^\/\* \$(\S+): (.+) \*\/$/ || $_ =~ /^\/\/\/ \$(\S+): (.+)/) {
+ next unless $1 eq $property;
+ $value .= ' ' . execute_functions($file, $1, $2);
+ }
+ }
+ close $fh;
+
+ # Strip all extraneous whitespace.
+ $value =~ s/^\s+|\s+$//g;
+ return $value || $default;
+}
+
+sub execute_functions($$$) {
+ my ($file, $name, $line) = @_;
+
+ # NOTE: we have to use 'our' instead of 'my' here because of a Perl bug.
+ for (our @parameters = (); $line =~ /([a-z_]+)\((?:\s*"([^"]*)(?{push @parameters, $2})"\s*)*\)/; undef @parameters) {
+ my $sub = make::directive->can("__function_$1");
+ print_error "unknown $name directive '$1' in $file!" unless $sub;
+
+ # Call the subroutine and replace the function.
+ my $result = $sub->($file, @parameters);
+ if (defined $result) {
+ $line = $` . $result . $';
+ next;
+ }
+
+ # If the subroutine returns undef then it is a sign that we should
+ # disregard the rest of the line and stop processing it.
+ $line = $`;
+ }
+
+ return $line;
+}
+
+sub __environment {
+ my ($prefix, $suffix) = @_;
+ $suffix =~ s/[-.]/_/g;
+ $suffix =~ s/[^A-Za-z0-9_]//g;
+ return $prefix . uc $suffix;
+}
+
+sub __error {
+ my ($file, @message) = @_;
+ push @message, '';
+
+ # If we have package details then suggest to the user that they check
+ # that they have the packages installed.=
+ my $dependencies = get_directive($file, 'PackageInfo');
+ if (defined $dependencies) {
+ my @packages = sort grep { /^\S+$/ } split /\s/, $dependencies;
+ push @message, 'You should make sure you have the following packages installed:';
+ for (@packages) {
+ push @message, " * $_";
+ }
+ } else {
+ push @message, 'You should make sure that you have all of the required dependencies';
+ push @message, 'for this module installed.';
+ }
+ push @message, '';
+
+ # If we have author information then tell the user to report the bug
+ # to them. Otherwise, assume it is a bundled module and tell the user
+ # to report it to the InspIRCd issue tracker.
+ my $author = get_directive($file, 'ModAuthor');
+ if (defined $author) {
+ push @message, 'If you believe this error to be a bug then you can try to contact the';
+ push @message, 'author of this module:';
+ my $author_mail = get_directive($file, 'ModAuthorMail');
+ if (defined $author_mail) {
+ push @message, " * $author <$author_mail>";
+ } else {
+ push @message, " * $author";
+ }
+ } else {
+ push @message, 'If you believe this error to be a bug then you can file a bug report';
+ push @message, 'at https://github.com/inspircd/inspircd/issues';
+ }
+ push @message, '';
+
+ push @message, 'If you would like help with fixing this problem then visit our IRC';
+ push @message, 'channel at irc.inspircd.org #InspIRCd for support.';
+ push @message, '';
+
+ print_error @message;
+}
+
+sub __function_error {
+ my ($file, @messages) = @_;
+ __error $file, @messages;
+}
+
+sub __function_execute {
+ my ($file, $command, $environment, $defaults) = @_;
+
+ # Try to execute the command...
+ chomp(my $result = `$command ${\DIRECTIVE_ERROR_PIPE}`);
+ unless ($?) {
+ print_format "Execution of `<|GREEN $command|>` succeeded: <|BOLD $result|>\n";
+ return $result;
+ }
+
+ # If looking up with pkg-config fails then check the environment...
+ if (defined $environment && $environment ne '') {
+ $environment = __environment 'INSPIRCD_', $environment;
+ if (defined $ENV{$environment}) {
+ print_format "Execution of `<|GREEN $command|>` failed; using the environment: <|BOLD $ENV{$environment}|>\n";
+ return $ENV{$environment};
+ }
+ }
+
+ # If all else fails then look for the defaults..
+ if (defined $defaults) {
+ print_format "Execution of `<|GREEN $command|>` failed; using the defaults: <|BOLD $defaults|>\n";
+ return $defaults;
+ }
+
+ # Executing the command failed and we don't have any defaults so give up.
+ __error $file, "`<|GREEN $command|>` exited with a non-zero exit code!";
+}
+
+sub __function_find_compiler_flags {
+ my ($file, $name, $defaults) = @_;
+
+ # Try to look up the compiler flags with pkg-config...
+ chomp(my $flags = `pkg-config --cflags $name ${\DIRECTIVE_ERROR_PIPE}`);
+ unless ($?) {
+ print_format "Found the compiler flags for <|GREEN ${\basename $file, '.cpp'}|> using pkg-config: <|BOLD $flags|>\n";
+ return $flags;
+ }
+
+ # If looking up with pkg-config fails then check the environment...
+ my $key = __environment 'INSPIRCD_CXXFLAGS_', $name;
+ if (defined $ENV{$key}) {
+ print_format "Found the compiler flags for <|GREEN ${\basename $file, '.cpp'}|> using the environment: <|BOLD $ENV{$key}|>\n";
+ return $ENV{$key};
+ }
+
+ # If all else fails then look for the defaults..
+ if (defined $defaults) {
+ print_format "Found the compiler flags for <|GREEN ${\basename $file, '.cpp'}|> using the defaults: <|BOLD $defaults|>\n";
+ return $defaults;
+ }
+
+ # We can't find it via pkg-config, via the environment, or via the defaults so give up.
+ __error $file, "unable to find the compiler flags for <|GREEN ${\basename $file, '.cpp'}|>!";
+}
+
+sub __function_find_linker_flags {
+ my ($file, $name, $defaults) = @_;
+
+ # Try to look up the linker flags with pkg-config...
+ chomp(my $flags = `pkg-config --libs $name ${\DIRECTIVE_ERROR_PIPE}`);
+ unless ($?) {
+ print_format "Found the linker flags for <|GREEN ${\basename $file, '.cpp'}|> using pkg-config: <|BOLD $flags|>\n";
+ return $flags;
+ }
+
+ # If looking up with pkg-config fails then check the environment...
+ my $key = __environment 'INSPIRCD_CXXFLAGS_', $name;
+ if (defined $ENV{$key}) {
+ print_format "Found the linker flags for <|GREEN ${\basename $file, '.cpp'}|> using the environment: <|BOLD $ENV{$key}|>\n";
+ return $ENV{$key};
+ }
+
+ # If all else fails then look for the defaults..
+ if (defined $defaults) {
+ print_format "Found the linker flags for <|GREEN ${\basename $file, '.cpp'}|> using the defaults: <|BOLD $defaults|>\n";
+ return $defaults;
+ }
+
+ # We can't find it via pkg-config, via the environment, or via the defaults so give up.
+ __error $file, "unable to find the linker flags for <|GREEN ${\basename $file, '.cpp'}|>!";
+}
+
+sub __function_require_system {
+ my ($file, $name, $minimum, $maximum) = @_;
+ my ($system, $version);
+
+ # Linux is special and can be compared by distribution names.
+ if ($^O eq 'linux' && $name ne 'linux') {
+ chomp($system = lc `lsb_release --id --short 2>/dev/null`);
+ chomp($version = lc `lsb_release --release --short 2>/dev/null`);
+ }
+
+ # Gather information on the system if we don't have it already.
+ chomp($system ||= lc `uname -s 2>/dev/null`);
+ chomp($version ||= lc `uname -r 2>/dev/null`);
+
+ # We only care about the important bit of the version number so trim the rest.
+ $version =~ s/^(\d+\.\d+).+/$1/;
+
+ # Check whether the current system is suitable.
+ return undef if $name ne $system;
+ return undef if defined $minimum && $version < $minimum;
+ return undef if defined $maximum && $version > $maximum;
+
+ # Requirement directives don't change anything directly.
+ return "";
+}
+
+sub __function_require_version {
+ my ($file, $name, $minimum, $maximum) = @_;
+
+ # If pkg-config isn't installed then we can't do anything here.
+ if (system "pkg-config --exists $name ${\DIRECTIVE_ERROR_PIPE}") {
+ print_warning "unable to look up the version of $name using pkg-config!";
+ return undef;
+ }
+
+ # Check with pkg-config whether we have the required version.
+ return undef if defined $minimum && system "pkg-config --atleast-version $minimum $name";
+ return undef if defined $maximum && system "pkg-config --max-version $maximum $name";
+
+ # Requirement directives don't change anything directly.
+ return "";
+}
+
+sub __function_warning {
+ my ($file, @messages) = @_;
+ print_warning @messages;
+}
+
+1;
diff --git a/make/run-cc.pl b/make/run-cc.pl
deleted file mode 100755
index 58b5850ca..000000000
--- a/make/run-cc.pl
+++ /dev/null
@@ -1,237 +0,0 @@
-#!/usr/bin/env perl
-
-#
-# InspIRCd -- Internet Relay Chat Daemon
-#
-# Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
-# Copyright (C) 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/>.
-#
-
-
-### THIS IS DESIGNED TO BE RUN BY MAKE! DO NOT RUN FROM THE SHELL (because it MIGHT sigterm the shell)! ###
-
-use strict;
-use warnings FATAL => qw(all);
-
-use POSIX ();
-
-# Runs the compiler, passing it the given arguments.
-# Filters select output from the compiler's standard error channel and
-# can take different actions as a result.
-
-# NOTE: this is *NOT* a hash (sadly: a hash would stringize all the regexes and thus render them useless, plus you can't index a hash based on regexes anyway)
-# even though we use the => in it.
-
-# The subs are passed the message, and anything the regex captured.
-
-my $cc = shift(@ARGV);
-
-my $showncmdline = 0;
-
-# GCC's "location of error stuff", which accumulates the "In file included from" include stack
-my $location = "";
-
-my @msgfilters = (
- [ qr/^(.*) warning: cannot pass objects of non-POD type `(.*)' through `\.\.\.'; call will abort at runtime/ => sub {
- my ($msg, $where, $type) = @_;
- my $errstr = $location . "$where error: cannot pass objects of non-POD type `$type' through `...'\n";
- $location = "";
- if ($type =~ m/::(basic_)?string/) {
- $errstr .= "$where (Did you forget to call c_str()?)\n";
- }
- die $errstr;
- } ],
-
- # Start of an include stack.
- [ qr/^In file included from .*[,:]$/ => sub {
- my ($msg) = @_;
- $location = "$msg\n";
- return undef;
- } ],
-
- # Continuation of an include stack.
- [ qr/^ from .*[,:]$/ => sub {
- my ($msg) = @_;
- $location .= "$msg\n";
- return undef;
- } ],
-
- # A function, method, constructor, or destructor is the site of a problem
- [ qr/In ((con|de)structor|(member )?function)/ => sub {
- my ($msg) = @_;
- # If a complete location string is waiting then probably we dropped an error, so drop the location for a new one.
- if ($location =~ m/In ((con|de)structor|(member )?function)/) {
- $location = "$msg\n";
- } else {
- $location .= "$msg\n";
- }
- return undef;
- } ],
-
- [ qr/^.* warning: / => sub {
- my ($msg) = @_;
- my $str = $location . "\e[33;1m$msg\e[0m\n";
- $showncmdline = 1;
- $location = "";
- return $str;
- } ],
-
- [ qr/^.* error: / => sub {
- my ($msg) = @_;
- my $str = "";
- $str = "An error occured when executing:\e[37;1m $cc " . join(' ', @ARGV) . "\n" unless $showncmdline;
- $showncmdline = 1;
- $str .= $location . "\e[31;1m$msg\e[0m\n";
- $location = "";
- return $str;
- } ],
-
- [ qr/./ => sub {
- my ($msg) = @_;
- $msg = $location . $msg;
- $location = "";
- $msg =~ s/std::basic_string\<char\, std\:\:char_traits\<char\>, std::allocator\<char\> \>(\s+|)/std::string/g;
- $msg =~ s/std::basic_string\<char\, .*?irc_char_traits\<char\>, std::allocator\<char\> \>(\s+|)/irc::string/g;
- for my $stl (qw(deque vector list)) {
- $msg =~ s/std::$stl\<(\S+), std::allocator\<\1\> \>/std::$stl\<$1\>/g;
- $msg =~ s/std::$stl\<(std::pair\<\S+, \S+\>), std::allocator\<\1 \> \>/std::$stl<$1 >/g;
- }
- $msg =~ s/std::map\<(\S+), (\S+), std::less\<\1\>, std::allocator\<std::pair\<const \1, \2\> \> \>/std::map<$1, $2>/g;
- # Warning: These filters are GNU C++ specific!
- $msg =~ s/__gnu_cxx::__normal_iterator\<(\S+)\*, std::vector\<\1\> \>/std::vector<$1>::iterator/g;
- $msg =~ s/__gnu_cxx::__normal_iterator\<(std::pair\<\S+, \S+\>)\*, std::vector\<\1 \> \>/std::vector<$1 >::iterator/g;
- $msg =~ s/__gnu_cxx::__normal_iterator\<char\*, std::string\>/std::string::iterator/g;
- $msg =~ s/__gnu_cxx::__normal_iterator\<char\*, irc::string\>/irc::string::iterator/g;
- return $msg;
- } ],
-);
-
-my $pid;
-
-my ($r_stderr, $w_stderr);
-
-my $name = "";
-my $action = "";
-
-if ($cc eq "ar") {
- $name = $ARGV[1];
- $action = "ARCHIVE";
-} else {
- foreach my $n (@ARGV)
- {
- if ($n =~ /\.cpp$/)
- {
- my $f = $n;
- if (defined $ENV{SOURCEPATH}) {
- $f =~ s#^$ENV{SOURCEPATH}/src/##;
- }
- if ($action eq "BUILD")
- {
- $name .= " " . $f;
- }
- else
- {
- $action = "BUILD";
- $name = $f;
- }
- }
- elsif ($action eq "BUILD") # .cpp has priority.
- {
- next;
- }
- elsif ($n eq "-o")
- {
- $action = $name = $n;
- }
- elsif ($name eq "-o")
- {
- $action = "LINK";
- $name = $n;
- }
- }
-}
-
-if (!defined($cc) || $cc eq "") {
- die "Compiler not specified!\n";
-}
-
-pipe($r_stderr, $w_stderr) or die "pipe stderr: $!\n";
-
-$pid = fork;
-
-die "Cannot fork to start $cc! $!\n" unless defined($pid);
-
-if ($pid) {
-
- printf "\t\e[1;32m%-20s\e[0m%s\n", $action . ":", $name unless $name eq "";
-
- my $fail = 0;
- # Parent - Close child-side pipes.
- close $w_stderr;
- # Close STDIN to ensure no conflicts with child.
- close STDIN;
- # Now read each line of stderr
-LINE: while (defined(my $line = <$r_stderr>)) {
- chomp $line;
-
- for my $filter (@msgfilters) {
- my @caps;
- if (@caps = ($line =~ $filter->[0])) {
- $@ = "";
- $line = eval {
- $filter->[1]->($line, @caps);
- };
- if ($@) {
- # Note that $line is undef now.
- $fail = 1;
- print STDERR $@;
- }
- next LINE unless defined($line);
- }
- }
- # Chomp off newlines again, in case the filters put some back in.
- chomp $line;
- print STDERR "$line\n";
- }
- waitpid $pid, 0;
- close $r_stderr;
- my $exit = $?;
- # Simulate the same exit, so make gets the right termination info.
- if (POSIX::WIFSIGNALED($exit)) {
- # Make won't get the right termination info (it gets ours, not the compiler's), so we must tell the user what really happened ourselves!
- print STDERR "$cc killed by signal " . POSIX::WTERMSIGN($exit) . "\n";
- kill "TERM", getppid(); # Needed for bsd make.
- kill "TERM", $$;
- }
- else {
- if (POSIX::WEXITSTATUS($exit) == 0) {
- if ($fail) {
- kill "TERM", getppid(); # Needed for bsd make.
- kill "TERM", $$;
- }
- exit 0;
- } else {
- exit POSIX::WEXITSTATUS($exit);
- }
- }
-} else {
- # Child - Close parent-side pipes.
- close $r_stderr;
- # Divert stderr
- open STDERR, ">&", $w_stderr or die "Cannot divert STDERR: $!\n";
- # Run the compiler!
- exec { $cc } $cc, @ARGV;
- die "exec $cc: $!\n";
-}
diff --git a/make/template/config.h b/make/template/config.h
index 513c550f5..660678b38 100644
--- a/make/template/config.h
+++ b/make/template/config.h
@@ -20,8 +20,7 @@
#pragma once
#define INSPIRCD_BRANCH "InspIRCd-@VERSION_MAJOR@.@VERSION_MINOR@"
-#define INSPIRCD_VERSION "InspIRCd-@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@"
-#define INSPIRCD_REVISION "@VERSION_LABEL@"
+#define INSPIRCD_VERSION "InspIRCd-@VERSION_FULL@"
#define INSPIRCD_SYSTEM "@SYSTEM_NAME_VERSION@"
#define INSPIRCD_CONFIG_PATH "@CONFIG_DIR@"
diff --git a/make/template/inspircd b/make/template/inspircd
index 7610557b8..949f85cee 100644
--- a/make/template/inspircd
+++ b/make/template/inspircd
@@ -19,11 +19,35 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
+# InspIRCd Start up the InspIRCd Internet Relay Chat Daemon
+#
+# chkconfig: 2345 55 25
+# description: InspIRCd -- Internet Relay Chat Daemon
+#
+# processname: inspircd
use strict;
use POSIX;
use Fcntl;
+# From http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
+use constant {
+ STATUS_EXIT_SUCCESS => 0,
+ STATUS_EXIT_DEAD_WITH_PIDFILE => 1,
+ STATUS_EXIT_DEAD_WITH_LOCKFILE => 2,
+ STATUS_EXIT_NOT_RUNNING => 3,
+ STATUS_EXIT_UNKNOWN => 4,
+
+ GENERIC_EXIT_SUCCESS => 0,
+ GENERIC_EXIT_UNSPECIFIED => 1,
+ GENERIC_EXIT_INVALID_ARGUMENTS => 2,
+ GENERIC_EXIT_UNIMPLEMENTED => 3,
+ GENERIC_EXIT_INSUFFICIENT_PRIVILEGE => 4,
+ GENERIC_EXIT_NOT_INSTALLED => 5,
+ GENERIC_EXIT_NOT_CONFIGURED => 6,
+ GENERIC_EXIT_NOT_RUNNING => 7
+};
+
my $basepath = "@BASE_DIR@";
my $confpath = "@CONFIG_DIR@/";
my $binpath = "@BINARY_DIR@";
@@ -31,7 +55,7 @@ my $runpath = "@BASE_DIR@";
my $datadir = "@DATA_DIR@";
my $valgrindlogpath = "$basepath/valgrindlogs";
my $executable = "inspircd";
-my $version = "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@+@VERSION_LABEL@";
+my $version = "@VERSION_FULL@";
my $uid = "@UID@";
if (!("--runasroot" ~~ @ARGV) && ($< == 0 || $> == 0)) {
@@ -82,12 +106,11 @@ if (!defined($sub))
{
print STDERR "Invalid command or none given.\n";
cmd_help();
- exit 1;
+ exit GENERIC_EXIT_UNIMPLEMENTED;
}
else
{
- $sub->(@ARGV);
- exit 0;
+ exit $sub->(@ARGV); # Error code passed through return value
}
sub cmd_help()
@@ -100,7 +123,7 @@ sub cmd_help()
$_ =~ s/_/-/g foreach (@cmds, @devs);
print STDERR "Usage: ./inspircd (" . join("|", @cmds) . ")\n";
print STDERR "Developer arguments: (" . join("|", @devs) . ")\n";
- exit 0;
+ exit GENERIC_EXIT_SUCCESS;
}
sub cmd_status()
@@ -108,10 +131,10 @@ sub cmd_status()
if (getstatus() == 1) {
my $pid = getprocessid();
print "InspIRCd is running (PID: $pid)\n";
- exit();
+ exit STATUS_EXIT_SUCCESS;
} else {
print "InspIRCd is not running. (Or PID File not found)\n";
- exit();
+ exit STATUS_EXIT_NOT_RUNNING;
}
}
@@ -121,43 +144,43 @@ sub cmd_rehash()
my $pid = getprocessid();
system("kill -HUP $pid >/dev/null 2>&1");
print "InspIRCd rehashed (pid: $pid).\n";
- exit();
+ exit GENERIC_EXIT_SUCCESS;
} else {
print "InspIRCd is not running. (Or PID File not found)\n";
- exit();
+ exit GENERIC_EXIT_NOT_RUNNING;
}
}
sub cmd_cron()
{
- if (getstatus() == 0) { goto &cmd_start(); }
- exit();
+ if (getstatus() == 0) { goto &cmd_start(@_); }
+ exit GENERIC_EXIT_UNSPECIFIED;
}
sub cmd_version()
{
print "InspIRCd version: $version\n";
- exit();
+ exit GENERIC_EXIT_SUCCESS;
}
sub cmd_restart(@)
{
cmd_stop();
unlink($pidfile) if (-e $pidfile);
- goto &cmd_start;
+ goto &cmd_start(@_);
}
sub hid_cheese_sandwich()
{
print "Creating Cheese Sandwich..\n";
print "Done.\n";
- exit();
+ exit GENERIC_EXIT_SUCCESS;
}
sub cmd_start(@)
{
# Check to see its not 'running' already.
- if (getstatus() == 1) { print "InspIRCd is already running.\n"; return 0; }
+ if (getstatus() == 1) { print "InspIRCd is already running.\n"; exit GENERIC_EXIT_SUCCESS; }
# If we are still alive here.. Try starting the IRCd..
chdir $runpath;
print "$binpath/$executable doesn't exist\n" and return 0 unless(-e "$binpath/$executable");
@@ -219,7 +242,7 @@ sub dev_valdebug(@)
# If we are still alive here.. Try starting the IRCd..
# May want to do something with these args at some point: --suppressions=.inspircd.sup --gen-suppressions=yes
# Could be useful when we want to stop it complaining about things we're sure aren't issues.
- exec qw(valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=10), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
+ exec qw(valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=30), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
die "Failed to start valgrind: $!\n";
}
@@ -253,7 +276,7 @@ sub dev_valdebug_unattended(@)
sysopen STDERR, "$valgrindlogpath/valdebug.$suffix", O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND, 0666 or die "Can't open $valgrindlogpath/valdebug.$suffix: $!\n";
# May want to do something with these args at some point: --suppressions=.inspircd.sup --gen-suppressions=yes
# Could be useful when we want to stop it complaining about things we're sure aren't issues.
- exec qw(valgrind -v --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=15 --track-fds=yes),
+ exec qw(valgrind -v --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=30 --track-fds=yes),
"--suppressions=$binpath/valgrind.sup", qw(--gen-suppressions=all),
qw(--leak-resolution=med --time-stamp=yes --log-fd=2 --),
"$binpath/$executable", qw(--nofork --debug --nolog), @_;
@@ -278,13 +301,13 @@ sub dev_screenvaldebug(@)
# If we are still alive here.. Try starting the IRCd..
print "Starting InspIRCd in `screen`, type `screen -r` when the ircd crashes to view the valgrind and gdb output and get a backtrace.\n";
print "Once you're inside the screen session press ^C + d to re-detach from the session\n";
- exec qw(screen -m -d valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=10), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
+ exec qw(screen -m -d valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=30), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
die "Failed to start screen: $!\n";
}
sub cmd_stop()
{
- if (getstatus() == 0) { print "InspIRCd is not running. (Or PID File not found)\n"; return 0; }
+ if (getstatus() == 0) { print "InspIRCd is not running. (Or PID File not found)\n"; return GENERIC_EXIT_SUCCESS; }
# Get to here, we have something to kill.
my $pid = getprocessid();
print "Stopping InspIRCd (pid: $pid)...\n";
@@ -294,12 +317,12 @@ sub cmd_stop()
sleep 1;
if (getstatus() == 0) {
print "InspIRCd Stopped.\n";
- return;
+ return GENERIC_EXIT_SUCCESS;
}
}
print "InspIRCd not dying quietly -- forcing kill\n";
kill KILL => $pid;
- return 0;
+ return GENERIC_EXIT_SUCCESS;
}
###
@@ -410,7 +433,7 @@ sub checkvalgrind
unless(`valgrind --version`)
{
print "Couldn't start valgrind: $!\n";
- exit;
+ exit GENERIC_EXIT_UNSPECIFIED;
}
}
@@ -419,7 +442,7 @@ sub checkgdb
unless(`gdb --version`)
{
print "Couldn't start gdb: $!\n";
- exit;
+ exit GENERIC_EXIT_UNSPECIFIED;
}
}
@@ -428,6 +451,6 @@ sub checkscreen
unless(`screen --version`)
{
print "Couldn't start screen: $!\n";
- exit;
+ exit GENERIC_EXIT_UNSPECIFIED;
}
}
diff --git a/make/template/inspircd-genssl.1 b/make/template/inspircd-genssl.1
new file mode 100644
index 000000000..d43a3b4e8
--- /dev/null
+++ b/make/template/inspircd-genssl.1
@@ -0,0 +1,46 @@
+.\"
+.\" InspIRCd -- Internet Relay Chat Daemon
+.\"
+.\" Copyright (C) 2014 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/>.
+.\"
+
+
+.TH "InspIRCd" "1" "June 2014" "InspIRCd @VERSION_FULL@" "InspIRCd Manual"
+
+.SH "NAME"
+\t\fBInspIRCd\fR - \fIthe\fR stable, high-performance and modular Internet Relay Chat Daemon
+.BR
+
+.SH "SYNOPSIS"
+\t\fBinspircd-genssl\fR [ auto | gnutls | openssl ]
+
+.SH "OPTIONS"
+.TP
+.B "auto"
+.br
+Looks for both GnuTLS and OpenSSL and uses the first one which is available for certificate generation.
+.TP
+.B "gnutls"
+.br
+Generates certificates using GnuTLS.
+.TP
+.br
+.B "openssl"
+Generates certificates using OpenSSL.
+
+.SH "SUPPORT"
+IRC support for InspIRCd can be found at irc://irc.inspircd.org/inspircd.
+
+Bug reports and feature requests can be filed at https://github.com/inspircd/inspircd/issues.
diff --git a/make/template/inspircd.1 b/make/template/inspircd.1
new file mode 100644
index 000000000..eb1453d2f
--- /dev/null
+++ b/make/template/inspircd.1
@@ -0,0 +1,104 @@
+.\"
+.\" InspIRCd -- Internet Relay Chat Daemon
+.\"
+.\" Copyright (C) 2014 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/>.
+.\"
+
+
+.TH "InspIRCd" "1" "June 2014" "InspIRCd @VERSION_FULL@" "InspIRCd Manual"
+
+.SH "NAME"
+\t\fBInspIRCd\fR - \fIthe\fR stable, high-performance and modular Internet Relay Chat Daemon
+.BR
+
+.SH "SYNOPSIS"
+\t\fBinspircd\fR [--config <file>] [--debug] [--nofork] [--nolog] [--runasroot] [--version]
+
+.SH "OPTIONS"
+.TP
+.B "--config <file>"
+.br
+Sets the path to the main configuration file. Defaults to \fI@CONFIG_DIR@/inspircd.conf\fR.
+.TP
+.B "--debug"
+.br
+Log verbosely to the standard output stream.
+.TP
+.B "--nofork"
+.br
+Don't fork into the background after starting up.
+.TP
+.B "--nolog"
+.br
+Don't write to log files.
+.TP
+.B "--runasroot"
+.br
+Allow the server to start as root (not recommended).
+.TP
+.B "--version"
+.br
+Displays the InspIRCd version and exits.
+
+.SH "EXIT STATUS"
+.TP
+.B "0 (EXIT_STATUS_NOERROR)"
+.br
+The server exited cleanly.
+.TP
+.B "1 (EXIT_STATUS_DIE)"
+.br
+The server exited because the DIE command was executed.
+.TP
+.B "2 (EXIT_STATUS_CONFIG)"
+.br
+The server exited because of a configuration file error.
+.TP
+.B "3 (EXIT_STATUS_LOG)"
+.br
+The server exited because of a log file error.
+.TP
+.B "4 (EXIT_STATUS_FORK)"
+.br
+The server exited because it was unable to fork into the background.
+.TP
+.B "5 (EXIT_STATUS_ARGV)"
+.br
+The server exited because an invalid argument was passed to it on the command line.
+.TP
+.B "6 (EXIT_STATUS_PID)"
+.br
+The server exited because it was unable to write to the PID file.
+.TP
+.B "7 (EXIT_STATUS_SOCKETENGINE)"
+.br
+The server exited because it was unable to initialize the @SOCKETENGINE@ socket engine.
+.TP
+.B "8 (EXIT_STATUS_ROOT)"
+.br
+The server exited because the user tried to start as root without \fI--runasroot\fR.
+.TP
+.B "9 (EXIT_STATUS_MODULE)"
+.br
+The server exited because it was unable to load a module on first run.
+.TP
+.B "10 (EXIT_STATUS_SIGTERM)"
+.br
+The server exited because it received SIGTERM.
+
+.SH "SUPPORT"
+IRC support for InspIRCd can be found at irc://irc.inspircd.org/inspircd.
+
+Bug reports and feature requests can be filed at https://github.com/inspircd/inspircd/issues.
diff --git a/make/template/inspircd.service b/make/template/inspircd.service
new file mode 100644
index 000000000..e5f28a674
--- /dev/null
+++ b/make/template/inspircd.service
@@ -0,0 +1,35 @@
+%platform linux
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+# Copyright (C) 2014 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/>.
+#
+
+
+[Unit]
+After=network.target
+Description=InspIRCd - Internet Relay Chat Daemon
+Requires=network.target
+
+[Service]
+ExecReload=@BASE_DIR@/inspircd rehash
+ExecStart=@BASE_DIR@/inspircd start
+ExecStop=@BASE_DIR@/inspircd stop
+PIDFile=@DATA_DIR@/inspircd.pid
+Restart=on-failure
+Type=forking
+
+[Install]
+WantedBy=multi-user.target
diff --git a/make/template/main.mk b/make/template/main.mk
index 9a8853021..50feb8f8d 100644
--- a/make/template/main.mk
+++ b/make/template/main.mk
@@ -37,13 +37,15 @@ COMPILER = @COMPILER_NAME@
SYSTEM = @SYSTEM_NAME@
BUILDPATH ?= $(PWD)/build
SOCKETENGINE = @SOCKETENGINE@
-CORECXXFLAGS = -fPIC -fvisibility-inlines-hidden -pipe -Iinclude -Wall -Wextra -Wfatal-errors -Wno-unused-parameter -Wshadow
+CORECXXFLAGS = -fPIC -fvisibility=hidden -fvisibility-inlines-hidden -pipe -Iinclude -Wall -Wextra -Wfatal-errors -Wno-unused-parameter -Wshadow
LDLIBS = -lstdc++
CORELDFLAGS = -rdynamic -L. $(LDFLAGS)
PICLDFLAGS = -fPIC -shared -rdynamic $(LDFLAGS)
BASE = "$(DESTDIR)@BASE_DIR@"
CONPATH = "$(DESTDIR)@CONFIG_DIR@"
+MANPATH = "$(DESTDIR)@MANUAL_DIR@"
MODPATH = "$(DESTDIR)@MODULE_DIR@"
+LOGPATH = "$(DESTDIR)@LOG_DIR@"
DATPATH = "$(DESTDIR)@DATA_DIR@"
BINPATH = "$(DESTDIR)@BINARY_DIR@"
INSTALL = install
@@ -53,11 +55,10 @@ INSTMODE_BIN = 0750
INSTMODE_LIB = 0640
@IFNEQ $(COMPILER) ICC
- CORECXXFLAGS += -pedantic -Woverloaded-virtual -Wshadow -Wformat=2 -Wmissing-format-attribute
+ CORECXXFLAGS += -Woverloaded-virtual -Wshadow
+@IFNEQ $(SYSTEM) openbsd
+ CORECXXFLAGS += -pedantic -Wformat=2 -Wmissing-format-attribute
@ENDIF
-
-@IFNEQ $(SYSTEM)-$(COMPILER) darwin-GCC
- CORECXXFLAGS += -fvisibility=hidden
@ENDIF
@IFNEQ $(SYSTEM) darwin
@@ -83,26 +84,26 @@ INSTMODE_LIB = 0640
PICLDFLAGS = -fPIC -shared -twolevel_namespace -undefined dynamic_lookup $(LDFLAGS)
@ENDIF
-@IFNDEF D
- D=0
+@IFNDEF INSPIRCD_DEBUG
+ INSPIRCD_DEBUG=0
@ENDIF
DBGOK=0
-@IFEQ $(D) 0
- CORECXXFLAGS += -O2
+@IFEQ $(INSPIRCD_DEBUG) 0
+ CORECXXFLAGS += -fno-rtti -O2
@IFEQ $(COMPILER) GCC
CORECXXFLAGS += -g1
@ENDIF
HEADER = std-header
DBGOK=1
@ENDIF
-@IFEQ $(D) 1
- CORECXXFLAGS += -O0 -g3 -Werror
+@IFEQ $(INSPIRCD_DEBUG) 1
+ CORECXXFLAGS += -O0 -g3 -Werror -DINSPIRCD_ENABLE_RTTI
HEADER = debug-header
DBGOK=1
@ENDIF
-@IFEQ $(D) 2
- CORECXXFLAGS += -O2 -g3
+@IFEQ $(INSPIRCD_DEBUG) 2
+ CORECXXFLAGS += -fno-rtti -O2 -g3
HEADER = debug-header
DBGOK=1
@ENDIF
@@ -113,42 +114,36 @@ FOOTER = finishmessage
@TARGET GNU_MAKE SOURCEPATH = $(shell /bin/pwd)
@TARGET BSD_MAKE SOURCEPATH != /bin/pwd
-@IFDEF V
- RUNCC = $(CXX)
- RUNLD = $(CXX)
- VERBOSE = -v
-@ELSE
+@IFNDEF INSPIRCD_VERBOSE
@TARGET GNU_MAKE MAKEFLAGS += --silent
@TARGET BSD_MAKE MAKE += -s
- RUNCC = perl $(SOURCEPATH)/make/run-cc.pl $(CXX)
- RUNLD = perl $(SOURCEPATH)/make/run-cc.pl $(CXX)
@ENDIF
-@IFDEF PURE_STATIC
- CORECXXFLAGS += -DPURE_STATIC
+@IFDEF INSPIRCD_STATIC
+ CORECXXFLAGS += -DINSPIRCD_STATIC
@ENDIF
-# Add the users CXXFLAGS to the base ones to allow them to override
-# things like -Wfatal-errors if they wish to.
-CORECXXFLAGS += $(CXXFLAGS)
+# Add the users CPPFLAGS/CXXFLAGS to the base ones to allow them to
+# override things like -Wfatal-errors if they wish to.
+CORECXXFLAGS += $(CPPFLAGS) $(CXXFLAGS)
-@DO_EXPORT RUNCC RUNLD CORECXXFLAGS LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS
-@DO_EXPORT SOURCEPATH BUILDPATH PURE_STATIC
+@DO_EXPORT CXX CORECXXFLAGS LDLIBS PICLDFLAGS INSPIRCD_VERBOSE SOCKETENGINE CORELDFLAGS
+@DO_EXPORT SOURCEPATH BUILDPATH INSPIRCD_STATIC
# Default target
TARGET = all
-@IFDEF M
+@IFDEF INSPIRCD_MODULE
HEADER = mod-header
FOOTER = mod-footer
- @TARGET BSD_MAKE TARGET = modules/${M:S/.so$//}.so
- @TARGET GNU_MAKE TARGET = modules/$(M:.so=).so
+ @TARGET BSD_MAKE TARGET = modules/${INSPIRCD_MODULE:S/.so$//}.so
+ @TARGET GNU_MAKE TARGET = modules/$(INSPIRCD_MODULE:.so=).so
@ENDIF
-@IFDEF T
+@IFDEF INSPIRCD_TARGET
HEADER =
FOOTER = target
- TARGET = $(T)
+ TARGET = $(INSPIRCD_TARGET)
@ENDIF
@IFEQ $(DBGOK) 0
@@ -159,10 +154,10 @@ all: $(FOOTER)
target: $(HEADER)
$(MAKEENV) perl make/calcdep.pl
- cd $(BUILDPATH); $(MAKEENV) $(MAKE) -f real.mk $(TARGET)
+ cd "$(BUILDPATH)"; $(MAKEENV) $(MAKE) -f real.mk $(TARGET)
debug:
- @${MAKE} D=1 all
+ @${MAKE} INSPIRCD_DEBUG=1 all
debug-header:
@echo "*************************************"
@@ -179,7 +174,7 @@ debug-header:
@echo "*************************************"
mod-header:
-@IFDEF PURE_STATIC
+@IFDEF INSPIRCD_STATIC
@echo 'Cannot build single modules in pure-static build'
@exit 1
@ENDIF
@@ -220,25 +215,32 @@ install: target
exit 1; \
fi
@-$(INSTALL) -d -o $(INSTUID) -m $(INSTMODE_DIR) $(BASE)
- @-$(INSTALL) -d -o $(INSTUID) -m $(INSTMODE_DIR) $(BASE)/data
- @-$(INSTALL) -d -o $(INSTUID) -m $(INSTMODE_DIR) $(BASE)/logs
+ @-$(INSTALL) -d -o $(INSTUID) -m $(INSTMODE_DIR) $(DATPATH)
+ @-$(INSTALL) -d -o $(INSTUID) -m $(INSTMODE_DIR) $(LOGPATH)
@-$(INSTALL) -d -m $(INSTMODE_DIR) $(BINPATH)
- @-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/aliases
@-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/modules
+ @-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/services
+ @-$(INSTALL) -d -m $(INSTMODE_DIR) $(MANPATH)
@-$(INSTALL) -d -m $(INSTMODE_DIR) $(MODPATH)
- [ $(BUILDPATH)/bin/ -ef $(BINPATH) ] || $(INSTALL) -m $(INSTMODE_BIN) $(BUILDPATH)/bin/inspircd $(BINPATH)
-@IFNDEF PURE_STATIC
- [ $(BUILDPATH)/modules/ -ef $(MODPATH) ] || $(INSTALL) -m $(INSTMODE_LIB) $(BUILDPATH)/modules/*.so $(MODPATH)
+ [ "$(BUILDPATH)/bin/" -ef $(BINPATH) ] || $(INSTALL) -m $(INSTMODE_BIN) "$(BUILDPATH)/bin/inspircd" $(BINPATH)
+@IFNDEF INSPIRCD_STATIC
+ [ "$(BUILDPATH)/modules/" -ef $(MODPATH) ] || $(INSTALL) -m $(INSTMODE_LIB) "$(BUILDPATH)/modules/"*.so $(MODPATH)
@ENDIF
- -$(INSTALL) -m $(INSTMODE_BIN) inspircd $(BASE) 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_BIN) @CONFIGURE_DIRECTORY@/inspircd $(BASE) 2>/dev/null
-$(INSTALL) -m $(INSTMODE_LIB) .gdbargs $(BASE)/.gdbargs 2>/dev/null
@IFEQ $(SYSTEM) darwin
- -$(INSTALL) -m $(INSTMODE_BIN) org.inspircd.plist $(BASE) 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_BIN) @CONFIGURE_DIRECTORY@/org.inspircd.plist $(BASE) 2>/dev/null
+@ENDIF
+@IFEQ $(SYSTEM) linux
+ -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd.service $(BASE) 2>/dev/null
@ENDIF
+ -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd.1 $(MANPATH) 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd-genssl.1 $(MANPATH) 2>/dev/null
-$(INSTALL) -m $(INSTMODE_BIN) tools/genssl $(BINPATH)/inspircd-genssl 2>/dev/null
-$(INSTALL) -m $(INSTMODE_LIB) docs/conf/*.example $(CONPATH)/examples
- -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/aliases/*.example $(CONPATH)/examples/aliases
-$(INSTALL) -m $(INSTMODE_LIB) docs/conf/modules/*.example $(CONPATH)/examples/modules
+ -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/services/*.example $(CONPATH)/examples/services
+ -$(INSTALL) -m $(INSTMODE_LIB) *.pem $(CONPATH) 2>/dev/null
@echo ""
@echo "*************************************"
@echo "* INSTALL COMPLETE! *"
@@ -253,36 +255,39 @@ install: target
@echo 'Remember to create your config file:' $(CONPATH)/inspircd.conf
@echo 'Examples are available at:' $(CONPATH)/examples/
-GNUmakefile BSDmakefile: make/template/main.mk src/version.sh configure .config.cache
- ./configure -update
+GNUmakefile BSDmakefile: make/template/main.mk src/version.sh configure @CONFIGURE_CACHE_FILE@
+ ./configure --update
@TARGET BSD_MAKE .MAKEFILEDEPS: BSDmakefile
clean:
@echo Cleaning...
- -rm -f $(BUILDPATH)/bin/inspircd $(BUILDPATH)/include $(BUILDPATH)/real.mk
- -rm -rf $(BUILDPATH)/obj $(BUILDPATH)/modules
- @-rmdir $(BUILDPATH)/bin 2>/dev/null
- @-rmdir $(BUILDPATH) 2>/dev/null
+ -rm -f "$(BUILDPATH)/bin/inspircd" "$(BUILDPATH)/include" "$(BUILDPATH)/real.mk"
+ -rm -rf "$(BUILDPATH)/obj" "$(BUILDPATH)/modules"
+ @-rmdir "$(BUILDPATH)/bin" 2>/dev/null
+ @-rmdir "$(BUILDPATH)" 2>/dev/null
@echo Completed.
deinstall:
-rm -f $(BINPATH)/inspircd
-rm -rf $(CONPATH)/examples
- -rm -f $(MODPATH)/*.so
+ -rm -f $(MANPATH)/inspircd.1
+ -rm -f $(MANPATH)/inspircd-genssl.1
+ -rm -f $(MODPATH)/m_*.so
+ -rm -f $(MODPATH)/core_*.so
-rm -f $(BASE)/.gdbargs
+ -rm -f $(BASE)/inspircd.service
-rm -f $(BASE)/org.inspircd.plist
configureclean:
- rm -f .config.cache
+ rm -f .gdbargs
rm -f BSDmakefile
rm -f GNUmakefile
rm -f include/config.h
- rm -f inspircd
- -rm -f org.inspircd.plist
+ rm -rf @CONFIGURE_DIRECTORY@
distclean: clean configureclean
- -rm -rf $(SOURCEPATH)/run
- find $(SOURCEPATH)/src/modules -type l | xargs rm -f
+ -rm -rf "$(SOURCEPATH)/run"
+ find "$(SOURCEPATH)/src/modules" -type l | xargs rm -f
help:
@echo 'InspIRCd Makefile'
@@ -290,11 +295,11 @@ help:
@echo 'Use: ${MAKE} [flags] [targets]'
@echo ''
@echo 'Flags:'
- @echo ' V=1 Show the full command being executed instead of "BUILD: dns.cpp"'
- @echo ' D=1 Enable debug build, for module development or crash tracing'
- @echo ' D=2 Enable debug build with optimizations, for detailed backtraces'
- @echo ' DESTDIR= Specify a destination root directory (for tarball creation)'
- @echo ' -j <N> Run a parallel build using N jobs'
+ @echo ' INSPIRCD_VERBOSE=1 Show the full command being executed instead of "BUILD: dns.cpp"'
+ @echo ' INSPIRCD_DEBUG=1 Enable debug build, for module development or crash tracing'
+ @echo ' INSPIRCD_DEBUG=2 Enable debug build with optimizations, for detailed backtraces'
+ @echo ' DESTDIR= Specify a destination root directory (for tarball creation)'
+ @echo ' -j <N> Run a parallel build using N jobs'
@echo ''
@echo 'Targets:'
@echo ' all Complete build of InspIRCd, without installing (default)'
@@ -302,14 +307,16 @@ help:
@echo ' Currently installs to ${BASE}'
@echo ' debug Compile a debug build. Equivalent to "make D=1 all"'
@echo ''
- @echo ' M=m_foo Builds a single module (cmd_foo also works here)'
- @echo ' T=target Builds a user-specified target, such as "inspircd" or "modules"'
- @echo ' Other targets are specified by their path in the build directory'
- @echo ' Multiple targets may be separated by a space'
+ @echo ' INSPIRCD_MODULE=m_foo Builds a single module (core_foo also works here)'
+ @echo ' INSPIRCD_TARGET=target Builds a user-specified target, such as "inspircd" or "modules"'
+ @echo ' Other targets are specified by their path in the build directory'
+ @echo ' Multiple targets may be separated by a space'
@echo ''
@echo ' clean Cleans object files produced by the compile'
@echo ' distclean Cleans all generated files (build, configure, run, etc)'
@echo ' deinstall Removes the files created by "make install"'
@echo
+.NOTPARALLEL:
+
.PHONY: all target debug debug-header mod-header mod-footer std-header finishmessage install clean deinstall configureclean help
diff --git a/make/template/org.inspircd.plist b/make/template/org.inspircd.plist
index ef5ef199b..ae4e90916 100644
--- a/make/template/org.inspircd.plist
+++ b/make/template/org.inspircd.plist
@@ -5,14 +5,14 @@
<dict>
<key>Iterations</key>
<integer>3</integer>
+ <key>KeepAlive</key>
+ <true/>
<key>Label</key>
<string>org.inspircd</string>
<key>LowPriorityIO</key>
<true/>
<key>Nice</key>
<integer>1</integer>
- <key>OnDemand</key>
- <false/>
<key>Program</key>
<string>@BINARY_DIR@/inspircd</string>
<key>ProgramArguments</key>
@@ -22,7 +22,13 @@
</array>
<key>ServiceIPC</key>
<false/>
+ <key>StandardOutPath</key>
+ <string>@LOG_DIR@/launchd-stdout.log</string>
+ <key>StandardErrorPath</key>
+ <string>@LOG_DIR@/launchd-stderr.log</string>
<key>UserName</key>
- <string>ircdaemon</string>
+ <string>@USER@</string>
+ <key>GroupName</key>
+ <string>@GROUP@</string>
</dict>
</plist>
diff --git a/make/test/clock_gettime.cpp b/make/test/clock_gettime.cpp
index 91d8cd412..d111d591f 100644
--- a/make/test/clock_gettime.cpp
+++ b/make/test/clock_gettime.cpp
@@ -1,6 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2013 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
diff --git a/make/test/compiler.cpp b/make/test/compiler.cpp
new file mode 100644
index 000000000..f01423325
--- /dev/null
+++ b/make/test/compiler.cpp
@@ -0,0 +1,39 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2014-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 <iostream>
+#if defined _LIBCPP_VERSION
+# include <array>
+# include <type_traits>
+# include <unordered_map>
+#else
+# include <tr1/array>
+# include <tr1/type_traits>
+# include <tr1/unordered_map>
+#endif
+
+#if defined __llvm__ && !defined __clang__ && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 1
+# error "LLVM-GCC 4.2.1 has broken visibility support."
+#endif
+
+int main() {
+ std::cout << "Hello, World!" << std::endl;
+ return 0;
+}
diff --git a/make/test/compiler_info.cpp b/make/test/compiler_info.cpp
new file mode 100644
index 000000000..10b156fc8
--- /dev/null
+++ b/make/test/compiler_info.cpp
@@ -0,0 +1,41 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2017 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 <iostream>
+
+#if defined __INTEL_COMPILER // Also defines __clang__ and __GNUC__
+# define INSPIRCD_COMPILER_NAME "Intel"
+# define INSPIRCD_COMPILER_VERSION (__INTEL_COMPILER / 100) << '.' << (__INTEL_COMPILER % 100)
+#elif defined __clang__ // Also defines __GNUC__
+# if defined __apple_build_version__
+# define INSPIRCD_COMPILER_NAME "AppleClang"
+# else
+# define INSPIRCD_COMPILER_NAME "Clang"
+# endif
+# define INSPIRCD_COMPILER_VERSION __clang_major__ << '.' << __clang_minor__
+#elif defined __GNUC__
+# define INSPIRCD_COMPILER_NAME "GCC"
+# define INSPIRCD_COMPILER_VERSION __GNUC__ << '.' << __GNUC_MINOR__
+#endif
+
+int main() {
+ std::cout << "NAME " << INSPIRCD_COMPILER_NAME << std::endl
+ << "VERSION " << INSPIRCD_COMPILER_VERSION << std::endl;
+ return 0;
+}
diff --git a/make/test/eventfd.cpp b/make/test/eventfd.cpp
index 980d04485..9b38b793b 100644
--- a/make/test/eventfd.cpp
+++ b/make/test/eventfd.cpp
@@ -1,6 +1,8 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2012 William Pitcock <nenolod@dereferenced.org>
+ * Copyright (C) 2009-2010 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
diff --git a/make/test/kqueue.cpp b/make/test/kqueue.cpp
index a8b9882cf..708677adf 100644
--- a/make/test/kqueue.cpp
+++ b/make/test/kqueue.cpp
@@ -1,6 +1,7 @@
/*
* 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
diff --git a/make/unit-cc.pl b/make/unit-cc.pl
index 77f97a3f2..1cf6cf866 100755
--- a/make/unit-cc.pl
+++ b/make/unit-cc.pl
@@ -19,16 +19,23 @@
#
+BEGIN {
+ push @INC, $ENV{SOURCEPATH};
+ require 5.10.0;
+}
+
use strict;
-use warnings;
-BEGIN { push @INC, $ENV{SOURCEPATH}; }
-use make::configure;
+use warnings FATAL => qw(all);
+
+use File::Spec::Functions qw(abs2rel);
+
+use make::console;
+use make::directive;
chdir $ENV{BUILDPATH};
my $type = shift;
my $out = shift;
-my $verbose = ($type =~ s/-v$//);
if ($type eq 'gen-ld') {
do_static_find(@ARGV);
@@ -49,10 +56,25 @@ if ($type eq 'gen-ld') {
}
exit 1;
+sub message($$$) {
+ my ($type, $file, $command) = @_;
+ if ($ENV{INSPIRCD_VERBOSE}) {
+ print "$command\n";
+ } else {
+ print_format "\t<|GREEN $type:|>\t\t$file\n";
+ }
+}
+
+sub rpath($) {
+ my $message = shift;
+ $message =~ s/-L(\S+)/-Wl,-rpath,$1 -L$1/g unless defined $ENV{INSPIRCD_DISABLE_RPATH};
+ return $message;
+}
+
sub do_static_find {
my @flags;
for my $file (@ARGV) {
- push @flags, get_property($file, 'LinkerFlags');
+ push @flags, rpath(get_directive($file, 'LinkerFlags', ''));
}
open F, '>', $out;
print F join ' ', @flags;
@@ -61,32 +83,37 @@ sub do_static_find {
}
sub do_static_link {
- my $execstr = "$ENV{RUNLD} -o $out $ENV{CORELDFLAGS}";
+ my $execstr = "$ENV{CXX} -o $out $ENV{CORELDFLAGS}";
+ my $link_flags = '';
for (@ARGV) {
if (/\.cmd$/) {
open F, '<', $_;
my $libs = <F>;
chomp $libs;
- $execstr .= ' '.$libs;
+ $link_flags .= ' '.$libs;
close F;
} else {
$execstr .= ' '.$_;
}
}
- $execstr .= ' '.$ENV{LDLIBS};
- print "$execstr\n" if $verbose;
+ $execstr .= ' '.$ENV{LDLIBS}.' '.$link_flags;
+ message 'LINK', $out, $execstr;
exec $execstr;
}
sub do_core_link {
- my $execstr = "$ENV{RUNLD} -o $out $ENV{CORELDFLAGS} @_ $ENV{LDLIBS}";
- print "$execstr\n" if $verbose;
+ my $execstr = "$ENV{CXX} -o $out $ENV{CORELDFLAGS} @_ $ENV{LDLIBS}";
+ message 'LINK', $out, $execstr;
exec $execstr;
}
sub do_link_dir {
- my $execstr = "$ENV{RUNLD} -o $out $ENV{PICLDFLAGS} @_";
- print "$execstr\n" if $verbose;
+ my ($dir, $link_flags) = (shift, '');
+ for my $file (<$dir/*.cpp>) {
+ $link_flags .= rpath(get_directive($file, 'LinkerFlags', '')) . ' ';
+ }
+ my $execstr = "$ENV{CXX} -o $out $ENV{PICLDFLAGS} $link_flags @_";
+ message 'LINK', $out, $execstr;
exec $execstr;
}
@@ -95,25 +122,22 @@ sub do_compile {
my $flags = '';
my $libs = '';
- my $binary = $ENV{RUNCC};
if ($do_compile) {
- $flags = $ENV{CORECXXFLAGS} . ' ' . get_property($file, 'CompileFlags');
+ $flags = $ENV{CORECXXFLAGS} . ' ' . get_directive($file, 'CompilerFlags', '');
if ($file =~ m#(?:^|/)((?:m|core)_[^/. ]+)(?:\.cpp|/.*\.cpp)$#) {
$flags .= ' -DMODNAME=\\"'.$1.'\\"';
}
- } else {
- $binary = $ENV{RUNLD};
}
if ($do_link) {
$flags = join ' ', $flags, $ENV{PICLDFLAGS};
- $libs = get_property($file, 'LinkerFlags');
+ $libs = rpath(get_directive($file, 'LinkerFlags', ''));
} else {
$flags .= ' -c';
}
- my $execstr = "$binary -o $out $flags $file $libs";
- print "$execstr\n" if $verbose;
+ my $execstr = "$ENV{CXX} -o $out $flags $file $libs";
+ message 'BUILD', abs2rel($file, "$ENV{SOURCEPATH}/src"), $execstr;
exec $execstr;
}
diff --git a/make/utilities.pm b/make/utilities.pm
deleted file mode 100644
index 404243966..000000000
--- a/make/utilities.pm
+++ /dev/null
@@ -1,516 +0,0 @@
-#
-# InspIRCd -- Internet Relay Chat Daemon
-#
-# Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
-# Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
-# Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
-# Copyright (C) 2007 Dennis Friis <peavey@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/>.
-#
-
-
-BEGIN {
- require 5.8.0;
-}
-
-package make::utilities;
-
-use strict;
-use warnings FATAL => qw(all);
-
-use Exporter 'import';
-use Fcntl;
-use File::Path;
-use File::Spec::Functions qw(rel2abs);
-use Getopt::Long;
-use POSIX;
-
-our @EXPORT = qw(get_version module_installed prompt_bool prompt_dir prompt_string get_cpu_count make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring);
-
-my %already_added = ();
-my %version = ();
-
-sub get_version {
- return %version if %version;
-
- # Attempt to retrieve version information from src/version.sh
- chomp(my $vf = `sh src/version.sh 2>/dev/null`);
- if ($vf =~ /^InspIRCd-([0-9]+)\.([0-9]+)\.([0-9]+)(?:\+(\w+))?$/) {
- %version = ( MAJOR => $1, MINOR => $2, PATCH => $3, LABEL => $4 );
- }
-
- # Attempt to retrieve missing version information from Git
- chomp(my $gr = `git describe --tags 2>/dev/null`);
- if ($gr =~ /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\d+-(\w+))?$/) {
- $version{MAJOR} = $1 unless defined $version{MAJOR};
- $version{MINOR} = $2 unless defined $version{MINOR};
- $version{PATCH} = $3 unless defined $version{PATCH};
- $version{LABEL} = $4 if defined $4;
- }
-
- # The user is using a stable release which does not have
- # a label attached.
- $version{LABEL} = 'release' unless defined $version{LABEL};
-
- # If any of these fields are missing then the user has deleted the
- # version file and is not running from Git. Fill in the fields with
- # dummy data so we don't get into trouble with undef values later.
- $version{MAJOR} = '0' unless defined $version{MAJOR};
- $version{MINOR} = '0' unless defined $version{MINOR};
- $version{PATCH} = '0' unless defined $version{PATCH};
-
- return %version;
-}
-
-sub module_installed($) {
- my $module = shift;
- eval("use $module;");
- return !$@;
-}
-
-sub prompt_bool($$$) {
- my ($interactive, $question, $default) = @_;
- my $answer = prompt_string($interactive, $question, $default ? 'y' : 'n');
- return $answer =~ /y/i;
-}
-
-sub prompt_dir($$$) {
- my ($interactive, $question, $default) = @_;
- my ($answer, $create) = (undef, 'y');
- do {
- $answer = rel2abs(prompt_string($interactive, $question, $default));
- $create = prompt_bool($interactive && !-d $answer, "$answer does not exist. Create it?", 'y');
- my $mkpath = eval {
- mkpath($answer, 0, 0750);
- return 1;
- };
- unless (defined $mkpath) {
- print "Error: unable to create $answer!\n\n";
- $create = 0;
- }
- } while (!$create);
- return $answer;
-}
-
-sub prompt_string($$$) {
- my ($interactive, $question, $default) = @_;
- return $default unless $interactive;
- print $question, "\n";
- print "[\e[1;32m$default\e[0m] => ";
- chomp(my $answer = <STDIN>);
- print "\n";
- return $answer ? $answer : $default;
-}
-
-sub get_cpu_count {
- my $count = 1;
- if ($^O =~ /bsd/) {
- $count = `sysctl -n hw.ncpu`;
- } elsif ($^O eq 'darwin') {
- $count = `sysctl -n hw.activecpu`;
- } elsif ($^O eq 'linux') {
- $count = `getconf _NPROCESSORS_ONLN`;
- } elsif ($^O eq 'solaris') {
- $count = `psrinfo -p`;
- }
- chomp($count);
- return $count;
-}
-
-sub promptstring($$$$$)
-{
- my ($prompt, $configitem, $default, $package, $commandlineswitch) = @_;
- my $var;
- if (!$main::interactive)
- {
- my $opt_commandlineswitch;
- GetOptions ("$commandlineswitch=s" => \$opt_commandlineswitch);
- if (defined $opt_commandlineswitch)
- {
- print "\e[1;32m$opt_commandlineswitch\e[0m\n";
- $var = $opt_commandlineswitch;
- }
- else
- {
- die "Could not detect $package! Please specify the $prompt via the command line option \e[1;32m--$commandlineswitch=\"/path/to/file\"\e[0m";
- }
- }
- else
- {
- print "\nPlease enter the $prompt?\n";
- print "[\e[1;32m$default\e[0m] -> ";
- chomp($var = <STDIN>);
- }
- if ($var eq "")
- {
- $var = $default;
- }
- $main::config{$configitem} = $var;
-}
-
-sub make_rpath($;$)
-{
- my ($executable, $module) = @_;
- chomp(my $data = `$executable`);
- my $output = "";
- while ($data =~ /-L(\S+)/)
- {
- my $libpath = $1;
- if (!exists $already_added{$libpath})
- {
- print "Adding extra library path to \e[1;32m$module\e[0m ... \e[1;32m$libpath\e[0m\n";
- $already_added{$libpath} = 1;
- }
- $output .= "-Wl,-rpath -Wl,$libpath -L$libpath " unless defined $main::opt_disablerpath;
- $data =~ s/-L(\S+)//;
- }
- return $output;
-}
-
-sub extend_pkg_path()
-{
- if (!exists $ENV{PKG_CONFIG_PATH})
- {
- $ENV{PKG_CONFIG_PATH} = "/usr/lib/pkgconfig:/usr/local/lib/pkgconfig:/usr/local/libdata/pkgconfig:/usr/X11R6/libdata/pkgconfig";
- }
- else
- {
- $ENV{PKG_CONFIG_PATH} .= ":/usr/local/lib/pkgconfig:/usr/local/libdata/pkgconfig:/usr/X11R6/libdata/pkgconfig";
- }
-}
-
-sub pkgconfig_get_include_dirs($$$;$)
-{
- my ($packagename, $headername, $defaults, $module) = @_;
-
- extend_pkg_path();
-
- print "Locating include directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
-
- my $v = `pkg-config --modversion $packagename 2>/dev/null`;
- my $ret = `pkg-config --cflags $packagename 2>/dev/null`;
- my $foo = "";
- if ((!defined $v) || ($v eq ""))
- {
- print "\e[31mCould not find $packagename via pkg-config\e[m (\e[1;32mplease install pkg-config\e[m)\n";
- my $locbin = $^O eq 'solaris' ? 'slocate' : 'locate';
- $foo = `$locbin "$headername" 2>/dev/null | head -n 1`;
- my $find = $foo =~ /(.+)\Q$headername\E/ ? $1 : '';
- chomp($find);
- if ((defined $find) && ($find ne "") && ($find ne $packagename))
- {
- print "(\e[1;32mFound via search\e[0m) ";
- $foo = "-I$1";
- }
- else
- {
- $foo = " ";
- undef $v;
- }
- $ret = "$foo";
- }
- if (($defaults ne "") && (($ret eq "") || (!defined $ret)))
- {
- $ret = "$foo " . $defaults;
- }
- chomp($ret);
- if ((($ret eq " ") || (!defined $ret)) && ((!defined $v) || ($v eq "")))
- {
- my $key = "default_includedir_$packagename";
- if (exists $main::config{$key})
- {
- $ret = $main::config{$key};
- }
- else
- {
- $headername =~ s/^\///;
- promptstring("path to the directory containing $headername", $key, "/usr/include",$packagename,"$packagename-includes");
- $packagename =~ tr/a-z/A-Z/;
- if (defined $v)
- {
- $main::config{$key} = "-I$main::config{$key}" . " $defaults -DVERSION_$packagename=\"$v\"";
- }
- else
- {
- $main::config{$key} = "-I$main::config{$key}" . " $defaults -DVERSION_$packagename=\"0.0\"";
- }
- $main::config{$key} =~ s/^\s+//g;
- $ret = $main::config{$key};
- return $ret;
- }
- }
- else
- {
- chomp($v);
- my $key = "default_includedir_$packagename";
- $packagename =~ tr/a-z/A-Z/;
- $main::config{$key} = "$ret -DVERSION_$packagename=\"$v\"";
- $main::config{$key} =~ s/^\s+//g;
- $ret = $main::config{$key};
- print "\e[1;32m$ret\e[0m (version $v)\n";
- }
- $ret =~ s/^\s+//g;
- return $ret;
-}
-
-sub pkgconfig_check_version($$;$)
-{
- my ($packagename, $version, $module) = @_;
-
- extend_pkg_path();
-
- print "Checking version of package \e[1;32m$packagename\e[0m is >= \e[1;32m$version\e[0m... ";
-
- my $v = `pkg-config --modversion $packagename 2>/dev/null`;
- if (defined $v)
- {
- chomp($v);
- }
- if ((defined $v) && ($v ne ""))
- {
- if (!system "pkg-config --atleast-version $version $packagename")
- {
- print "\e[1;32mYes (version $v)\e[0m\n";
- return 1;
- }
- else
- {
- print "\e[1;32mNo (version $v)\e[0m\n";
- return 0;
- }
- }
- # If we didnt find it, we cant definitively say its too old.
- # Return ok, and let pkgconflibs() or pkgconfincludes() pick up
- # the missing library later on.
- print "\e[1;32mNo (not found)\e[0m\n";
- return 1;
-}
-
-sub pkgconfig_get_lib_dirs($$$;$)
-{
- my ($packagename, $libname, $defaults, $module) = @_;
-
- extend_pkg_path();
-
- print "Locating library directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
-
- my $v = `pkg-config --modversion $packagename 2>/dev/null`;
- my $ret = `pkg-config --libs $packagename 2>/dev/null`;
-
- my $foo = "";
- if ((!defined $v) || ($v eq ""))
- {
- my $locbin = $^O eq 'solaris' ? 'slocate' : 'locate';
- $foo = `$locbin "$libname" | head -n 1`;
- $foo =~ /(.+)\Q$libname\E/;
- my $find = $1;
- chomp($find);
- if ((defined $find) && ($find ne "") && ($find ne $packagename))
- {
- print "(\e[1;32mFound via search\e[0m) ";
- $foo = "-L$1";
- }
- else
- {
- $foo = " ";
- undef $v;
- }
- $ret = "$foo";
- }
-
- if (($defaults ne "") && (($ret eq "") || (!defined $ret)))
- {
- $ret = "$foo " . $defaults;
- }
- chomp($ret);
- if ((($ret eq " ") || (!defined $ret)) && ((!defined $v) || ($v eq "")))
- {
- my $key = "default_libdir_$packagename";
- if (exists $main::config{$key})
- {
- $ret = $main::config{$key};
- }
- else
- {
- $libname =~ s/^\///;
- promptstring("path to the directory containing $libname", $key, "/usr/lib",$packagename,"$packagename-libs");
- $main::config{$key} = "-L$main::config{$key}" . " $defaults";
- $main::config{$key} =~ s/^\s+//g;
- $ret = $main::config{$key};
- return $ret;
- }
- }
- else
- {
- chomp($v);
- print "\e[1;32m$ret\e[0m (version $v)\n";
- my $key = "default_libdir_$packagename";
- $main::config{$key} = $ret;
- $main::config{$key} =~ s/^\s+//g;
- $ret =~ s/^\s+//g;
- }
- $ret =~ s/^\s+//g;
- return $ret;
-}
-
-# Translate a $CompileFlags etc line and parse out function calls
-# to functions within these modules at configure time.
-sub translate_functions($$)
-{
- my ($line,$module) = @_;
-
- eval
- {
- $module =~ /modules*\/(.+?)$/;
- $module = $1;
-
- if ($line =~ /ifuname\(\!"(\w+)"\)/)
- {
- my $uname = $1;
- if ($uname eq $^O)
- {
- $line = "";
- return "";
- }
-
- $line =~ s/ifuname\(\!"(.+?)"\)//;
- }
-
- if ($line =~ /ifuname\("(\w+)"\)/)
- {
- my $uname = $1;
- if ($uname ne $^O)
- {
- $line = "";
- return "";
- }
-
- $line =~ s/ifuname\("(.+?)"\)//;
- }
-
- if ($line =~ /if\("(\w+)"\)/)
- {
- if (defined $main::config{$1})
- {
- if (($main::config{$1} !~ /y/i) and ($main::config{$1} ne "1"))
- {
- $line = "";
- return "";
- }
- }
-
- $line =~ s/if\("(.+?)"\)//;
- }
- if ($line =~ /if\(\!"(\w+)"\)/)
- {
- if (!exists $main::config{$1})
- {
- $line = "";
- return "";
- }
- else
- {
- if (defined $1)
- {
- if (exists ($main::config{$1}) and (($main::config{$1} =~ /y/i) or ($main::config{$1} eq "1")))
- {
- $line = "";
- return "";
- }
- }
- }
-
- $line =~ s/if\(\!"(.+?)"\)//;
- }
- while ($line =~ /exec\("(.+?)"\)/)
- {
- print "Executing program for module \e[1;32m$module\e[0m ... \e[1;32m$1\e[0m\n";
- my $replace = `$1`;
- die $replace if ($replace =~ /Configuration failed/);
- chomp($replace);
- $line =~ s/exec\("(.+?)"\)/$replace/;
- }
- while ($line =~ /execruntime\("(.+?)"\)/)
- {
- $line =~ s/execruntime\("(.+?)"\)/`$1`/;
- }
- while ($line =~ /eval\("(.+?)"\)/)
- {
- print "Evaluating perl code for module \e[1;32m$module\e[0m ... ";
- my $tmpfile;
- do
- {
- $tmpfile = tmpnam();
- } until sysopen(TF, $tmpfile, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, 0700);
- print "(Created and executed \e[1;32m$tmpfile\e[0m)\n";
- print TF $1;
- close TF;
- my $replace = `perl $tmpfile`;
- chomp($replace);
- $line =~ s/eval\("(.+?)"\)/$replace/;
- }
- while ($line =~ /pkgconflibs\("(.+?)","(.+?)","(.+?)"\)/)
- {
- my $replace = pkgconfig_get_lib_dirs($1, $2, $3, $module);
- $line =~ s/pkgconflibs\("(.+?)","(.+?)","(.+?)"\)/$replace/;
- }
- while ($line =~ /pkgconfversion\("(.+?)","(.+?)"\)/)
- {
- if (pkgconfig_check_version($1, $2, $module) != 1)
- {
- die "Version of package $1 is too old. Please upgrade it to version \e[1;32m$2\e[0m or greater and try again.";
- }
- # This doesnt actually get replaced with anything
- $line =~ s/pkgconfversion\("(.+?)","(.+?)"\)//;
- }
- while ($line =~ /pkgconflibs\("(.+?)","(.+?)",""\)/)
- {
- my $replace = pkgconfig_get_lib_dirs($1, $2, "", $module);
- $line =~ s/pkgconflibs\("(.+?)","(.+?)",""\)/$replace/;
- }
- while ($line =~ /pkgconfincludes\("(.+?)","(.+?)",""\)/)
- {
- my $replace = pkgconfig_get_include_dirs($1, $2, "", $module);
- $line =~ s/pkgconfincludes\("(.+?)","(.+?)",""\)/$replace/;
- }
- while ($line =~ /pkgconfincludes\("(.+?)","(.+?)","(.+?)"\)/)
- {
- my $replace = pkgconfig_get_include_dirs($1, $2, $3, $module);
- $line =~ s/pkgconfincludes\("(.+?)","(.+?)","(.+?)"\)/$replace/;
- }
- while ($line =~ /rpath\("(.+?)"\)/)
- {
- my $replace = make_rpath($1,$module);
- $line =~ s/rpath\("(.+?)"\)/$replace/;
- }
- };
- if ($@)
- {
- my $err = $@;
- #$err =~ s/at .+? line \d+.*//g;
- print "\n\nConfiguration failed. The following error occured:\n\n$err\n";
- print "\nMake sure you have pkg-config installed\n";
- print "\nIn the case of gnutls configuration errors on debian,\n";
- print "Ubuntu, etc, you should ensure that you have installed\n";
- print "gnutls-bin as well as gnutls-dev and gnutls.\n";
- exit;
- }
- else
- {
- return $line;
- }
-}
-
-1;
-
diff --git a/modulemanager b/modulemanager
index 86f9ca0c4..7471dcc77 100755
--- a/modulemanager
+++ b/modulemanager
@@ -3,6 +3,7 @@
#
# InspIRCd -- Internet Relay Chat Daemon
#
+# Copyright (C) 2012-2017 Peter Powell <petpow@saberuk.com>
# Copyright (C) 2008-2009 Robin Burchell <robin+git@viroteck.net>
#
# This file is part of InspIRCd. InspIRCd is free software: you can
@@ -19,23 +20,26 @@
#
-use strict;
-use warnings FATAL => qw(all);
-
-use make::utilities;
-
BEGIN {
- unless (module_installed("LWP::Simple")) {
+ require 5.10.0;
+ unless (eval "use LWP::Simple; 1") {
die "Your system is missing the LWP::Simple Perl module!";
}
- unless (module_installed("Crypt::SSLeay") || module_installed("IO::Socket::SSL")) {
+ unless (eval "use Crypt::SSLeay; 1" || eval "use IO::Socket::SSL; 1") {
die "Your system is missing the Crypt::SSLeay or IO::Socket::SSL Perl modules!";
}
-
}
-use File::Basename;
-use LWP::Simple;
+use feature ':5.10';
+use strict;
+use warnings FATAL => qw(all);
+
+use File::Basename qw(basename);
+use FindBin qw($RealDir);
+
+use lib $RealDir;
+use make::common;
+use make::console;
my %installed;
# $installed{name} = $version
@@ -102,7 +106,7 @@ sub parse_url {
}
# hash of installed module versions from our mini-database, key (m_foobar) to version (00abacca..).
-my %mod_versions;
+my %mod_versions = read_config_file '.modulemanager';
# useless helper stub
sub getmodversion {
@@ -110,19 +114,6 @@ sub getmodversion {
return $mod_versions{$file};
}
-# read in installed versions
-if (-e '.modulemanager')
-{
- open SRC, '.modulemanager' or die ".modulemanager exists but i can't read it: $!";
- while (<SRC>)
- {
- s/\n//;
- (my $mod, my $ver) = split(/ /, $_);
- $mod_versions{$mod} = $ver;
- }
- close SRC;
-}
-
# read in external URL sources
open SRC, 'sources.list' or die "Could not open sources.list: $!";
while (<SRC>) {
@@ -262,10 +253,8 @@ sub resolve_deps {
}
}
-my $action = $#ARGV >= 0 ? lc shift @ARGV : 'help';
-
-if ($action eq 'install') {
- for my $mod (@ARGV) {
+command 'install', 'Install a third-party module', sub {
+ for my $mod (@_) {
my $vers = $mod =~ s/=([-0-9.]+)// ? $1 : undef;
$mod = lc $mod;
unless ($modules{$mod}) {
@@ -279,7 +268,9 @@ if ($action eq 'install') {
}
$todo{$mod} = $ver;
}
-} elsif ($action eq 'upgrade') {
+};
+
+command 'upgrade', 'Upgrade a third-party module', sub {
my @installed = sort keys %installed;
for my $mod (@installed) {
next unless $mod =~ /^m_/;
@@ -289,7 +280,9 @@ if ($action eq 'install') {
%todo = %saved;
}
}
-} elsif ($action eq 'list') {
+};
+
+command 'list', 'List available third-party modules', sub {
my @all = sort keys %modules;
for my $mod (@all) {
my @vers = sort { ver_cmp() } keys %{$modules{$mod}};
@@ -303,25 +296,16 @@ if ($action eq 'install') {
my $vers = join ' ', map { $_ eq $instver ? "\e[1m$_\e[m" : $_ } @vers;
print "$mod ($vers) - $desc\n";
}
-} else {
- print <<ENDUSAGE
-Use: $0 <action> <args>
-Action is one of the following
- install install new modules
- upgrade upgrade installed modules
- list lists available modules
-
-For installing a package, specify its name or name=version to force the
-installation of a specific version.
-ENDUSAGE
-;exit 1;
-}
+ exit 0;
+};
+
+execute_command @ARGV;
resolve_deps(0);
$| = 1; # immediate print of lines without \n
-print "Processing changes for $action...\n";
+print "Processing changes...\n";
for my $mod (keys %installed) {
next if $todo{$mod};
print "Uninstalling $mod $installed{$mod}\n";
@@ -345,20 +329,20 @@ for my $mod (sort keys %todo) {
}
$mod_versions{$mod} = $ver;
- my $stat = getstore($url, "src/modules/$mod.cpp");
- if ($stat == 200) {
+ my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
+ my $response = $ua->get($url);
+
+ if ($response->is_success) {
+ open(MF, ">src/modules/$mod.cpp") or die "\nFilesystem not writable: $!";
+ print MF $response->decoded_content;
+ close(MF);
print " - done\n";
} else {
- print " - HTTP $stat\n";
+ printf "\nHTTP %s: %s\n", $response->code, $response->message;
}
}
# write database of installed versions
-open SRC, '>.modulemanager' or die "can't write installed versions to .modulemanager, won't be able to track upgrades properly: $!";
-foreach my $key (keys %mod_versions)
-{
- print SRC "$key $mod_versions{$key}\n";
-}
-close SRC;
+write_config_file '.modulemanager', %mod_versions;
print "Finished!\n";
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/modules/m_spanningtree/translate.cpp b/src/modules/m_spanningtree/translate.cpp
new file mode 100644
index 000000000..66e1bb35b
--- /dev/null
+++ b/src/modules/m_spanningtree/translate.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 "translate.h"
+
+std::string Translate::ModeChangeListToParams(const Modes::ChangeList::List& modes)
+{
+ 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/modules/m_spanningtree/translate.h b/src/modules/m_spanningtree/translate.h
new file mode 100644
index 000000000..a2bc6df78
--- /dev/null
+++ b/src/modules/m_spanningtree/translate.h
@@ -0,0 +1,30 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+namespace Translate
+{
+ /** 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))
diff --git a/tools/genssl b/tools/genssl
index aa8d48300..189311c1a 100755
--- a/tools/genssl
+++ b/tools/genssl
@@ -21,29 +21,31 @@
BEGIN {
- require 5.8.0;
+ require 5.10.0;
}
+use feature ':5.10';
use strict;
use warnings FATAL => qw(all);
use File::Temp();
# IMPORTANT: This script has to be able to run by itself so that it can be used
-# by binary distributions where the make/utilities.pm module will not
+# by binary distributions where the make/console.pm module will not
# be available!
sub prompt($$) {
my ($question, $default) = @_;
- print "$question\n";
+ return prompt_string(1, $question, $default) if eval 'use FindBin;use lib $FindBin::RealDir;use make::console; 1';
+ say $question;
print "[$default] => ";
chomp(my $answer = <STDIN>);
- print "\n";
+ say '';
return $answer ? $answer : $default;
}
if ($#ARGV != 0 || $ARGV[0] !~ /^(?:auto|gnutls|openssl)$/i) {
- print "Syntax: genssl <auto|gnutls|openssl>\n";
+ say STDERR "Usage: $0 <auto|gnutls|openssl>";
exit 1;
}
@@ -51,7 +53,7 @@ if ($#ARGV != 0 || $ARGV[0] !~ /^(?:auto|gnutls|openssl)$/i) {
my $certtool = $^O eq 'darwin' ? 'gnutls-certtool' : 'certtool';
# Check whether the user has the required tools installed.
-my $has_gnutls = !system "$certtool --version >/dev/null 2>&1";
+my $has_gnutls = `$certtool --version v 2>/dev/null`;
my $has_openssl = !system 'openssl version >/dev/null 2>&1';
# The framework the user has specified.
@@ -64,14 +66,14 @@ if ($tool eq 'auto') {
} elsif ($has_openssl) {
$tool = 'openssl';
} else {
- print STDERR "SSL generation failed: could not find $certtool or openssl in the PATH!\n";
+ say STDERR "SSL generation failed: could not find $certtool or openssl in the PATH!";
exit 1;
}
} elsif ($tool eq 'gnutls' && !$has_gnutls) {
- print STDERR "SSL generation failed: could not find '$certtool' in the PATH!\n";
+ say STDERR "SSL generation failed: could not find '$certtool' in the PATH!";
exit 1;
} elsif ($tool eq 'openssl' && !$has_openssl) {
- print STDERR "SSL generation failed: could not find 'openssl' in the PATH!\n";
+ say STDERR 'SSL generation failed: could not find \'openssl\' in the PATH!';
exit 1;
}
@@ -92,6 +94,8 @@ my $dercert;
my $status = 0;
if ($tool eq 'gnutls') {
+ $has_gnutls =~ /certtool.+?(\d+\.\d+)/;
+ my $sec_param = $1 lt '2.10' ? '--bits 2048' : '--sec-param normal';
my $tmp = new File::Temp();
print $tmp <<__GNUTLS_END__;
cn = "$common_name"
@@ -113,9 +117,9 @@ ocsp_signing_key
time_stamping_key
__GNUTLS_END__
close($tmp);
- $status ||= system "$certtool --generate-privkey --outfile key.pem";
+ $status ||= system "$certtool --generate-privkey $sec_param --outfile key.pem";
$status ||= system "$certtool --generate-self-signed --load-privkey key.pem --outfile cert.pem --template $tmp";
- $status ||= system "$certtool --generate-dh-params --bits 2048 --outfile dhparams.pem";
+ $status ||= system "$certtool --generate-dh-params $sec_param --outfile dhparams.pem";
$dercert = `$certtool --certificate-info --infile cert.pem --outder` unless $status;
} elsif ($tool eq 'openssl') {
my $tmp = new File::Temp();
@@ -135,13 +139,14 @@ __OPENSSL_END__
}
if ($status) {
- print STDERR "SSL generation failed: $tool exited with a non-zero status!\n";
+ say STDERR "SSL generation failed: $tool exited with a non-zero status!";
exit 1;
}
if (defined $dercert && eval 'use Digest::SHA; 1') {
my $hash = Digest::SHA->new(256);
$hash->add($dercert);
- print "\nAdd this TLSA record to your domain for DANE support:\n";
- print "_6697._tcp." . $common_name . " TLSA 3 0 1 " . $hash->hexdigest . "\n";
+ say '';
+ say 'Add this TLSA record to your domain for DANE support:';
+ say "_6697._tcp." . $common_name . " TLSA 3 0 1 " . $hash->hexdigest;
}
diff --git a/tools/test-build b/tools/test-build
index 7f1bf68cd..2b9ee4ee2 100755
--- a/tools/test-build
+++ b/tools/test-build
@@ -2,7 +2,7 @@
#
# InspIRCd -- Internet Relay Chat Daemon
#
-# Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+# Copyright (C) 2013-2014 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
@@ -19,48 +19,61 @@
BEGIN {
- require 5.8.0;
+ require 5.10.0;
unless (-f 'configure') {
- print "Error: test-build must be run from the main source directory!\n";
+ print "Error: $0 must be run from the main source directory!\n";
exit 1;
}
}
+use feature ':5.10';
use strict;
use warnings FATAL => qw(all);
+use FindBin qw($RealDir);
+
+use lib $RealDir;
+use make::common;
use make::configure;
-use make::utilities;
-$ENV{D} = $ENV{V} = 1;
+$ENV{INSPIRCD_DEBUG} = $ENV{INSPIRCD_VERBOSE} = 1;
system 'git', 'clean', '-dfx';
-foreach my $compiler ('g++', 'clang++', 'icpc') {
- next if system "$compiler -v > /dev/null 2>&1";
+my @compilers = $#ARGV >= 0 ? @ARGV : qw(g++ clang++ icpc);
+foreach my $compiler (@compilers) {
+ if (system "$compiler -v > /dev/null 2>&1") {
+ say "Skipping $compiler as it is not installed on this system!";
+ next;
+ }
$ENV{CXX} = $compiler;
- my @socketengines = ( 'select' );
+ my @socketengines = qw(select);
push @socketengines, 'epoll' if test_header $compiler, 'sys/epoll.h';
push @socketengines, 'kqueue' if test_file $compiler, 'kqueue.cpp';
push @socketengines, 'poll' if test_header $compiler, 'poll.h';
push @socketengines, 'ports' if test_header $compiler, 'ports.h';
foreach my $socketengine (@socketengines) {
- print "Attempting to build using the $compiler compiler and the $socketengine socket engine...\n";
- if (system './configure', '--disable-interactive', "--socketengine=$socketengine") {
- print "Failed to configure using the $compiler compiler and the $socketengine socket engine!\n";
+ say "Attempting to build using the $compiler compiler and the $socketengine socket engine...";
+ system './configure', '--enable-extras', $ENV{TEST_BUILD_MODULES} if defined $ENV{TEST_BUILD_MODULES};
+ if (system './configure', '--development', '--socketengine', $socketengine) {
+ say "Failed to configure using the $compiler compiler and the $socketengine socket engine!";
exit 1;
}
- $ENV{PURE_STATIC} = 1;
- if (system 'make', '-j'.get_cpu_count, 'install') {
- print "Failed to compile with static modules using the $compiler compiler and the $socketengine socket engine!\n";
- exit 1;
+ if (!defined $ENV{TEST_BUILD_DYNAMIC}) {
+ $ENV{INSPIRCD_STATIC} = 1;
+ if (system 'make', '-j'.get_cpu_count, 'install') {
+ say "Failed to compile with static modules using the $compiler compiler and the $socketengine socket engine!";
+ exit 1;
+ }
}
- delete $ENV{PURE_STATIC};
- if (system 'make', '-j'.get_cpu_count, 'install') {
- print "Failed to compile with dynamic modules using the $compiler compiler and the $socketengine socket engine!\n";
- exit 1;
+ if (!defined $ENV{TEST_BUILD_STATIC}) {
+ delete $ENV{INSPIRCD_STATIC};
+ if (system 'make', '-j'.get_cpu_count, 'install') {
+ say "Failed to compile with dynamic modules using the $compiler compiler and the $socketengine socket engine!";
+ exit 1;
+ }
}
- print "Building using the $compiler compiler and the $socketengine socket engine succeeded!\n";
+ say "Building using the $compiler compiler and the $socketengine socket engine succeeded!";
}
system 'git', 'clean', '-dfx';
diff --git a/tools/travis-ci.sh b/tools/travis-ci.sh
new file mode 100755
index 000000000..bb32e19a1
--- /dev/null
+++ b/tools/travis-ci.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+set -ev
+if [ "$TRAVIS_OS_NAME" = "linux" ]
+then
+ sudo apt-get update --assume-yes
+ sudo apt-get install --assume-yes libgeoip-dev libgnutls-dev libldap2-dev libmysqlclient-dev libpcre3-dev libpq-dev libsqlite3-dev libssl-dev libtre-dev
+else
+ >&2 echo "'$TRAVIS_OS_NAME' is an unknown Travis CI environment!"
+ exit 1
+fi
+export TEST_BUILD_MODULES="m_geoip.cpp,m_ldap.cpp,m_mysql.cpp,m_pgsql.cpp,m_regex_pcre.cpp,m_regex_posix.cpp,m_regex_tre.cpp,m_sqlite3.cpp,m_ssl_gnutls.cpp,m_ssl_openssl.cpp"
+./tools/test-build $CXX
diff --git a/win/CMakeLists.txt b/win/CMakeLists.txt
index a9c58bcfc..b5812b0c2 100644
--- a/win/CMakeLists.txt
+++ b/win/CMakeLists.txt
@@ -2,16 +2,19 @@ cmake_minimum_required(VERSION 2.8)
project(InspIRCd CXX)
-set(CONF_PATH "conf" CACHE PATH "Configuration file path")
-set(MODULE_PATH "modules" CACHE PATH "Module path")
-set(DATA_PATH "data" CACHE PATH "Data path")
-set(LOG_PATH "logs" CACHE PATH "Log file path")
+set(CONFIG_DIR "conf" CACHE PATH "Configuration file path")
+set(MODULE_DIR "modules" CACHE PATH "Module path")
+set(DATA_DIR "data" CACHE PATH "Data path")
+set(LOG_DIR "logs" CACHE PATH "Log file path")
set(EXTRA_INCLUDES "" CACHE PATH "Extra include paths")
set(EXTRA_LIBS "" CACHE PATH "Extra library paths")
set(INSPIRCD_BASE "${CMAKE_CURRENT_SOURCE_DIR}/../")
+set(SYSTEM_NAME_VERSION ${CMAKE_SYSTEM})
+set(SOCKETENGINE "select")
+
# Build with multiple processes
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
@@ -20,10 +23,9 @@ set(CMAKE_MODULE_PATH "${INSPIRCD_BASE}/win")
# Grab version info from version.sh
file(STRINGS "${INSPIRCD_BASE}/src/version.sh" VERSIONSH)
-string(REGEX REPLACE ".*InspIRCd-([0-9]*).*" "\\1" MAJOR_VERSION "${VERSIONSH}")
-string(REGEX REPLACE ".*InspIRCd-[0-9]*\\.([0-9]*).*" "\\1" MINOR_VERSION "${VERSIONSH}")
-string(REGEX REPLACE ".*InspIRCd-[0-9]*\\.[0-9]*\\.([0-9]*).*" "\\1" PATCH_VERSION "${VERSIONSH}")
-set(FULL_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}")
+string(REGEX REPLACE ".*InspIRCd-([0-9]*).*" "\\1" VERSION_MAJOR "${VERSIONSH}")
+string(REGEX REPLACE ".*InspIRCd-[0-9]*\\.([0-9]*).*" "\\1" VERSION_MINOR "${VERSIONSH}")
+string(REGEX REPLACE ".*InspIRCd-[0-9]*\\.[0-9]*\\.([0-9]*).*" "\\1" VERSION_PATCH "${VERSIONSH}")
if(MSVC)
# Without /SAFESEH:NO old libraries compiled with VS 2010 or older won't link correctly to VS2012 (eg, extra module libs)
@@ -59,7 +61,7 @@ if(MSVC)
endif(MSVC)
configure_file("${INSPIRCD_BASE}/win/inspircd.rc.cmake" "${INSPIRCD_BASE}/win/inspircd.rc")
-configure_file("${INSPIRCD_BASE}/win/config.h.cmake" "${INSPIRCD_BASE}/win/config.h")
+configure_file("${INSPIRCD_BASE}/make/template/config.h" "${INSPIRCD_BASE}/include/config.h")
add_executable(inspircd ${INSPIRCD_SOURCES} "${INSPIRCD_BASE}/win/inspircd.rc")
target_link_libraries(inspircd win32_memory)
@@ -76,21 +78,26 @@ install(FILES ${EXTRA_DLLS} DESTINATION .)
file(GLOB_RECURSE EXAMPLE_CONFIGS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIRCD_BASE}/docs/conf/*.example")
install(FILES ${EXAMPLE_CONFIGS} DESTINATION conf)
+# Install nationalchars files
+file(GLOB_RECURSE EXAMPLE_LOCALES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIRCD_BASE}/locales/*")
+install(FILES ${EXAMPLE_LOCALES} DESTINATION locales)
+
# Create an empty data and logs directory and install them
-file(MAKE_DIRECTORY ${DATA_PATH})
-install(DIRECTORY ${DATA_PATH} DESTINATION .)
-file(MAKE_DIRECTORY ${LOG_PATH})
-install(DIRECTORY ${LOG_PATH} DESTINATION .)
+file(MAKE_DIRECTORY ${DATA_DIR})
+install(DIRECTORY ${DATA_DIR} DESTINATION .)
+file(MAKE_DIRECTORY ${LOG_DIR})
+install(DIRECTORY ${LOG_DIR} DESTINATION .)
if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
+ set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ".") # place runtime libraries next to InspIRCd binary
include(InstallRequiredSystemLibraries)
set(CPACK_PACKAGE_NAME "InspIRCd IRC Daemon")
set(CPACK_PACKAGE_VENDOR "InspIRCd Development Team")
- set(CPACK_PACKAGE_VERSION_MAJOR ${MAJOR_VERSION})
- set(CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION})
- set(CPACK_PACKAGE_VERSION_PATCH ${PATCH_VERSION})
- set(CPACK_PACKAGE_FILE_NAME "InspIRCd-${FULL_VERSION}")
+ set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})
+ set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR})
+ set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH})
+ set(CPACK_PACKAGE_FILE_NAME "InspIRCd-${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/../docs/COPYING")
set(CPACK_GENERATOR "NSIS")
@@ -101,7 +108,7 @@ if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
set(CPACK_NSIS_MUI_UNIICON "${INSPIRCD_BASE}/win\\\\inspircd.ico")
set(CPACK_NSIS_INSTALLED_ICON_NAME "inspircd.exe")
set(CPACK_NSIS_URL_INFO_ABOUT "http://www.inspircd.org/")
- set(CPACK_NSIS_COMPRESSOR "/SOLID lzma")
+ set(CPACK_NSIS_COMPRESSOR "/SOLID zlib")
include(CPack)
endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
diff --git a/win/config.h.cmake b/win/config.h.cmake
deleted file mode 100644
index 3a34a706b..000000000
--- a/win/config.h.cmake
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#define BRANCH "@MAJOR_VERSION@.@MINOR_VERSION@"
-#define VERSION "@FULL_VERSION@"
-#define REVISION "release"
-#define SYSTEM "@CMAKE_SYSTEM@"
-#define INSPIRCD_SOCKETENGINE_NAME "select"
-
-#define CONFIG_PATH "@CONF_PATH@"
-#define MOD_PATH "@MODULE_PATH@"
-#define DATA_PATH "@DATA_PATH@"
-#define LOG_PATH "@LOG_PATH@"
-
-#include "inspircd_win32wrapper.h"
-#include "threadengines/threadengine_win32.h"
diff --git a/win/inspircd.rc.cmake b/win/inspircd.rc.cmake
index cd0adc580..06012b3f5 100644
--- a/win/inspircd.rc.cmake
+++ b/win/inspircd.rc.cmake
@@ -1,8 +1,8 @@
101 ICON "inspircd.ico"
1 VERSIONINFO
- FILEVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@
- PRODUCTVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@
+ FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@
+ PRODUCTVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -17,14 +17,14 @@ BEGIN
BEGIN
BLOCK "040904b0"
BEGIN
- VALUE "Comments", "InspIRCd @MAJOR_VERSION@.@MINOR_VERSION@ IRC Daemon"
+ VALUE "Comments", "InspIRCd @VERSION_MAJOR@.@VERSION_MINOR@ IRC Daemon"
VALUE "CompanyName", "InspIRCd Development Team"
VALUE "FileDescription", "InspIRCd"
VALUE "FileVersion", "@FULL_VERSION@"
VALUE "InternalName", "InspIRCd"
- VALUE "LegalCopyright", "Copyright (c) 2013 InspIRCd Development Team"
+ VALUE "LegalCopyright", "Copyright (c) 2015 InspIRCd Development Team"
VALUE "OriginalFilename", "inspircd.exe"
- VALUE "ProductName", "InspIRCd - The Inspire IRC Daemon"
+ VALUE "ProductName", "InspIRCd - Internet Relay Chat Daemon"
VALUE "ProductVersion", "@FULL_VERSION@"
END
END
diff --git a/win/inspircd_win32wrapper.cpp b/win/inspircd_win32wrapper.cpp
index eba18dca3..3e0a264a5 100644
--- a/win/inspircd_win32wrapper.cpp
+++ b/win/inspircd_win32wrapper.cpp
@@ -39,8 +39,8 @@ CoreExport const char *insp_inet_ntop(int af, const void *src, char *dst, sockle
memset(&in, 0, sizeof(in));
in.sin_family = AF_INET;
memcpy(&in.sin_addr, src, sizeof(struct in_addr));
- getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
- return dst;
+ if (getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST) == 0)
+ return dst;
}
else if (af == AF_INET6)
{
@@ -48,8 +48,8 @@ CoreExport const char *insp_inet_ntop(int af, const void *src, char *dst, sockle
memset(&in, 0, sizeof(in));
in.sin6_family = AF_INET6;
memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
- getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
- return dst;
+ if (getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST) == 0)
+ return dst;
}
return NULL;
}
@@ -206,6 +206,11 @@ CWin32Exception::CWin32Exception() : exception()
dwErrorCode = GetLastError();
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);
+ for (size_t i = 0; i < _countof(szErrorString); i++)
+ {
+ if ((szErrorString[i] == '\r') || (szErrorString[i] == '\n'))
+ szErrorString[i] = 0;
+ }
}
CWin32Exception::CWin32Exception(const CWin32Exception& other)
diff --git a/win/inspircd_win32wrapper.h b/win/inspircd_win32wrapper.h
index 9e6724126..d65d4eb92 100644
--- a/win/inspircd_win32wrapper.h
+++ b/win/inspircd_win32wrapper.h
@@ -58,6 +58,7 @@
/* Disable the deprecation warnings.. it spams :P */
#define _CRT_SECURE_NO_DEPRECATE
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
/* Normal windows (platform-specific) includes */
#include <winsock2.h>
@@ -84,6 +85,8 @@
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
+typedef int ssize_t;
+
/* Convert formatted (xxx.xxx.xxx.xxx) string to in_addr struct */
CoreExport int insp_inet_pton(int af, const char * src, void * dst);
@@ -94,9 +97,11 @@ CoreExport const char * insp_inet_ntop(int af, const void * src, char * dst, soc
#define inet_pton insp_inet_pton
#define inet_ntop insp_inet_ntop
-/* Safe printf functions aren't defined in VC++ */
+/* Safe printf functions aren't defined in VC++ releases older than v14 */
+#if _MSC_VER <= 1800
#define snprintf _snprintf
#define vsnprintf _vsnprintf
+#endif
#ifndef va_copy
#define va_copy(dest, src) (dest = src)
@@ -147,11 +152,13 @@ struct DIR
bool first;
};
+#if _MSC_VER <= 1800
struct timespec
{
time_t tv_sec;
long tv_nsec;
};
+#endif
CoreExport DIR * opendir(const char * path);
CoreExport dirent * readdir(DIR * handle);
@@ -197,8 +204,6 @@ CoreExport void closedir(DIR * handle);
void * ::operator new(size_t iSize);
void ::operator delete(void * ptr);
-#define DISABLE_WRITEV
-
#include <exception>
class CWin32Exception : public std::exception
@@ -216,3 +221,27 @@ private:
// Same value as EXIT_STATUS_FORK (EXIT_STATUS_FORK is unused on Windows)
#define EXIT_STATUS_SERVICE 4
+
+// POSIX iovec
+struct iovec
+{
+ void* iov_base; // Starting address
+ size_t iov_len; // Number of bytes to transfer
+};
+
+// Windows WSABUF with POSIX field names
+struct WindowsIOVec
+{
+ // POSIX iovec has iov_base then iov_len, WSABUF in Windows has the fields in reverse order
+ u_long iov_len; // Number of bytes to transfer
+ char FAR* iov_base; // Starting address
+};
+
+inline ssize_t writev(int fd, const WindowsIOVec* iov, int count)
+{
+ DWORD sent;
+ int ret = WSASend(fd, reinterpret_cast<LPWSABUF>(const_cast<WindowsIOVec*>(iov)), count, &sent, 0, NULL, NULL);
+ if (ret == 0)
+ return sent;
+ return -1;
+}
diff --git a/win/make_gnutls_cert.bat b/win/make_gnutls_cert.bat
index 97792cc29..159cd1012 100644
--- a/win/make_gnutls_cert.bat
+++ b/win/make_gnutls_cert.bat
@@ -1,14 +1,14 @@
-@echo off
-
-echo This program will generate SSL certificates for m_ssl_gnutls.so
-echo Ensure certtool.exe is in your system path. It can be downloaded
-echo at ftp://ftp.gnu.org/gnu/gnutls/w32/. If you do not know the answer
-echo to one of the questions just press enter.
-echo.
-
-pause
-
-certtool --generate-privkey --outfile conf/key.pem
-certtool --generate-self-signed --load-privkey conf/key.pem --outfile conf/cert.pem
-
+@echo off
+
+echo This program will generate SSL certificates for m_ssl_gnutls.so
+echo Ensure certtool.exe is in your system path. It can be downloaded
+echo at ftp://ftp.gnu.org/gnu/gnutls/w32/. If you do not know the answer
+echo to one of the questions just press enter.
+echo.
+
+pause
+
+certtool --generate-privkey --outfile conf/key.pem
+certtool --generate-self-signed --load-privkey conf/key.pem --outfile conf/cert.pem
+
pause \ No newline at end of file
diff --git a/win/modules/CMakeLists.txt b/win/modules/CMakeLists.txt
index b3e59de61..2c2617e2b 100644
--- a/win/modules/CMakeLists.txt
+++ b/win/modules/CMakeLists.txt
@@ -38,5 +38,5 @@ foreach(MODULE_NAME ${INSPIRCD_MODULES})
)
# Set the module to be installed to the module directory
- install(TARGETS ${SO_NAME} DESTINATION modules)
+ install(TARGETS ${SO_NAME} DESTINATION ${MODULE_DIR})
endforeach(MODULE_NAME ${INSPIRCD_MODULES})