]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge branch 'insp20' into insp3.
authorPeter Powell <petpow@saberuk.com>
Wed, 1 May 2019 14:25:23 +0000 (15:25 +0100)
committerPeter Powell <petpow@saberuk.com>
Wed, 1 May 2019 23:49:01 +0000 (00:49 +0100)
633 files changed:
.gitattributes [new file with mode: 0644]
.github/ISSUE_TEMPLATE.md [new file with mode: 0644]
.gitignore
.travis.yml
README.md
configure
docs/Doxyfile
docs/conf/aliases/anope.conf.example [deleted file]
docs/conf/aliases/atheme.conf.example [deleted file]
docs/conf/censor.conf.example [deleted file]
docs/conf/filter.conf.example
docs/conf/helpop-full.conf.example [deleted file]
docs/conf/helpop.conf.example
docs/conf/inspircd.conf.example
docs/conf/links.conf.example
docs/conf/modules.conf.example
docs/conf/modules/charybdis.conf.example [deleted file]
docs/conf/modules/unrealircd.conf.example [deleted file]
docs/conf/opers.conf.example
docs/conf/quotes.txt.example
docs/conf/rules.txt.example [deleted file]
docs/conf/services/anope.conf.example [new file with mode: 0644]
docs/conf/services/atheme.conf.example [new file with mode: 0644]
docs/conf/services/generic.conf.example [new file with mode: 0644]
docs/rfc/rfc1035.txt [deleted file]
docs/rfc/rfc1413.txt [deleted file]
docs/rfc/rfc1459.txt [deleted file]
docs/sql/sqloper.mysql.sql [new file with mode: 0644]
docs/sql/sqloper.pgsql.sql [new file with mode: 0644]
docs/sql/sqloper.sqlite3.sql [new file with mode: 0644]
extras/m_sqloper.mssql.sql [deleted file]
extras/m_sqloper.mysql.sql [deleted file]
extras/m_sqloper.postgresql.sql [deleted file]
extras/m_sqloper.sqlite3.sql [deleted file]
include/aligned_storage.h [new file with mode: 0644]
include/bancache.h
include/base.h
include/caller.h [deleted file]
include/channels.h
include/clientprotocol.h [new file with mode: 0644]
include/clientprotocolevent.h [new file with mode: 0644]
include/clientprotocolmsg.h [new file with mode: 0644]
include/command_parse.h
include/commands/cmd_whowas.h
include/compat.h [new file with mode: 0644]
include/configparser.h
include/configreader.h
include/consolecolors.h
include/convto.h [new file with mode: 0644]
include/ctables.h
include/cull_list.h
include/dns.h [deleted file]
include/dynamic.h
include/dynref.h [new file with mode: 0644]
include/event.h [new file with mode: 0644]
include/exitcodes.h
include/extensible.h
include/filelogger.h
include/fileutils.h [new file with mode: 0644]
include/flat_map.h [new file with mode: 0644]
include/hash_map.h [deleted file]
include/hashcomp.h
include/inspircd.h
include/inspsocket.h
include/inspstring.h
include/intrusive_list.h [new file with mode: 0644]
include/intrusive_list_impl.h [new file with mode: 0644]
include/iohook.h [new file with mode: 0644]
include/isupportmanager.h [new file with mode: 0644]
include/listmode.h [new file with mode: 0644]
include/logger.h
include/membership.h
include/message.h [new file with mode: 0644]
include/mode.h
include/modechange.h [new file with mode: 0644]
include/modes/cmode_b.h [deleted file]
include/modes/cmode_k.h [deleted file]
include/modes/cmode_l.h [deleted file]
include/modes/cmode_o.h [deleted file]
include/modes/cmode_v.h [deleted file]
include/modes/simplemodes.h [deleted file]
include/modes/umode_o.h [deleted file]
include/modes/umode_s.h [deleted file]
include/modules.h
include/modules/account.h [new file with mode: 0644]
include/modules/away.h [new file with mode: 0644]
include/modules/callerid.h [new file with mode: 0644]
include/modules/cap.h [new file with mode: 0644]
include/modules/ctctags.h [new file with mode: 0644]
include/modules/dns.h [new file with mode: 0644]
include/modules/exemption.h [new file with mode: 0644]
include/modules/geolocation.h [new file with mode: 0644]
include/modules/hash.h [new file with mode: 0644]
include/modules/httpd.h [new file with mode: 0644]
include/modules/invite.h [new file with mode: 0644]
include/modules/ircv3.h [new file with mode: 0644]
include/modules/ircv3_batch.h [new file with mode: 0644]
include/modules/ircv3_servertime.h [new file with mode: 0644]
include/modules/ldap.h [new file with mode: 0644]
include/modules/names.h [new file with mode: 0644]
include/modules/regex.h [new file with mode: 0644]
include/modules/reload.h [new file with mode: 0644]
include/modules/sasl.h [new file with mode: 0644]
include/modules/server.h [new file with mode: 0644]
include/modules/shun.h [new file with mode: 0644]
include/modules/sql.h [new file with mode: 0644]
include/modules/ssl.h [new file with mode: 0644]
include/modules/stats.h [new file with mode: 0644]
include/modules/webirc.h [new file with mode: 0644]
include/modules/who.h [new file with mode: 0644]
include/modules/whois.h [new file with mode: 0644]
include/numeric.h [new file with mode: 0644]
include/numericbuilder.h [new file with mode: 0644]
include/numerics.h
include/parammode.h [new file with mode: 0644]
include/protocol.h
include/server.h [new file with mode: 0644]
include/snomasks.h
include/socket.h
include/socketengine.h
include/stdalgo.h [new file with mode: 0644]
include/testsuite.h [deleted file]
include/threadengine.h
include/threadengines/threadengine_pthread.h
include/threadengines/threadengine_win32.h
include/timer.h
include/token_list.h [new file with mode: 0644]
include/typedefs.h
include/uid.h
include/usermanager.h
include/users.h
include/xline.h
locales/readme.txt
make/calcdep.pl
make/check_epoll.cpp [deleted file]
make/check_eventfd.cpp [deleted file]
make/check_kqueue.cpp [deleted file]
make/check_stdint.cpp [deleted file]
make/check_strlcpy.cpp [deleted file]
make/common.pm [new file with mode: 0644]
make/configure.pm
make/console.pm [new file with mode: 0644]
make/directive.pm [new file with mode: 0644]
make/gnutlscert.pm [deleted file]
make/opensslcert.pm [deleted file]
make/run-cc.pl [deleted file]
make/template/bsd.mk [new file with mode: 0644]
make/template/config.h [new file with mode: 0644]
make/template/gdbargs [new file with mode: 0644]
make/template/inspircd
make/template/inspircd-genssl.1 [new file with mode: 0644]
make/template/inspircd.1 [new file with mode: 0644]
make/template/inspircd.service [new file with mode: 0644]
make/template/main.mk
make/template/org.inspircd.plist
make/test/arc4random_buf.cpp [new file with mode: 0644]
make/test/clock_gettime.cpp [new file with mode: 0644]
make/test/compiler.cpp [new file with mode: 0644]
make/test/compiler_info.cpp [new file with mode: 0644]
make/test/eventfd.cpp [new file with mode: 0644]
make/test/kqueue.cpp [new file with mode: 0644]
make/unit-cc.pl
make/utilities.pm [deleted file]
modulemanager
src/bancache.cpp
src/base.cpp
src/channels.cpp
src/cidr.cpp
src/clientprotocol.cpp [new file with mode: 0644]
src/command_parse.cpp
src/commands.cpp
src/commands/cmd_admin.cpp [deleted file]
src/commands/cmd_away.cpp [deleted file]
src/commands/cmd_clearcache.cpp [deleted file]
src/commands/cmd_commands.cpp [deleted file]
src/commands/cmd_connect.cpp [deleted file]
src/commands/cmd_die.cpp [deleted file]
src/commands/cmd_eline.cpp [deleted file]
src/commands/cmd_gline.cpp [deleted file]
src/commands/cmd_info.cpp [deleted file]
src/commands/cmd_invite.cpp [deleted file]
src/commands/cmd_ison.cpp [deleted file]
src/commands/cmd_join.cpp [deleted file]
src/commands/cmd_kick.cpp [deleted file]
src/commands/cmd_kill.cpp [deleted file]
src/commands/cmd_kline.cpp [deleted file]
src/commands/cmd_links.cpp [deleted file]
src/commands/cmd_list.cpp [deleted file]
src/commands/cmd_loadmodule.cpp [deleted file]
src/commands/cmd_lusers.cpp [deleted file]
src/commands/cmd_map.cpp [deleted file]
src/commands/cmd_mode.cpp [deleted file]
src/commands/cmd_modenotice.cpp [deleted file]
src/commands/cmd_modules.cpp [deleted file]
src/commands/cmd_motd.cpp [deleted file]
src/commands/cmd_names.cpp [deleted file]
src/commands/cmd_nick.cpp [deleted file]
src/commands/cmd_notice.cpp [deleted file]
src/commands/cmd_oper.cpp [deleted file]
src/commands/cmd_part.cpp [deleted file]
src/commands/cmd_pass.cpp [deleted file]
src/commands/cmd_ping.cpp [deleted file]
src/commands/cmd_pong.cpp [deleted file]
src/commands/cmd_privmsg.cpp [deleted file]
src/commands/cmd_qline.cpp [deleted file]
src/commands/cmd_quit.cpp [deleted file]
src/commands/cmd_rehash.cpp [deleted file]
src/commands/cmd_reloadmodule.cpp [deleted file]
src/commands/cmd_restart.cpp [deleted file]
src/commands/cmd_rules.cpp [deleted file]
src/commands/cmd_server.cpp [deleted file]
src/commands/cmd_squit.cpp [deleted file]
src/commands/cmd_stats.cpp [deleted file]
src/commands/cmd_time.cpp [deleted file]
src/commands/cmd_topic.cpp [deleted file]
src/commands/cmd_unloadmodule.cpp [deleted file]
src/commands/cmd_user.cpp [deleted file]
src/commands/cmd_userhost.cpp [deleted file]
src/commands/cmd_version.cpp [deleted file]
src/commands/cmd_wallops.cpp [deleted file]
src/commands/cmd_who.cpp [deleted file]
src/commands/cmd_whois.cpp [deleted file]
src/commands/cmd_whowas.cpp [deleted file]
src/commands/cmd_zline.cpp [deleted file]
src/configparser.cpp
src/configreader.cpp
src/coremods/core_channel/cmd_invite.cpp [new file with mode: 0644]
src/coremods/core_channel/cmd_join.cpp [new file with mode: 0644]
src/coremods/core_channel/cmd_kick.cpp [new file with mode: 0644]
src/coremods/core_channel/cmd_names.cpp [new file with mode: 0644]
src/coremods/core_channel/cmd_topic.cpp [new file with mode: 0644]
src/coremods/core_channel/cmode_k.cpp [new file with mode: 0644]
src/coremods/core_channel/cmode_l.cpp [new file with mode: 0644]
src/coremods/core_channel/core_channel.cpp [new file with mode: 0644]
src/coremods/core_channel/core_channel.h [new file with mode: 0644]
src/coremods/core_channel/invite.cpp [new file with mode: 0644]
src/coremods/core_channel/invite.h [new file with mode: 0644]
src/coremods/core_dns.cpp [new file with mode: 0644]
src/coremods/core_hostname_lookup.cpp [new file with mode: 0644]
src/coremods/core_info/cmd_admin.cpp [new file with mode: 0644]
src/coremods/core_info/cmd_commands.cpp [new file with mode: 0644]
src/coremods/core_info/cmd_info.cpp [new file with mode: 0644]
src/coremods/core_info/cmd_modules.cpp [new file with mode: 0644]
src/coremods/core_info/cmd_motd.cpp [new file with mode: 0644]
src/coremods/core_info/cmd_time.cpp [new file with mode: 0644]
src/coremods/core_info/cmd_version.cpp [new file with mode: 0644]
src/coremods/core_info/core_info.cpp [new file with mode: 0644]
src/coremods/core_info/core_info.h [new file with mode: 0644]
src/coremods/core_list.cpp [new file with mode: 0644]
src/coremods/core_loadmodule.cpp [new file with mode: 0644]
src/coremods/core_lusers.cpp [new file with mode: 0644]
src/coremods/core_message.cpp [new file with mode: 0644]
src/coremods/core_mode.cpp [new file with mode: 0644]
src/coremods/core_oper/cmd_die.cpp [new file with mode: 0644]
src/coremods/core_oper/cmd_kill.cpp [new file with mode: 0644]
src/coremods/core_oper/cmd_oper.cpp [new file with mode: 0644]
src/coremods/core_oper/cmd_rehash.cpp [new file with mode: 0644]
src/coremods/core_oper/cmd_restart.cpp [new file with mode: 0644]
src/coremods/core_oper/core_oper.cpp [new file with mode: 0644]
src/coremods/core_oper/core_oper.h [new file with mode: 0644]
src/coremods/core_reloadmodule.cpp [new file with mode: 0644]
src/coremods/core_serialize_rfc.cpp [new file with mode: 0644]
src/coremods/core_stats.cpp [new file with mode: 0644]
src/coremods/core_stub.cpp [new file with mode: 0644]
src/coremods/core_user/cmd_away.cpp [new file with mode: 0644]
src/coremods/core_user/cmd_ison.cpp [new file with mode: 0644]
src/coremods/core_user/cmd_nick.cpp [new file with mode: 0644]
src/coremods/core_user/cmd_part.cpp [new file with mode: 0644]
src/coremods/core_user/cmd_quit.cpp [new file with mode: 0644]
src/coremods/core_user/cmd_user.cpp [new file with mode: 0644]
src/coremods/core_user/cmd_userhost.cpp [new file with mode: 0644]
src/coremods/core_user/core_user.cpp [new file with mode: 0644]
src/coremods/core_user/core_user.h [new file with mode: 0644]
src/coremods/core_user/umode_o.cpp [new file with mode: 0644]
src/coremods/core_user/umode_s.cpp [new file with mode: 0644]
src/coremods/core_wallops.cpp [new file with mode: 0644]
src/coremods/core_who.cpp [new file with mode: 0644]
src/coremods/core_whois.cpp [new file with mode: 0644]
src/coremods/core_whowas.cpp [new file with mode: 0644]
src/coremods/core_xline/cmd_eline.cpp [new file with mode: 0644]
src/coremods/core_xline/cmd_gline.cpp [new file with mode: 0644]
src/coremods/core_xline/cmd_kline.cpp [new file with mode: 0644]
src/coremods/core_xline/cmd_qline.cpp [new file with mode: 0644]
src/coremods/core_xline/cmd_zline.cpp [new file with mode: 0644]
src/coremods/core_xline/core_xline.cpp [new file with mode: 0644]
src/coremods/core_xline/core_xline.h [new file with mode: 0644]
src/cull_list.cpp
src/dns.cpp [deleted file]
src/dynamic.cpp
src/filelogger.cpp
src/fileutils.cpp [new file with mode: 0644]
src/hashcomp.cpp
src/helperfuncs.cpp
src/inspircd.cpp
src/inspsocket.cpp
src/inspstring.cpp
src/listensocket.cpp
src/listmode.cpp [new file with mode: 0644]
src/logger.cpp
src/mode.cpp
src/modes/cmode_b.cpp [deleted file]
src/modes/cmode_k.cpp [deleted file]
src/modes/cmode_l.cpp [deleted file]
src/modes/cmode_o.cpp [deleted file]
src/modes/cmode_v.cpp [deleted file]
src/modes/umode_o.cpp [deleted file]
src/modes/umode_s.cpp [deleted file]
src/modmanager_dynamic.cpp [deleted file]
src/modmanager_static.cpp [deleted file]
src/modulemanager.cpp [new file with mode: 0644]
src/modules.cpp
src/modules/account.h [deleted file]
src/modules/extra/README
src/modules/extra/m_geo_maxmind.cpp [new file with mode: 0644]
src/modules/extra/m_geoip.cpp [deleted file]
src/modules/extra/m_ldap.cpp [new file with mode: 0644]
src/modules/extra/m_ldapauth.cpp [deleted file]
src/modules/extra/m_ldapoper.cpp [deleted file]
src/modules/extra/m_mssql.cpp [deleted file]
src/modules/extra/m_mysql.cpp
src/modules/extra/m_pgsql.cpp
src/modules/extra/m_regex_pcre.cpp
src/modules/extra/m_regex_posix.cpp
src/modules/extra/m_regex_re2.cpp [new file with mode: 0644]
src/modules/extra/m_regex_stdlib.cpp
src/modules/extra/m_regex_tre.cpp
src/modules/extra/m_sqlite3.cpp
src/modules/extra/m_ssl_gnutls.cpp
src/modules/extra/m_ssl_mbedtls.cpp [new file with mode: 0644]
src/modules/extra/m_ssl_openssl.cpp
src/modules/extra/m_sslrehashsignal.cpp [new file with mode: 0644]
src/modules/hash.h [deleted file]
src/modules/httpd.h [deleted file]
src/modules/m_abbreviation.cpp
src/modules/m_alias.cpp
src/modules/m_allowinvite.cpp
src/modules/m_alltime.cpp
src/modules/m_anticaps.cpp [new file with mode: 0644]
src/modules/m_auditorium.cpp
src/modules/m_autoop.cpp
src/modules/m_banexception.cpp
src/modules/m_banredirect.cpp
src/modules/m_bcrypt.cpp [new file with mode: 0644]
src/modules/m_blockamsg.cpp
src/modules/m_blockcaps.cpp
src/modules/m_blockcolor.cpp
src/modules/m_botmode.cpp
src/modules/m_callerid.cpp
src/modules/m_cap.cpp
src/modules/m_cap.h [deleted file]
src/modules/m_cban.cpp
src/modules/m_censor.cpp
src/modules/m_cgiirc.cpp
src/modules/m_chancreate.cpp
src/modules/m_chanfilter.cpp
src/modules/m_chanhistory.cpp
src/modules/m_chanlog.cpp
src/modules/m_channames.cpp
src/modules/m_channelban.cpp
src/modules/m_chanprotect.cpp [deleted file]
src/modules/m_check.cpp
src/modules/m_chghost.cpp
src/modules/m_chgident.cpp
src/modules/m_chgname.cpp
src/modules/m_classban.cpp [new file with mode: 0644]
src/modules/m_clearchan.cpp [new file with mode: 0644]
src/modules/m_cloaking.cpp
src/modules/m_clones.cpp
src/modules/m_close.cpp [deleted file]
src/modules/m_commonchans.cpp
src/modules/m_conn_join.cpp
src/modules/m_conn_umodes.cpp
src/modules/m_conn_waitpong.cpp
src/modules/m_connectban.cpp
src/modules/m_connflood.cpp
src/modules/m_customprefix.cpp
src/modules/m_customtitle.cpp
src/modules/m_cycle.cpp
src/modules/m_dccallow.cpp
src/modules/m_deaf.cpp
src/modules/m_delayjoin.cpp
src/modules/m_delaymsg.cpp
src/modules/m_denychans.cpp
src/modules/m_devoice.cpp [deleted file]
src/modules/m_disable.cpp [new file with mode: 0644]
src/modules/m_dnsbl.cpp
src/modules/m_exemptchanops.cpp
src/modules/m_filter.cpp
src/modules/m_flashpolicyd.cpp [new file with mode: 0644]
src/modules/m_gecosban.cpp
src/modules/m_geoban.cpp [new file with mode: 0644]
src/modules/m_geoclass.cpp [new file with mode: 0644]
src/modules/m_globalload.cpp
src/modules/m_globops.cpp
src/modules/m_halfop.cpp [deleted file]
src/modules/m_haproxy.cpp [new file with mode: 0644]
src/modules/m_helpop.cpp
src/modules/m_hidechans.cpp
src/modules/m_hidelist.cpp [new file with mode: 0644]
src/modules/m_hidemode.cpp [new file with mode: 0644]
src/modules/m_hideoper.cpp
src/modules/m_hostchange.cpp
src/modules/m_hostcycle.cpp [new file with mode: 0644]
src/modules/m_httpd.cpp
src/modules/m_httpd_acl.cpp
src/modules/m_httpd_config.cpp
src/modules/m_httpd_stats.cpp
src/modules/m_ident.cpp
src/modules/m_inviteexception.cpp
src/modules/m_ircv3.cpp
src/modules/m_ircv3_accounttag.cpp [new file with mode: 0644]
src/modules/m_ircv3_batch.cpp [new file with mode: 0644]
src/modules/m_ircv3_capnotify.cpp [new file with mode: 0644]
src/modules/m_ircv3_chghost.cpp [new file with mode: 0644]
src/modules/m_ircv3_ctctags.cpp [new file with mode: 0644]
src/modules/m_ircv3_echomessage.cpp [new file with mode: 0644]
src/modules/m_ircv3_invitenotify.cpp [new file with mode: 0644]
src/modules/m_ircv3_servertime.cpp [new file with mode: 0644]
src/modules/m_ircv3_sts.cpp [new file with mode: 0644]
src/modules/m_joinflood.cpp
src/modules/m_jumpserver.cpp [deleted file]
src/modules/m_kicknorejoin.cpp
src/modules/m_knock.cpp
src/modules/m_ldapauth.cpp [new file with mode: 0644]
src/modules/m_ldapoper.cpp [new file with mode: 0644]
src/modules/m_lockserv.cpp
src/modules/m_maphide.cpp
src/modules/m_md5.cpp
src/modules/m_messageflood.cpp
src/modules/m_mlock.cpp
src/modules/m_modenotice.cpp [new file with mode: 0644]
src/modules/m_monitor.cpp [new file with mode: 0644]
src/modules/m_muteban.cpp
src/modules/m_namedmodes.cpp
src/modules/m_namesx.cpp
src/modules/m_nationalchars.cpp
src/modules/m_nickflood.cpp
src/modules/m_nicklock.cpp
src/modules/m_noctcp.cpp
src/modules/m_nokicks.cpp
src/modules/m_nonicks.cpp
src/modules/m_nonotice.cpp
src/modules/m_nopartmsg.cpp
src/modules/m_ojoin.cpp
src/modules/m_operchans.cpp
src/modules/m_operjoin.cpp
src/modules/m_operlevels.cpp
src/modules/m_operlog.cpp
src/modules/m_opermodes.cpp
src/modules/m_opermotd.cpp
src/modules/m_operprefix.cpp
src/modules/m_override.cpp
src/modules/m_passforward.cpp
src/modules/m_password_hash.cpp
src/modules/m_pbkdf2.cpp [new file with mode: 0644]
src/modules/m_permchannels.cpp
src/modules/m_randquote.cpp
src/modules/m_redirect.cpp
src/modules/m_regex.h [deleted file]
src/modules/m_regex_glob.cpp
src/modules/m_regonlycreate.cpp [deleted file]
src/modules/m_remove.cpp
src/modules/m_repeat.cpp [new file with mode: 0644]
src/modules/m_restrictchans.cpp
src/modules/m_restrictmsg.cpp
src/modules/m_ripemd160.cpp [deleted file]
src/modules/m_rline.cpp
src/modules/m_rmode.cpp [new file with mode: 0644]
src/modules/m_sajoin.cpp
src/modules/m_sakick.cpp
src/modules/m_samode.cpp
src/modules/m_sanick.cpp
src/modules/m_sapart.cpp
src/modules/m_saquit.cpp
src/modules/m_sasl.cpp
src/modules/m_satopic.cpp
src/modules/m_securelist.cpp
src/modules/m_seenicks.cpp
src/modules/m_serverban.cpp
src/modules/m_services_account.cpp
src/modules/m_servprotect.cpp
src/modules/m_sethost.cpp
src/modules/m_setident.cpp
src/modules/m_setidle.cpp
src/modules/m_setname.cpp
src/modules/m_sha1.cpp [new file with mode: 0644]
src/modules/m_sha256.cpp
src/modules/m_showfile.cpp [new file with mode: 0644]
src/modules/m_showwhois.cpp
src/modules/m_shun.cpp
src/modules/m_silence.cpp
src/modules/m_spanningtree/addline.cpp
src/modules/m_spanningtree/away.cpp
src/modules/m_spanningtree/cachetimer.cpp [deleted file]
src/modules/m_spanningtree/cachetimer.h
src/modules/m_spanningtree/capab.cpp
src/modules/m_spanningtree/commandbuilder.h [new file with mode: 0644]
src/modules/m_spanningtree/commands.h
src/modules/m_spanningtree/compat.cpp
src/modules/m_spanningtree/delline.cpp
src/modules/m_spanningtree/encap.cpp
src/modules/m_spanningtree/fjoin.cpp
src/modules/m_spanningtree/fmode.cpp
src/modules/m_spanningtree/ftopic.cpp
src/modules/m_spanningtree/hmac.cpp
src/modules/m_spanningtree/idle.cpp
src/modules/m_spanningtree/ijoin.cpp [new file with mode: 0644]
src/modules/m_spanningtree/link.h
src/modules/m_spanningtree/main.cpp
src/modules/m_spanningtree/main.h
src/modules/m_spanningtree/metadata.cpp
src/modules/m_spanningtree/misccommands.cpp [new file with mode: 0644]
src/modules/m_spanningtree/netburst.cpp
src/modules/m_spanningtree/nick.cpp [new file with mode: 0644]
src/modules/m_spanningtree/nickcollide.cpp
src/modules/m_spanningtree/num.cpp [new file with mode: 0644]
src/modules/m_spanningtree/operquit.cpp [deleted file]
src/modules/m_spanningtree/opertype.cpp
src/modules/m_spanningtree/override_map.cpp
src/modules/m_spanningtree/override_squit.cpp
src/modules/m_spanningtree/override_stats.cpp
src/modules/m_spanningtree/override_whois.cpp
src/modules/m_spanningtree/ping.cpp
src/modules/m_spanningtree/pingtimer.cpp [new file with mode: 0644]
src/modules/m_spanningtree/pingtimer.h [new file with mode: 0644]
src/modules/m_spanningtree/pong.cpp
src/modules/m_spanningtree/postcommand.cpp
src/modules/m_spanningtree/precommand.cpp
src/modules/m_spanningtree/protocolinterface.cpp
src/modules/m_spanningtree/protocolinterface.h
src/modules/m_spanningtree/push.cpp [deleted file]
src/modules/m_spanningtree/rconnect.cpp
src/modules/m_spanningtree/remoteuser.cpp [new file with mode: 0644]
src/modules/m_spanningtree/remoteuser.h [new file with mode: 0644]
src/modules/m_spanningtree/resolvers.cpp
src/modules/m_spanningtree/resolvers.h
src/modules/m_spanningtree/rsquit.cpp
src/modules/m_spanningtree/save.cpp
src/modules/m_spanningtree/server.cpp
src/modules/m_spanningtree/servercommand.cpp [new file with mode: 0644]
src/modules/m_spanningtree/servercommand.h [new file with mode: 0644]
src/modules/m_spanningtree/sinfo.cpp [new file with mode: 0644]
src/modules/m_spanningtree/svsjoin.cpp
src/modules/m_spanningtree/svsnick.cpp
src/modules/m_spanningtree/svspart.cpp
src/modules/m_spanningtree/translate.cpp [new file with mode: 0644]
src/modules/m_spanningtree/translate.h [new file with mode: 0644]
src/modules/m_spanningtree/treeserver.cpp
src/modules/m_spanningtree/treeserver.h
src/modules/m_spanningtree/treesocket.h
src/modules/m_spanningtree/treesocket1.cpp
src/modules/m_spanningtree/treesocket2.cpp
src/modules/m_spanningtree/uid.cpp
src/modules/m_spanningtree/utils.cpp
src/modules/m_spanningtree/utils.h
src/modules/m_spanningtree/version.cpp [deleted file]
src/modules/m_sqlauth.cpp
src/modules/m_sqloper.cpp
src/modules/m_sslinfo.cpp
src/modules/m_sslmodes.cpp
src/modules/m_starttls.cpp [new file with mode: 0644]
src/modules/m_stripcolor.cpp
src/modules/m_svshold.cpp
src/modules/m_swhois.cpp
src/modules/m_testnet.cpp [deleted file]
src/modules/m_timedbans.cpp
src/modules/m_tline.cpp
src/modules/m_topiclock.cpp
src/modules/m_uhnames.cpp
src/modules/m_uninvite.cpp
src/modules/m_userip.cpp
src/modules/m_vhost.cpp
src/modules/m_watch.cpp
src/modules/m_websocket.cpp [new file with mode: 0644]
src/modules/m_xline_db.cpp
src/modules/sasl.h [deleted file]
src/modules/spanningtree.h [deleted file]
src/modules/sql.h [deleted file]
src/modules/ssl.h [deleted file]
src/modules/u_listmode.h [deleted file]
src/server.cpp
src/snomasks.cpp
src/socket.cpp
src/socketengine.cpp
src/socketengines/socketengine_epoll.cpp
src/socketengines/socketengine_kqueue.cpp
src/socketengines/socketengine_poll.cpp
src/socketengines/socketengine_ports.cpp [deleted file]
src/socketengines/socketengine_select.cpp
src/testsuite.cpp [deleted file]
src/threadengine.cpp
src/threadengines/threadengine_pthread.cpp
src/threadengines/threadengine_win32.cpp
src/timer.cpp
src/user_resolver.cpp [deleted file]
src/usermanager.cpp
src/userprocess.cpp [deleted file]
src/users.cpp
src/version.sh
src/whois.cpp [deleted file]
src/wildcard.cpp
src/xline.cpp
tools/create_templates.pl [deleted file]
tools/gdbargs [deleted file]
tools/genssl [new file with mode: 0755]
tools/test-build [new file with mode: 0755]
tools/travis-ci.sh
vendor/README.md [new file with mode: 0644]
vendor/bcrypt/crypt_blowfish.c [new file with mode: 0644]
vendor/bcrypt/crypt_blowfish.h [new file with mode: 0644]
vendor/http_parser/http_parser.c [new file with mode: 0644]
vendor/http_parser/http_parser.h [new file with mode: 0644]
vendor/sha2/sha2.c [new file with mode: 0644]
vendor/sha2/sha2.h [new file with mode: 0644]
vendor/utfcpp/utf8.h [new file with mode: 0644]
vendor/utfcpp/utf8/checked.h [new file with mode: 0644]
vendor/utfcpp/utf8/core.h [new file with mode: 0644]
vendor/utfcpp/utf8/cpp11.h [new file with mode: 0644]
vendor/utfcpp/utf8/unchecked.h [new file with mode: 0644]
win/.gitignore
win/CMakeLists.txt
win/NSIS.template.in
win/README.txt
win/inspircd.rc.cmake
win/inspircd_config.h.cmake [deleted file]
win/inspircd_memory_functions.cpp
win/inspircd_version.h.cmake [deleted file]
win/inspircd_win32wrapper.cpp
win/inspircd_win32wrapper.h
win/make_gnutls_cert.bat
win/modules/CMakeLists.txt
win/win32service.cpp
win/win32service.h

diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..ec1c66d
--- /dev/null
@@ -0,0 +1 @@
+*.bat  text eol=crlf
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644 (file)
index 0000000..ea6121d
--- /dev/null
@@ -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`:**
+
index f39aa4a55c73c92d57ef9e017ec358a30ad9b4d7..0bf719c7c9c735bb7615015949c8e3a1ad67d698 100644 (file)
@@ -2,31 +2,32 @@
 *.pem
 *.swp
 
-/.config.cache
-/.modulemanager
-/BSDmakefile
+.*
+!.git*
+
+/.configure
+/Makefile
 /GNUmakefile
 /build
 /docs/doxygen
-/inspircd
-/org.inspircd.plist
 /run
-/bin
 
-/include/inspircd_config.h
-/include/inspircd_version.h
+/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_geo_maxmind.cpp
+/src/modules/m_ldap.cpp
 /src/modules/m_mysql.cpp
 /src/modules/m_pgsql.cpp
 /src/modules/m_regex_pcre.cpp
 /src/modules/m_regex_posix.cpp
+/src/modules/m_regex_re2.cpp
 /src/modules/m_regex_stdlib.cpp
 /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
+/src/modules/m_sslrehashsignal.cpp
 
+/win/Win32
+/win/*.dir
index bb82add9b681014f029101c3e75a69201a3d26d2..c8740e2677a9b8424422ce3c25a75daf41fd9e02 100644 (file)
@@ -1,10 +1,10 @@
 compiler:
   - clang
   - gcc
-dist: trusty
+dist: xenial
 env:
-  - PURE_STATIC=1
-  -
+    - CXXFLAGS=-std=gnu++98
+    - CXXFLAGS=-std=c++14
 language: cpp
 notifications:
   email: false
index e6b2d7fa099bedd94d38228a000851b43a87af7e..4733e0fdeadf3b3589e5518d76e77ef8bf7f3271 100644 (file)
--- a/README.md
+++ b/README.md
@@ -6,13 +6,13 @@ InspIRCd is a modular C++ Internet Relay Chat (IRC) server for UNIX-like and Win
 
 InspIRCd is supported on on the following platforms:
 
-- Most recent BSD variants using the Clang or GCC compilers and the BSD or GNU toolchains (Make, etc).
+- Most recent BSD variants using the Clang or GCC compilers and the GNU toolchains (Make, etc).
 
 - Most recent Linux distributions using the Clang or GCC compilers and the GNU toolchain.
 
-- The most recent three major releases of macOS using the AppleClang, Clang, or GCC (*not* LLVM-GCC) compilers and the BSD or GNU toolchains.
+- The most recent three major releases of macOS using the AppleClang, Clang, or GCC (*not* LLVM-GCC) compilers and the GNU toolchains.
 
-- Windows 7 or newer using the MSVC 11 (Visual Studio 2012) compiler and CMake 2.8 or newer.
+- Windows 7 or newer using the MSVC 14 (Visual Studio 2015) compiler and CMake 2.8 or newer.
 
 Alternate platforms and toolchains may also work but are not officially supported by the InspIRCd team. Generally speaking if you are using a reasonably modern UNIX-like system you should be able to build InspIRCd on it.
 
@@ -20,13 +20,17 @@ If you encounter any bugs then [please file an issue](https://github.com/inspirc
 
 ## Installation
 
-Most InspIRCd users running a UNIX-like system build from source. A guide about how to do this is available on [the InspIRCd docs site](http://docs.inspircd.org/2/installation/source).
+Most InspIRCd users running a UNIX-like system build from source. A guide about how to do this is available on [the InspIRCd docs site](https://docs.inspircd.org/3/installation/source).
 
-Building from source on Windows is generally not recommended but [a guide is available](https://github.com/inspircd/inspircd/blob/insp20/win/README.txt) if you wish to do this.
+Building from source on Windows is generally not recommended but [a guide is available](https://github.com/inspircd/inspircd/blob/master/win/README.txt) if you wish to do this.
+
+<!--
+TODO: uncomment this once we have binary packages for v3.
 
 If you are running on CentOS 7, Debian 7, or Windows binary packages are available from [the downloads page](https://github.com/inspircd/inspircd/releases/latest).
 
 A [Docker](https://www.docker.com) image is also available. See [the inspircd-docker repository](https://github.com/inspircd/inspircd-docker) for more information.
+-->
 
 Some distributions ship an InspIRCd package in their package managers. We generally do not recommend the use of such packages as in the past distributions have made broken modifications to InspIRCd and not kept their packages up to date with essential security updates.
 
index 638d48da0036b170785c10d68aecb4e7ab0fe858..a9d02ffc449c793bb0c7f1e958f1c580c368d7c2 100755 (executable)
--- 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>
 
 
 BEGIN {
-       require 5.8.0;
-       push @INC, '.';
+       require 5.10.0;
 }
 
+use feature ':5.10';
 use strict;
 use warnings FATAL => qw(all);
 
-use File::Copy ();
-use Socket;
-use Cwd;
-use Getopt::Long;
+use File::Basename        qw(basename);
+use File::Copy            ();
+use File::Spec::Functions qw(rel2abs);
+use FindBin               qw($RealDir);
+use Getopt::Long          qw(GetOptions);
+use POSIX                 qw(getgid getuid);
 
-# Utility functions for our buildsystem
-use make::utilities;
+use lib $RealDir;
+use make::common;
 use make::configure;
-use make::gnutlscert;
-use make::opensslcert;
-
-###############################################################################################
-#
-#                                 NON-EDITABLE VARIABLES
-#
-###############################################################################################
-
-our ($opt_use_gnutls, $opt_rebuild, $opt_use_openssl, $opt_nointeractive, $opt_ports,
-    $opt_epoll, $opt_kqueue, $opt_noports, $opt_noepoll, $opt_nokqueue,
-    $opt_noipv6, $opt_maxbuf, $opt_disable_debug, $opt_freebsd_port,
-       $opt_system, $opt_uid);
-
-our ($opt_cc, $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_script_dir,
+    $opt_socketengine,
+    $opt_system,
+    $opt_uid);
 
 sub list_extras ();
 
@@ -67,38 +73,31 @@ sub disable_extras (@);
 my @opt_enableextras;
 my @opt_disableextras;
 
-GetOptions (
-       'enable-gnutls' => \$opt_use_gnutls,
-       'rebuild' => \$opt_rebuild,
-       'system' => \$opt_system,
-       'uid=s' => \$opt_uid,
-       'enable-openssl' => \$opt_use_openssl,
-       'disable-interactive' => \$opt_nointeractive,
-       'enable-ports' => \$opt_ports,
-       'enable-epoll' => \$opt_epoll,
-       'enable-kqueue' => \$opt_kqueue,
-       'disable-ports' => \$opt_noports,
-       'disable-epoll' => \$opt_noepoll,
-       'disable-kqueue' => \$opt_nokqueue,
-       'disable-ipv6' => \$opt_noipv6,
-       'with-cc=s' => \$opt_cc,
-       'with-maxbuf=i' => \$opt_maxbuf,
-       'enable-freebsd-ports-openssl' => \$opt_freebsd_port,
-       '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,
-       'disable-debuginfo' => sub { $opt_disable_debug = 1 },
-       'help'  => sub { showhelp(); },
-       'update' => sub { update(); },
-       'clean' => sub { 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, # ^
-       'generate-openssl-cert' => sub { make_openssl_cert(); exit(0); },
-       'generate-gnutls-cert' => sub { make_gnutls_cert(); exit(0); }
+GetOptions(
+       'clean'  => \&cmd_clean,
+       'help'   => \&cmd_help,
+       'update' => \&cmd_update,
+
+       '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,
+       'script-dir=s'         => \$opt_script_dir,
+       '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) {
@@ -112,959 +111,295 @@ 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_cc) ||
-       (defined $opt_noipv6) ||
-       (defined $opt_kqueue) ||
-       (defined $opt_epoll) ||
-       (defined $opt_ports) ||
-       (defined $opt_use_openssl) ||
-       (defined $opt_nokqueue) ||
-       (defined $opt_noepoll) ||
-       (defined $opt_noports) ||
-       (defined $opt_maxbuf) ||
-       (defined $opt_system) ||
-       (defined $opt_uid) ||
-       (defined $opt_use_gnutls) ||
-       (defined $opt_freebsd_port)
+       !-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_script_dir ||
+       defined $opt_socketengine ||
+       defined $opt_system ||
+       defined $opt_uid
 );
 
-chomp(our $topdir = getcwd());
-our $this = resolve_directory($topdir);                                                # PWD, Regardless.
-our @modlist = ();                                                                     # Declare for Module List..
-our %config = ();                                                                      # Initiate Configuration Hash..
-our $cache_loaded = getcache();
-$config{ME} = resolve_directory($topdir);                              # Present Working Directory
-
-$config{BASE_DIR} ||= $config{ME}."/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} = $opt_uid || 'ircd';
-       $config{CONFIG_DIR}      = '/etc/inspircd';
-       $config{MODULE_DIR}      = '/usr/lib/inspircd';
-       $config{BINARY_DIR}      = '/usr/sbin/';
-       $config{BUILD_DIR}       = resolve_directory($config{ME}."/build");         # Build Directory
-       $config{DATA_DIR}        = '/var/inspircd';
-       $config{LOG_DIR}         = '/var/log/inspircd';
-} else {
-       $config{UID} = $opt_uid || $config{UID} || $<;
-       $config{CONFIG_DIR}      ||= resolve_directory($config{BASE_DIR}."/conf");      # Configuration Directory
-       $config{MODULE_DIR}      ||= resolve_directory($config{BASE_DIR}."/modules");   # Modules Directory
-       $config{BINARY_DIR}      ||= resolve_directory($config{BASE_DIR}."/bin");               # Binary Directory
-       $config{BUILD_DIR}       ||= resolve_directory($config{ME}."/build");         # Build Directory
-       $config{DATA_DIR}        ||= resolve_directory($config{BASE_DIR}."/data");      # Data directory
-       $config{LOG_DIR}         ||= resolve_directory($config{BASE_DIR}."/logs");      # Log directory
-}
-
-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;
-}
-chomp($config{HAS_GNUTLS}   = `pkg-config --modversion gnutls 2>/dev/null`); # GNUTLS Version.
+my %version = get_version $opt_distribution_label;
+print_format "<|BOLD Configuring InspIRCd $version{FULL} on $^O.|>\n";
 
-if (defined $opt_freebsd_port)
-{
-       chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
-       chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);
-       $config{USE_FREEBSD_BASE_SSL} = "n";
-}
-else
-{
-       if ($^O eq "freebsd")
-       {
-               # default: use base ssl
-               chomp($config{HAS_OPENSSL} = `openssl version | cut -d ' ' -f 2`);                      # OpenSSL version, freebsd specific
-               chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);      # Port version, may be different
+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);
        }
-       else
-       {
-               chomp($config{HAS_OPENSSL}  = `pkg-config --modversion openssl 2>/dev/null`);           # Openssl version, others
-               $config{HAS_OPENSSL_PORT} = "";
-               $config{USE_FREEBSD_BASE_SSL} = "n";
-       }
-}
-
-chomp(our $gnutls_ver = $config{HAS_GNUTLS});
-chomp(our $openssl_ver = $config{HAS_OPENSSL});
-$config{USE_GNUTLS}        ||= "n";
-if (defined $opt_use_gnutls)
-{
-       $config{USE_GNUTLS} = "y";                                      # Use gnutls.
-}
-$config{USE_OPENSSL}   ||= "n";                                                # Use openssl.
-if (defined $opt_use_openssl)
-{
-       $config{USE_OPENSSL} = "y";
-}
-
-if (!defined $opt_disable_debug) {
-       $config{OPTIMISATI}      = "-g1";                               # Optimisation Flag
-} else {
-       $config{OPTIMISATI}      = "-O2";
-}
-
-$config{HAS_STRLCPY}   = "false";                                      # strlcpy Check.
-$config{HAS_STDINT}     = "false";                                     # stdint.h check
-$config{USE_KQUEUE}     = "y";                                         # kqueue enabled
-if (defined $opt_nokqueue) {
-       $config{USE_KQUEUE} = "n";
-}
-$config{USE_POLL}     = "y";                                   # poll enabled
-$config{USE_EPOLL}       = "y";                                        # epoll enabled
-if (defined $opt_noepoll)
-{
-       $config{USE_EPOLL} = "n";
-}
-$config{USE_PORTS}       = "y";                                        # epoll enabled
-if (defined $opt_noports)
-{
-       $config{USE_PORTS} = "n";
-}
-$config{_SOMAXCONN} = SOMAXCONN;                                       # Max connections in accept queue
-$config{OSNAME}            = $^O;                                      # Operating System Name
-$config{IS_DARWIN}       = "NO";                                       # Is OSX?
-$config{STARTSCRIPT}     = "inspircd";                 # start script?
-$config{DESTINATION}     = "BASE";                             # Is target path.
-if ($config{OSNAME} =~ /darwin/i)
-{
-       $config{IS_DARWIN} = "YES";
-       $config{STARTSCRIPT}      = "org.inspircd.plist";               # start script for OSX.
-       $config{CC}                 = "xcrun clang++";                                  # C++ compiler for OSX.
-}
-elsif ($config{OSNAME} =~ /freebsd/i)
-{
-       chomp(my $fbsd_version = `uname -r`);
-       $fbsd_version =~ s/^(\d+\.\d+).*/$1/g;
-       $config{CC} = $fbsd_version >= 10.0 ? 'clang++' : 'g++';
-}
-else
-{
-       $config{CC}                 = "g++";                                            # C++ compiler
-}
-if (defined $opt_cc)
-{
-       $config{CC} = $opt_cc;
-}
-`$config{CC} -dumpversion` =~ /^(\d+)(?:\.(\d+))?/;
-$config{GCCVER} = defined $1 ? $1 : '';
-$config{GCCMINOR} = defined $2 ? $2 : '0';
-$config{MAXBUF}                        = "512";                                # Max buffer size
-
-if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)(?:[a-z])?(?:\-[a-z][0-9])?/) {
-       $config{HAS_OPENSSL} = $1;
-} else {
-       $config{HAS_OPENSSL} = "";
-}
-
-if (($config{GCCVER} eq "") || ($config{GCCMINOR} eq "")) {
-       print "`$config{CC}` was not found! A C++ compiler is required to build InspIRCd!\n";
-       print "You can pass a custom compiler to $0 using --with-cc=[name].\n";
-       exit;
 }
 
-# Get and Set some important vars..
-getmodules();
-
-sub clean
-{
-       unlink(".config.cache");
-}
-
-our ($has_epoll, $has_ports, $has_kqueue) = (0, 0, 0);
-
-sub update
-{
-       eval {
-               chomp($topdir = getcwd());
-               $this = resolve_directory($topdir);                                          # PWD, Regardless.
-               getmodules();
-               # Does the cache file exist?
-               if (!getcache()) {
-                       # No, No it doesn't.. *BASH*
-                       print "You have not run ./configure before. Please do this before trying to run the update script.\n";
-                       exit 0;
-               } else {
-                       # We've Loaded the cache file and all our variables..
-                       print "Updating files...\n";
-                       if (defined($opt_disable_debug) && $opt_disable_debug == 1)
-                       {
-                               print "Disabling debug information (-g).\n";
-                               $config{OPTIMISATI} = "";
-                       }
-                       $has_epoll = $config{HAS_EPOLL};
-                       $has_ports = $config{HAS_PORTS};
-                       $has_kqueue = $config{HAS_KQUEUE};
-                       writefiles(1);
-                       makecache();
-                       print "Complete.\n";
-                       exit;
-               }
-       };
-       if ($@)
-       {
-               print "Configure update failed: $@\n";
+$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;
        }
-       exit;
-}
-
-
-sub test_compile {
-       my $feature = shift;
-       my $fail = 0;
-       $fail ||= system "$config{CC} -o test_$feature make/check_$feature.cpp >/dev/null 2>&1";
-       $fail ||= system "./test_$feature";
-       unlink "test_$feature";
-       return !$fail;
 }
+my %compiler = get_compiler_info($config{CXX});
 
-print "Running non-interactive configure...\n" unless $interactive;
-print "Checking for cache from previous configure... ";
-print ($cache_loaded ? "found\n" : "not found\n");
-$config{SYSTEM} = lc $^O;
-print "Checking operating system version... $config{SYSTEM}\n";
-
-`$config{CC} -dumpversion` =~ /^(\d+)(?:\.(\d+))?/;
-$config{GCCVER} = defined $1 ? $1 : '';
-$config{GCCMINOR} = defined $2 ? $2 : '0';
+$config{HAS_ARC4RANDOM_BUF} = run_test 'arc4random_buf()', test_file($config{CXX}, 'arc4random_buf.cpp');
+$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');
 
-printf "Checking if stdint.h exists... ";
-$config{HAS_STDINT} = test_compile('stdint');
-print $config{HAS_STDINT} ? "yes\n" : "no\n";
+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, 'poll'   if run_test 'poll', test_header $config{CXX}, 'poll.h';
+push @socketengines, 'select';
 
-printf "Checking if strlcpy exists... ";
-$config{HAS_STRLCPY} = test_compile('strlcpy');
-print $config{HAS_STRLCPY} ? "yes\n" : "no\n";
-
-printf "Checking if kqueue exists... ";
-$has_kqueue = test_compile('kqueue');
-print $has_kqueue ? "yes\n" : "no\n";
-
-printf "Checking for epoll support... ";
-$has_epoll = test_compile('epoll');
-print $has_epoll ? "yes\n" : "no\n";
-
-printf "Checking for eventfd support... ";
-$config{HAS_EVENTFD} = test_compile('eventfd');
-print $config{HAS_EVENTFD} ? "yes\n" : "no\n";
-
-printf "Checking if Solaris I/O completion ports are available... ";
-$has_ports = 0;
-our $system = `uname -s`;
-chomp ($system);
-$has_ports = 1 if ($system eq "SunOS");
-
-if ($has_ports) {
-       my $kernel = `uname -r`;
-       chomp($kernel);
-       if (($kernel !~ /^5\.1./)) {
-               $has_ports = 0;
+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;
        }
 }
-print "yes\n" if $has_ports == 1;
-print "no\n" if $has_ports == 0;
-
-$config{HAS_EPOLL} = $has_epoll;
-$config{HAS_KQUEUE} = $has_kqueue;
+$config{SOCKETENGINE} = $opt_socketengine // $socketengines[0];
 
-printf "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";
-       }
+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';
+       $config{SCRIPT_DIR} = $opt_script_dir // '/usr/share/inspircd'
 } else {
-       print "no\n";
-       $config{HAS_GNUTLS} = "n";
-}
-
-printf "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";
-       }
+       $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';
+       $config{SCRIPT_DIR} = $opt_script_dir // $config{SCRIPT_DIR} // $config{BASE_DIR};
+}
+
+# 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 {
-       print "no\n";
-       $config{HAS_OPENSSL} = "n";
-}
-
-printf "Checking if you are running an ancient, unsupported OS... ";
-if ($config{OSNAME} =~ /FreeBSD/i)
-{
-       my $version = `uname -r`;
-       if ($version =~ /^4\./)
-       {
-               print "yes.\n";
-               print "FreeBSD 4.x is no longer supported. By ANYONE.\n";
-               print "To build, you will need to add the following to CXXFLAGS:\n";
-               print "\t-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1\n";
-       }
-       else
-       {
-               print "no ($version)\n";
-       }
-}
-else
-{
-       print "no ($config{OSNAME})\n";
-}
-
-################################################################################
-#                        BEGIN INTERACTIVE PART                              #
-################################################################################
-
-# Clear the Screen..
-if ($interactive)
-{
-       print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..)
-       my $wholeos = $^O;
-
-       my $rev = getrevision();
-       # 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$config{OSNAME}\e[0m ($wholeos)
-Your InspIRCd revision ID is \e[1;32mr$rev\e[0m
-STOP
-       if ($rev eq "r0") {
-               print " (Non-SVN build)";
-       }
-       print ".\n\n";
-
-       $config{CHANGE_COMPILER} = "n";
-       print "I have detected the following compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
-
-       while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
-               print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and
-should NOT be used. You should probably specify a newer compiler.\n\n";
-               yesno('CHANGE_COMPILER',"Do you want to change the compiler?");
-               if ($config{CHANGE_COMPILER} =~ /y/i) {
-                       print "What command do you want to use to invoke your compiler?\n";
-                       print "[\e[1;32m$config{CC}\e[0m] -> ";
-                       chomp($config{CC} = <STDIN>);
-                       if ($config{CC} eq "") {
-                               $config{CC} = "g++";
-                       }
-                       chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`);
-                       if ($foo ne "") {
-                               `$config{CC} -dumpversion` =~ /^(\d+)(?:\.(\d+))?/;
-                               $config{GCCVER} = defined $1 ? $1 : '';
-                               $config{GCCMINOR} = defined $2 ? $2 : '0';
-                               print "Queried compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
-                               if ($config{GCCVER} < 3) {
-                                       print "\e[1;32mGCC 2.x WILL NOT WORK!\e[0m. Let's try that again, shall we?\n";
-                               }
-                       }
-                       else {
-                               print "\e[1;32mWARNING!\e[0m Could not execute the compiler you specified. You may want to try again.\n";
-                       }
-               }
-       }
-
-       print "\n";
-
-       # Directory Settings..
-       my $tmpbase = $config{BASE_DIR};
-       dir_check("do you wish to install the InspIRCd base", "BASE_DIR");
-       if ($tmpbase ne $config{BASE_DIR}) {
-               $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");           # Configuration Dir
-               $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
-               $config{DATA_DIR}        = resolve_directory($config{BASE_DIR}."/data");        # Data Directory
-               $config{LOG_DIR}         = resolve_directory($config{BASE_DIR}."/logs");        # Log Directory
-               $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");     # Binary Directory
-       }
-
-       dir_check("are the configuration files", "CONFIG_DIR");
-       dir_check("are the modules to be compiled to", "MODULE_DIR");
-       dir_check("is the IRCd binary to be placed", "BINARY_DIR");
-       dir_check("are variable data files to be located in", "DATA_DIR");
-       dir_check("are the logs to be stored in", "LOG_DIR");
-       dir_check("do you want the build to take place", "BUILD_DIR");
-               
-       my $chose_hiperf = 0;
-       if ($has_kqueue) {
-               yesno('USE_KQUEUE',"You are running a BSD operating system, and kqueue\nwas detected. Would you like to enable kqueue support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable kqueue?");
-               print "\n";
-               if ($config{USE_KQUEUE} eq "y") {
-                       $chose_hiperf = 1;
-               }
-       }
-       if ($has_epoll) {
-               yesno('USE_EPOLL',"You are running a Linux 2.6+ operating system, and epoll\nwas detected. Would you like to enable epoll support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable epoll?");
-               print "\n";
-               if ($config{USE_EPOLL} eq "y") {
-                       $chose_hiperf = 1;
-               }
-       }
-       if ($has_ports) {
-               yesno('USE_PORTS',"You are running Solaris 10.\nWould you like to enable I/O completion ports support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable support for I/O completion ports?");
-               print "\n";
-               if ($config{USE_PORTS} eq "y") {
-                       $chose_hiperf = 1;
-               }
-       }
-
-       if (!$chose_hiperf) {
-               yesno('USE_POLL', "Would you like to use poll?\n This is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable poll?");
-               if ($config{USE_POLL} ne "y")
-               {
-                       print "No high-performance socket engines are available, or you chose\n";
-                       print "not to enable one. Defaulting to select() engine.\n\n";
-               }
-       }
-
-       $config{USE_FREEBSD_BASE_SSL} = "n";
-       $config{USE_FREEBSD_PORTS_SSL} = "n";
-       if ($config{HAS_OPENSSL_PORT} ne "")
-       {
-               $config{USE_FREEBSD_PORTS_SSL} = "y";
-               print "I have detected the OpenSSL FreeBSD port installed on your system,\n";
-               print "version \e[1;32m".$config{HAS_OPENSSL_PORT}."\e[0m. Your base system OpenSSL is version \e[1;32m".$openssl_ver."\e[0m.\n\n";
-               yesno('USE_FREEBSD_PORTS_SSL', "Do you want to use the FreeBSD ports version?");
-               print "\n";
-               $config{USE_FREEBSD_BASE_SSL} = "y" if ($config{USE_FREEBSD_PORTS_SSL} eq "n");
-
-               if ($config{USE_FREEBSD_BASE_SSL} eq "n")
-               {
-                       # update to port version
-                       $openssl_ver = $config{HAS_OPENSSL_PORT};
+       @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;
+       unless ($group[2]) {
+               print_warning <<"EOW";
+You are building as the privileged $group[0] group and have not specified
+an unprivileged group to run InspIRCd as.
+
+This is almost never what you should do. You should probably either create a new
+unprivileged user/group to build and run as or pass the '--gid [id|name]' flag
+to specify an unprivileged group to run as.
+EOW
+               if (!prompt_bool $interactive, "Are you sure you want to build as the $group[0] group?", 0) {
+                       say STDERR "If you are sure you want to build as the $group[0] group pass the --gid $group[2] flag." unless $interactive;
+                       exit 1;
+               }
+       }
+}
+$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;
+       unless ($user[2]) {
+               print_warning <<"EOW";
+You are building as the privileged $user[0] user and have not specified
+an unprivileged user to run InspIRCd as.
+
+This is almost never what you should do. You should probably either create a new
+unprivileged user/group to build and run as or pass the '--uid [id|name]' flag
+to specify an unprivileged user to run as.
+EOW
+               if (!prompt_bool $interactive, "Are you sure you want to build as the $user[0] user?", 0) {
+                       say STDERR "If you are sure you want to build as the $user[0] user pass the --uid $user[2] flag." unless $interactive;
+                       exit 1;
+               }
+       }
+}
+$config{USER} = $user[0];
+$config{UID}  = $user[2];
+
+# 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 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
+}
+
+# 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.
+
+You can obtain the latest stable version from https://www.inspircd.org or by
+running `<|GREEN git checkout $(git describe --abbrev=0 --tags insp3)|>` 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;
+       }
+}
+
+# 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}
+<|BOLD Script:|> $config{SCRIPT_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 SCRIPT_DIR)) {
+               $config{$key} =~ s/^\Q$original_base_dir\E/$config{BASE_DIR}/;
+       }
+       $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};
+       $config{SCRIPT_DIR} = prompt_dir $interactive, 'In what directory are scripts to be placed?',             $config{SCRIPT_DIR};
+}
+
+# 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
-       {
-               $config{USE_FREEBSD_BASE_SSL} = "y" if ($^O eq "freebsd");
-       }
-
-       $config{USE_SSL} ||= "n";
-       $config{MODUPDATE} ||= 'n';
-
-       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";
-
-               yesno('USE_SSL', "One or more SSL libraries detected. Would you like to enable SSL support?");
-               if ($config{USE_SSL} eq "y")
-               {
-                       if ($config{HAS_GNUTLS} eq "y")
-                       {
-                               yesno('USE_GNUTLS',"Would you like to enable SSL with m_ssl_gnutls? (recommended)");
-                               if ($config{USE_GNUTLS} eq "y")
-                               {
-                                       print "\nUsing GnuTLS SSL module.\n";
-                               }
-                       }
-
-                       if ($config{HAS_OPENSSL} eq "y")
-                       {
-                               yesno('USE_OPENSSL', "Would you like to enable SSL with m_ssl_openssl?");
-                               if ($config{USE_OPENSSL} eq "y")
-                               {
-                                       print "\nUsing OpenSSL SSL module.\nYou will get better performance if you move to GnuTLS in the future.\n";
-                               }
-                       }
+} else {
+       # TODO: finish modulemanager rewrite and replace this code with:
+       # system './modulemanager', 'enable', '--auto';
+       my %modules = (
+               # Missing: m_ldap, m_regex_stdlib, m_ssl_mbedtls
+               'm_geo_maxmind.cpp'     => 'pkg-config --exists libmaxminddb',
+               'm_mysql.cpp'           => 'mysql_config --version',
+               'm_pgsql.cpp'           => 'pg_config --version',
+               'm_regex_pcre.cpp'      => 'pcre-config --version',
+               'm_regex_posix.cpp'     => undef,
+               'm_regex_re2.cpp'       => 'pkg-config --exists re2',
+               'm_regex_tre.cpp'       => 'pkg-config --exists tre',
+               'm_sqlite3.cpp'         => 'pkg-config --exists sqlite3',
+               'm_ssl_gnutls.cpp'      => 'pkg-config --exists gnutls',
+               'm_ssl_openssl.cpp'     => 'pkg-config --exists openssl',
+               'm_sslrehashsignal.cpp' => undef,
+       );
+       while (my ($module, $command) = each %modules) {
+               unless (defined $command && system "$command 1>/dev/null 2>/dev/null") {
+                       enable_extras $module;
                }
        }
-       else
-       {
-               print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed and\n";
-               print "is in your path.\n\n";
-       }
-
-       yesno('MODUPDATE',"Would you like to check for updates to third-party modules?");
-       print "\n";
-       if ($config{MODUPDATE} eq "y") {
-               print "Checking for upgrades to extra and third-party modules... ";
-               system "./modulemanager upgrade";
-       }
 }
 
-# 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";
+# Generate SSL certificates.
+$question = <<EOQ;
+Would you like to generate a self-signed SSL certificate now? This certificate
+can be used for testing but <|BOLD should not|> be used on a production network.
 
-dumphash();
+Note: you can get a <|BOLD free|> CA-signed certificate from Let's Encrypt. See
+https://letsencrypt.org/getting-started/ for more details.
+EOQ
 
-if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y"))
-{
-       print "Sorry, but I couldn't detect GnuTLS. Make sure pkg-config is in your path.\n";
-       exit(0);
-}
-if (($config{USE_OPENSSL} eq "y") && ($config{HAS_OPENSSL} ne "y"))
-{
-       print "Sorry, but I couldn't detect OpenSSL. Make sure pkg-config and openssl are in your path.\n";
-       exit(0);
+if (<src/modules/m_ssl_*.cpp> && prompt_bool $interactive, $question, $interactive) {
+       system './tools/genssl', 'auto';
 }
-our $failed = 0;
 
-$config{CERTGEN} ||= 'y';
-yesno('CERTGEN',"Would you like to generate SSL certificates now?") if ($interactive && ($config{USE_GNUTLS} eq "y" || $config{USE_OPENSSL} eq "y"));
+# Cache the distribution label so that its not lost when --update is run.
+$config{DISTRIBUTION} = $opt_distribution_label if $opt_distribution_label;
 
-if ($config{USE_GNUTLS} eq "y") {
-       unless (-r "src/modules/m_ssl_gnutls.cpp") {
-               print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n";
-               symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!";
-       }
-       if ($interactive && $config{CERTGEN} eq 'y')
-       {
-               unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
-                       print "SSL certificates not found, generating.. \n\n
-*************************************************************
-* Generating the private key may take some time, once done, *
-* answer the questions which follow. If you are unsure,     *
-* just hit enter!                                           *
-*************************************************************\n\n";
-                       $failed = make_gnutls_cert();
-                       if ($failed) {
-                               print "\n\e[1;32mCertificate generation failed!\e[0m\n\n";
-                       } else {
-                               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";
-                               print "Done.\n\n";
-                       }
-               }
-               else {
-                       print "SSL certificates found, skipping.\n\n";
-               }
-       }
-       else
-       {
-               print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
-       }
-}
+write_configure_cache %config;
+parse_templates \%config, \%compiler, \%version;
 
-if ($config{USE_OPENSSL} eq "y") {
-       unless (-r "src/modules/m_ssl_openssl.cpp") {
-               print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n";
-               symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!";
-       }
-       $failed = 0;
-       if ($interactive && $config{CERTGEN} eq 'y')
-       {
-               unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
-                       print "SSL certificates not found, generating.. \n\n
-*************************************************************
-* Generating the certificates may take some time, go grab a *
-* coffee or something.                                     *
-*************************************************************\n\n";
-                       make_openssl_cert();
-                       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\nin non-interactive mode.\n\n";
-       }
-}
-if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) {
-       print "Skipping SSL certificate generation as SSL support is not available.\n\n";
-}
-
-depcheck();
-writefiles(1);
-makecache();
-
-print "\n\n";
-print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
-if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) {
-       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://docs.inspircd.org/2/installation/source\e[0m\n";
-}
-print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n\n";
-if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) {
-       print "\e[1;32mWARNING!\e[0m You are running OpenBSD but you are using the base gcc package\nrather than eg++. This compile will most likely fail, but I'm letting you\ngo ahead with it anyway, just in case I'm wrong :-)\n";
-}
+print_format <<"EOM";
 
-if ($config{GCCVER} < "3") {
-       print <<FOO2;
-\e[1;32mWARNING!\e[0m You are attempting to compile InspIRCd on GCC 2.x!
-GCC 2.x series compilers only had partial (read as broken) C++ support, and
-your compile will most likely fail horribly! If you have any problems, do NOT
-report them to the bugtracker or forums without first upgrading your compiler
-to a newer 3.x or 4.x (or whatever is available currently) version.
-FOO2
-}
+Configuration is complete! You have chosen to build with the following settings:
 
-if ($^O eq 'openbsd') {
-       print <<__OPENBSD_WARNING__;
-\e[1;32mWARNING!\e[0m OpenBSD 6.5 changed Make to no longer look for BSDmakefile when
-searching for the makefile. If the version of OpenBSD you are using is 6.5 or
-newer then you will need to run '\e[1;32mmake -f BSDmakefile\e[0m' or '\e[1;32mgmake\e[0m' instead.
-__OPENBSD_WARNING__
-}
+<|GREEN Compiler:|>
+  <|GREEN Binary:|>  $config{CXX}
+  <|GREEN Name:|>    $compiler{NAME}
+  <|GREEN Version:|> $compiler{VERSION}
 
-################################################################################
-#                            HELPER FUNCTIONS                          #
-################################################################################
-sub getcache {
-       # Retrieves the .config.cache file, and loads values into the main config hash.
-       open(CACHE, ".config.cache") or return 0;
-       while (<CACHE>) {
-               chomp;
-               # Ignore Blank lines, and comments..
-               next if /^\s*$/;
-               next if /^\s*#/;
-               my ($key, $value) = split("=", $_, 2);
-               $value =~ /^\"(.*)\"$/;
-               # Do something with data here!
-               $config{$key} = $1;
-       }
-       close(CACHE);
-       return 1;
-}
+<|GREEN Extra Modules:|>
+EOM
 
-sub makecache {
-       # Dump the contents of %config
-       print "Writing \e[1;32mcache file\e[0m for future ./configures ...\n";
-       open(FILEHANDLE, ">.config.cache");
-       foreach my $key (keys %config) {
-               print FILEHANDLE "$key=\"$config{$key}\"\n";
-       }
-       close(FILEHANDLE);
-}
-
-sub dir_check {
-       my ($desc, $hash_key) = @_;
-       my $complete = 0;
-       while (!$complete) {
-               print "In what directory $desc?\n";
-               print "[\e[1;32m$config{$hash_key}\e[0m] -> ";
-               chomp(my $var = <STDIN>);
-               if ($var eq "") {
-                       $var = $config{$hash_key};
-               }
-               if ($var =~ /^\~\/(.+)$/) {
-                       # Convert it to a full path..
-                       $var = resolve_directory($ENV{HOME} . "/" . $1);
-               }
-               elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/"))
-               {
-                       # Assume relative Path was given.. fill in the rest.
-                       $var = $this . "/$var";
-               }
-
-               $var = resolve_directory($var);
-               if (! -e $var) {
-                       print "$var does not exist. Create it?\n[\e[1;32my\e[0m] ";
-                       chomp(my $tmp = <STDIN>);
-                       if (($tmp eq "") || ($tmp =~ /^y/i)) {
-                               # Attempt to Create the Dir..
-                               my $chk = eval {
-                                       use File::Path ();
-                                       File::Path::mkpath($var, 0, 0777);
-                                       1;
-                               };
-                               unless (defined($chk) && -d $var) {
-                                       print "Unable to create directory. ($var)\n\n";
-                                       # Restart Loop..
-                                       next;
-                               }
-                       } else {
-                               # They said they don't want to create, and we can't install there.
-                               print "\n\n";
-                               next;
-                       }
-               } else {
-                       if (!is_dir($var)) {
-                               # Target exists, but is not a directory.
-                               print "File $var exists, but is not a directory.\n\n";
-                               next;
-                       }
-               }
-               # Either Dir Exists, or was created fine.
-               $config{$hash_key} = $var;
-               $complete = 1;
-               print "\n";
-       }
+for my $file (<src/modules/m_*>) {
+       my $module = basename $file, '.cpp';
+       say "  * $module" if -l $file;
 }
 
-our $SHARED = "";
+print_format <<"EOM";
 
-my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0);
+<|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}
+  <|GREEN Script:|> $config{SCRIPT_DIR}
 
-sub writefiles {
-       my($writeheader) = @_;
-       # First File.. inspircd_config.h
-       chomp(my $incos = `uname -n -s -r`);
-       chomp(my $version = `sh src/version.sh`);
-       chomp(my $revision2 = getrevision());
-       my $branch = "InspIRCd-0.0";
-       if ($version =~ /^(InspIRCd-[0-9]+\.[0-9]+)\.[0-9]+/)
-       {
-               $branch = $1;
-       }
-       if ($writeheader == 1)
-       {
-               print "Writing \e[1;32minspircd_config.h\e[0m\n";
-               open(FILEHANDLE, ">include/inspircd_config.h.tmp");
-               print FILEHANDLE <<EOF;
-/* Auto generated by configure, do not modify! */
-#ifndef INSPIRCD_CONFIG_H
-#define INSPIRCD_CONFIG_H
-
-/* this is for windows support. */
-#define CoreExport /**/
-#define DllExport /**/
-
-#define CONFIG_PATH "$config{CONFIG_DIR}"
-#define DATA_PATH "$config{DATA_DIR}"
-#define LOG_PATH "$config{LOG_DIR}"
-#define MOD_PATH "$config{MODULE_DIR}"
-#define SOMAXCONN_S "$config{_SOMAXCONN}"
-#define ENTRYPOINT int main(int argc, char** argv)
-
-EOF
-print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n";
-
-               if ($config{OSNAME} =~ /SunOS/i) {
-                       print FILEHANDLE "#define IS_SOLARIS\n";
-               }
-               if ($config{OSNAME} =~ /MINGW32/i) {
-                       print FILEHANDLE "#define IS_MINGW\n";
-               }
-               if ($config{GCCVER} >= 3) {
-                       print FILEHANDLE "#define GCC3\n";
-               }
-               if ($config{HAS_STRLCPY}) {
-                       print FILEHANDLE "#define HAS_STRLCPY\n";
-               }
-               if ($config{HAS_STDINT}) {
-                       print FILEHANDLE "#define HAS_STDINT\n";
-               }
-               if ($config{HAS_EVENTFD}) {
-                       print FILEHANDLE "#define HAS_EVENTFD\n";
-               }
-               if ($config{OSNAME} !~ /DARWIN/i) {
-                       print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
-               } else {
-                       print FILEHANDLE "#ifdef MAC_OS_X_VERSION_10_12\n";
-                       print FILEHANDLE "# define HAS_CLOCK_GETTIME\n";
-                       print FILEHANDLE "#endif\n";
-               }
-               my $use_hiperf = 0;
-               if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
-                       print FILEHANDLE "#define USE_KQUEUE\n";
-                       $config{SOCKETENGINE} = "socketengine_kqueue";
-                       $use_hiperf = 1;
-               }
-               if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
-                       print FILEHANDLE "#define USE_EPOLL\n";
-                       $config{SOCKETENGINE} = "socketengine_epoll";
-                       $use_hiperf = 1;
-               }
-               if (($has_ports) && ($config{USE_PORTS} eq "y")) {
-                       print FILEHANDLE "#define USE_PORTS\n";
-                       $config{SOCKETENGINE} = "socketengine_ports";
-                       $use_hiperf = 1;
-               }
-               # user didn't choose either epoll or select for their OS.
-               # default them to USE_SELECT (ewwy puke puke)
-               if (!$use_hiperf) {
-                       print "no hi-perf, " . $config{USE_POLL};
-                       if ($config{USE_POLL} eq "y")
-                       {
-                               print FILEHANDLE "#define USE_POLL\n";
-                               $config{SOCKETENGINE} = "socketengine_poll";
-                       }
-                       else
-                       {
-                               print FILEHANDLE "#define USE_SELECT\n";
-                               $config{SOCKETENGINE} = "socketengine_select";
-                       }
-               }
-               print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n";
-               close(FILEHANDLE);
-
-               open(FILEHANDLE, ">include/inspircd_version.h.tmp");
-               print FILEHANDLE <<EOF;
-#define BRANCH "$branch"
-#define VERSION "$version"
-#define REVISION "$revision2"
-#define SYSTEM "$incos"
-EOF
-               close FILEHANDLE;
-
-               for my $file (qw(include/inspircd_config.h include/inspircd_version.h)) {
-                       my $diff = 0;
-                       open my $fh1, $file or $diff = 1;
-                       open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
-                       while (!$diff) {
-                               my $line1 = <$fh1>;
-                               my $line2 = <$fh2>;
-                               if (defined($line1) != defined($line2)) {
-                                       $diff = 1;
-                               } elsif (!defined $line1) {
-                                       last;
-                               } else {
-                                       $diff = ($line1 ne $line2);
-                               }
-                       }
-                       if ($diff) {
-                               unlink $file;
-                               rename "$file.tmp", $file;
-                       } else {
-                               unlink "$file.tmp";
-                       }
-               }
-       }
+<|GREEN Execution Group:|> $config{GROUP} ($config{GID})
+<|GREEN Execution User:|>  $config{USER} ($config{UID})
+<|GREEN Socket Engine:|>   $config{SOCKETENGINE}
 
-       # Write all .in files.
-       my $tmp = "";
-       my $file = "";
-       my $exe = "inspircd";
+To build with these settings run '<|GREEN make -j${\get_cpu_count} install|>' now.
 
-       # Do this once here, and cache it in the .*.inc files,
-       # rather than attempting to read src/version.sh from
-       # compiled code -- we might not have the source to hand.
-       # Fix for bug#177 by Brain.
-
-       chomp($version = `sh ./src/version.sh`);
-       chomp(my $revision = getrevision());
-       $version = "$version(r$revision)";
-
-       # We can actually parse any file starting with . and ending with .inc,
-       # but right now we only parse .inspircd.inc to form './inspircd'
-       prepare_dynamic_makefile();
-
-       my @dotfiles = qw(main.mk inspircd);
-       push @dotfiles, 'org.inspircd.plist' if $config{OSNAME} eq 'darwin';
-
-       # HACK: we need to know if we are on GCC6 to disable the omission of `this` null pointer checks.
-       $config{GCC6} = `$config{CC} --version 2>/dev/null` =~ /gcc/i && $config{GCCVER} ge "6" ? "true" : "false";
-
-       foreach my $file (@dotfiles) {
-               open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
-               $_ = join '', <FILEHANDLE>;
-               close(FILEHANDLE);
-
-               $config{BUILD_DIR} ||= resolve_directory($config{ME}."/build");
-
-               for my $var (qw(
-                       CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
-                       STARTSCRIPT DESTINATION SOCKETENGINE LOG_DIR GCC6
-               )) {
-                       s/\@$var\@/$config{$var}/g;
-               }
-               s/\@EXECUTABLE\@/$exe/ if defined $exe;
-               s/\@VERSION\@/$version/ if defined $version;
-
-               if ($file eq 'main.mk') {
-                       print "Writing \e[1;32mGNUmakefile\e[0m ...\n";
-
-                       my $mk_tmp = $_;
-                       s/\@IFDEF (\S+)/ifdef $1/g;
-                       s/\@IFNDEF (\S+)/ifndef $1/g;
-                       s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
-                       s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
-                       s/\@ELSE/else/g;
-                       s/\@ENDIF/endif/g;
-                       s/ *\@BSD_ONLY .*\n//g;
-                       s/\@GNU_ONLY //g;
-                       s/\@DO_EXPORT (.*)/export $1/g;
-                       open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
-                       print MKF $_;
-                       close MKF;
-
-                       print "Writing \e[1;32mBSDmakefile\e[0m ...\n";
-                       $_ = $mk_tmp;
-                       s/\@IFDEF (\S+)/.if defined($1)/g;
-                       s/\@IFNDEF (\S+)/.if !defined($1)/g;
-                       s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
-                       s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
-                       s/\@ELSE/.else/g;
-                       s/\@ENDIF/.endif/g;
-                       s/\@BSD_ONLY //g;
-                       s/ *\@GNU_ONLY .*\n//g;
-                       $mk_tmp = $_;
-                       $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
-                       open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
-                       print MKF $mk_tmp;
-                       close MKF;
-               } else {
-                       print "Writing \e[1;32m$file\e[0m ...\n";
-                       open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
-                       print FILEHANDLE $_;
-                       close(FILEHANDLE);
-               }
-       }
-
-       chmod 0755, 'inspircd';
-}
-
-sub depcheck
-{
-       getmodules();
-       for my $mod (@modlist) {
-               getcompilerflags("src/modules/m_$mod.cpp");
-               getlinkerflags("src/modules/m_$mod.cpp");
-       }
-}
-
-sub prepare_dynamic_makefile
-{
-       my $i = 0;
-
-       if (!$has_epoll)
-       {
-               $config{USE_EPOLL} = 0;
-       }
-       if (!$has_kqueue)
-       {
-               $config{USE_KQUEUE} = 0;
-       }
-       if (!$has_ports)
-       {
-               $config{USE_PORTS} = 0;
-       }
-}
+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
@@ -1129,7 +464,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 / +/, getdependencies($abs_extra);
+               my @deps = split /\s+/, get_directive($abs_extra, 'ModDep', '');
                for my $dep (@deps) {
                        if (exists($extras{$dep})) {
                                my $ref = \$extras{$dep}; # Take reference.
@@ -1176,10 +511,10 @@ sub enable_extras (@) {
                        next;
                }
                # Get dependencies, and add them to be processed.
-               my @deps = split / +/, getdependencies($extrapath);
+               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") {
+                       if (!-e "src/modules/$dep" && !-e "include/$dep") {
                                if (-e "src/modules/extra/$dep") {
                                        print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
                                        push @extras, $dep;
@@ -1212,7 +547,7 @@ EXTRA:     for my $extra (@extras) {
                }
                # Check if anything needs this.
                for my $file (@files) {
-                       my @deps = split / +/, getdependencies("src/modules/extra/$file");
+                       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.
index 807020af51104b127c467f4c9a6de06447fe209c..3089a5170538585f38792fe6a796a40b8fe87603 100644 (file)
@@ -1,6 +1,6 @@
 DOXYFILE_ENCODING      = UTF-8
 PROJECT_NAME           = InspIRCd
-PROJECT_NUMBER         = 2.0
+PROJECT_NUMBER         = 3.0
 PROJECT_BRIEF          =
 PROJECT_LOGO           =
 OUTPUT_DIRECTORY       = docs/doxygen
@@ -93,9 +93,9 @@ EXCLUDE                =
 EXCLUDE_SYMLINKS       = YES
 EXCLUDE_PATTERNS       = */.git/* \
                          */doxygen/* \
-                         */commands/* \
-                         */modes/* \
-                         */modules/*
+                         */coremods/* \
+                         */modules/* \
+                         */vendor/*
 EXCLUDE_SYMBOLS        =
 EXAMPLE_PATH           =
 EXAMPLE_PATTERNS       =
@@ -219,7 +219,8 @@ EXPAND_ONLY_PREDEF     = NO
 SEARCH_INCLUDES        = YES
 INCLUDE_PATH           =
 INCLUDE_FILE_PATTERNS  =
-PREDEFINED             =
+PREDEFINED             = CoreExport=/**/ \
+                         INSPIRCD_INTRUSIVE_LIST_NAME=intrusive_list
 EXPAND_AS_DEFINED      =
 SKIP_FUNCTION_MACROS   = YES
 TAGFILES               =
diff --git a/docs/conf/aliases/anope.conf.example b/docs/conf/aliases/anope.conf.example
deleted file mode 100644 (file)
index 4d14414..0000000
+++ /dev/null
@@ -1,20 +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">
-
-# 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 [account] <password>
-# Identify for a nickname
-<alias text="ID" format="*" replace="PRIVMSG NickServ :IDENTIFY $2-" requires="NickServ" 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 (file)
index 7a0bc01..0000000
+++ /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/censor.conf.example b/docs/conf/censor.conf.example
deleted file mode 100644 (file)
index ea9e081..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# Configuration file for m_censor.so
-
-# The tags for this module are formatted as follows:
-#
-# <badword text="simple word"
-#          replace="text to replace with">
-#
-# You can specify <badword text="simple word" replace="">
-# to block lines containing the word
-
-<badword text="shit" replace="poo">
-<badword text="fuck" replace="(censored)">
index 45e5d28537a54c9ca48916c785677971343172bf..f9afc85a82e8e49d2cb960965499fef819e4c62b 100644 (file)
@@ -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,15 +6,18 @@
 #               reason="reason for filtering"
 #               action="action to take"
 #               flags="filter flags"
-#              duration="optional length of gline">
+#               duration="optional duration of the G-line, Z-line or shun">
 #
 # Valid actions for 'action' are:
 #
+# warn          This allows the line and sends out a notice to all opers
+#               with +s.
+#
 # block         This blocks the line, sends out a notice to all opers with
 #               +s and informs the user that their message was blocked.
 #
-# silent        This blocks the line only, and informs the user their message
-#               was blocked, but does not notify opers.
+# silent        This blocks the line only, and informs the user that their
+#               message was blocked, but does not notify opers.
 #
 # none          This action causes nothing to be done except logging. This
 #               is the default action if none is specified.
 # kill          This disconnects the user, with the 'reason' parameter as
 #               the kill reason.
 #
-# gline         G-LINE the user for 'duration' length of time. Durations may
-#               be specified using the notation 1y2d3h4m6s in a similar way to
-#               other glines, omitting the duration or setting it to 0 makes
-#               any glines set by this filter be permanent.
+# gline         G-line the user for 'duration' length of time. Durations may
+#               be specified using the notation 1y2w3d4h5m6s in a similar way to
+#               other G-lines, omitting the duration or setting it to 0 makes
+#               any G-lines set by this filter be permanent.
+#
+# zline         Z-line the user for 'duration' length of time. Durations may
+#               be specified using the notation 1y2w3d4h5m6s in a similar way to
+#               other Z-lines, omitting the duration or setting it to 0 makes
+#               any Z-lines set by this filter be permanent.
+#
+# shun          Shun the user for 'duration' length of time. Durations may
+#               be specified using the notation 1y2w3d4h5m6s in a similar way to
+#               other X-lines, omitting the duration or setting it to 0 makes
+#               any shuns set by this filter be permanent.
+#               Requires the shun module to be loaded.
 #
 # You can add filters from IRC using the /FILTER command. If you do this, they
 # will be set globally to your entire network.
 # 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="-">
+# <keyword pattern="*blah*" reason="Don't 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">
+# <keyword pattern="^blah.*?$" reason="Don't blah!" action="gline" duration="1d6h" flags="pnPq">
+
+# You may specify specific channels that are exempt from being filtered:
+#<exemptfromfilter target="#opers">
+#<exemptfromfilter target="#help">
+
+# You can also exempt messages from being filtered if they are sent to
+# specific nicks.
+# Example that exempts all messages sent *to* NickServ:
+#<exemptfromfilter target="NickServ">
 
-# An example of excluding a channel from filtering:
-# <exemptfromfilter channel="#help">
+# Note that messages *from* services are never subject to filtering;
+# <exemptfromfilter> tags are only for exempting messages sent *to* the
+# configured targets.
diff --git a/docs/conf/helpop-full.conf.example b/docs/conf/helpop-full.conf.example
deleted file mode 100644 (file)
index 959f224..0000000
+++ /dev/null
@@ -1,1101 +0,0 @@
-#####################
-#  Helpop Standard  #
-#####################
-
-<config format="xml">
-
-<alias text="HELP" replace="HELPOP $2-">
-
-<helpop key="start" value="InspIRCd Help System
-
-This system provides help for commands and modes.
-Specify your question or a command name as the
-parameter for this command.
-
-/HELPOP CUSER    -      To see a list of user commands
-/HELPOP COPER    -      To see a list of oper commands
-/HELPOP UMODES   -      To see a list of user modes
-/HELPOP CHMODES  -      To see a list of channel modes
-/HELPOP SNOMASKS -      To see a list of oper snotice masks
-/HELPOP EXTBANS  -      To see a list of extended bans">
-
-<helpop key="nohelp" value="There is no help for the topic
-you searched for. Please try again.">
-
-#####################
-#   User Commands   #
-#####################
-
-<helpop key="cuser" value="User Commands
--------------
-
-PRIVMSG   NOTICE   NICK      JOIN      PART
-CYCLE     KNOCK    MODE      DEVOICE   TOPIC
-KICK      FPART    REMOVE    TBAN      INVITE
-UNINVITE  AWAY     DCCALLOW  SILENCE   ACCEPT
-MKPASSWD  VHOST    TITLE     SETNAME
-
-WHOIS     WHOWAS   ISON      USERHOST  WATCH
-LIST      NAMES    WHO       MOTD      RULES
-ADMIN     MAP      LINKS     LUSERS    TIME
-STATS     VERSION  INFO      MODULES   COMMANDS
-SSLINFO
-
-USER      PASS     PING     PONG       QUIT
-
-OPER">
-
-<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>
-
-Uninvite a user from a channel, same syntax as INVITE.">
-
-<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
-days, three hours, four minutes and six seconds. Alternatively the
-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 - 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
-
-Duration is optional, and may be specified in seconds or in the
-form of 1m2h3d4w5y.">
-
-<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.">
-
-<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>
-
-Authenticate for a WHOIS title line and optionally a vhost using the
-specified username and password.">
-
-<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>">
-
-<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>]
-
-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
-a message stating they were removed from the channel and by whom.">
-
-<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 built-in mIRC command which caused trouble
-for some users.">
-
-<helpop key="devoice" value="/DEVOICE <channel>
-
-Devoices yourself on the specified channel.">
-
-<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.
-
-Valid SILENCE Flags
--------------------
-
- p        Block private messages
- c        Block channel messages
- i        Block invites
- n        Block private notices
- t        Block channel notices
- a        Block all of the above
- x        Exception
-
-Multiple letters may be specified. For an exception, you must pair x
-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 masks that you have silenced.">
-
-<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>
-
-This command is used by your client to register your
-IRC session, providing your ident and GECOS to the
-server.
-
-You should not use it during an established connection.">
-
-<helpop key="nick" value="/NICK <new nick>
-
-Change your nickname to <new nick>.">
-
-<helpop key="quit" value="/QUIT [<reason>]
-
-Quit from IRC and end your current session.">
-
-<helpop key="version" value="/VERSION
-
-Returns the server's version information.">
-
-<helpop key="ping" value="/PING <server>
-
-Ping a server. The server will answer with a PONG.">
-
-<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>]
-
-Shows the administrative information for the given server.">
-
-<helpop key="privmsg" value="/PRIVMSG <target> <text>
-
-Sends a message to a user or channel specified in <target>.">
-
-<helpop key="notice" value="/NOTICE <target> <text>
-
-Sends a notice to a user or channel specified in <target>.">
-
-<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>]
-
-Return a list of users on the channels you provide.">
-
-<helpop key="part" value="/PART <channel>[,<channel>] [<reason>]
-
-Leaves one or more channels you specify.">
-
-<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> [<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>.
-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.
-
-For a list of all user and channel modes, enter /HELPOP UMODES or
-/HELPOP CHMODES.">
-
-<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 <pattern> [afhilMmoprt]
-
-Looks up the information of users matching the range you provide.
-
-You may only /WHO nicknames in channels or on servers where you
-share a common channel with them, or ones which are not +i (unless
-you are a server operator). The search pattern may be a special
-sequence of characters determined by the flags given below, or
-it may be one of a nickname, a channel, a hostmask, an IP address
-mask or a server mask.
-
-Valid WHO Flags
----------------
-
-The following flags use <pattern> to match against the specified user data:
-
- a     Show users who have an away message matching <pattern>.
- i     Show users who have an ident (username) matching <pattern>.
- M     Show users who have metadata attached to them with a key name matching
-       <pattern> (server operators only).
- m     Show users who have the modes listed in <pattern>. The pattern
-       should be in the same format as a mode change e.g. +ow-i (server
-       operators only).
- p     Show users who are connected to a port in the <pattern> range (server
-       operators only).
- r     Show users who have a real name matching <pattern>.
- t     Show users who have connected in the last <pattern> seconds.
-
-The following flags filter users by their status:
-
- f     Only show users on remote (far) servers.
- l     Only show users on the local server.
- o     Only show server operators.
-
-The following flags modify the command output:
-
- h     Show real hostnames rather than display hostnames (server operators
-       only).
-
-You may combine one flag from the first group and multiple from the others in
-one WHO command.">
-
-<helpop key="motd" value="/MOTD [<server>]
-
-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="rules" value="/RULES
-
-Show the rules file for the local server. This is similar in effect to
-except that these are not sent automatically on connect.">
-
-<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>]
-
-Creates a list of all existing channels matching the glob pattern
-<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 <nick> [<nick>]
-
-Returns the hostname and nickname of a user, and some other
-miscellaneous information.">
-
-<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> ...]
-
-Returns a subset of the nicks you give, showing only those
-that are currently online.">
-
-<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,
-anyone can invite people to the channel, as long as the person
-doing the invite is a member of the channel they wish to invite
-the user to.
-
-Invited users may override bans, +k, and similar in addition to
-+i, depending on configuration.
-
-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>
-
-This command is used by your client when setting up
-your IRC session to submit a server password to the
-server.
-
-You should not use it during an established connection.">
-
-<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.">
-
-<helpop key="links" value="/LINKS
-
-Shows all servers linked to this one.">
-
-<helpop key="map" value="/MAP
-
-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>]
-
-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 [<server>]
-
-Returns the local time of the server, or remote time of another
-server.">
-
-<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>
-
-Sets your name to the specified name.">
-
-
-#####################
-#   Oper Commands   #
-#####################
-
-<helpop key="coper" value="Oper Commands
--------------
-
-OPERMOTD  CHECK     CLONES      USERIP   TLINE
-ALLTIME   WALLOPS   GLOBOPS     MODENOTICE
-CLOAK
-
-SETHOST   SETIDENT  CHGHOST     CHGIDENT CHGNAME
-SETIDLE   SWHOIS
-
-SANICK    NICKLOCK  NICKUNLOCK
-
-SAJOIN    SAPART    SAMODE      SATOPIC  SAKICK
-
-KILL      SAQUIT    GLINE       ZLINE    QLINE
-KLINE     RLINE     ELINE       CBAN     SHUN
-FILTER    OJOIN
-
-CONNECT   SQUIT     RCONNECT    RSQUIT
-
-DIE            RESTART      REHASH
-CLEARCACHE     LOADMODULE   UNLOADMODULE
-RELOADMODULE   GLOADMODULE  GUNLOADMODULE
-GRELOADMODULE  CLOSE        JUMPSERVER
-LOCKSERV       UNLOCKSERV">
-
-<helpop key="userip" value="/USERIP <nick> [<nick>]
-
-Returns the ip and nickname of the given users.">
-
-<helpop key="tline" value="/TLINE <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). Mask should be given as either a
-nick!user@host or user@IP (wildcards acceptable).">
-
-<helpop key="lockserv" value="/LOCKSERV
-
-Locks out all new connections notifying connecting users that the
-service is temporarily closed and to try again later.">
-
-<helpop key="unlockserv" value="/UNLOCKSERV
-
-Opens the server up again for new connections.">
-
-<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
-should be given for 'flags', from the list below (if you do not
-wish to specify any flags just place a '+' in this field):
-
-1. +a: Redirect all users immediately (except for opers) and cause
-them to quit with the given reason
-
-2. +n: Redirect any new users who connect and cause them to quit
-during registration
-
-You may use + and - to set or unset these flags in the command, the
-default flags are -a+n, which will just redirect new users. The
-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>]
-
-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
-filter definition, action, gline duration (when the action is 'gline')
-and reason.
-
-The filter will take effect when a message of any type specified by
-the flags and matching the definition is sent to the server, and
-perform the specified action.
-
-Valid FILTER Actions
---------------------
-
-None    Does nothing
-Block   Blocks message and informs +s IRCops of the blocked message
-        and all relevant info
-Silent  Blocks message, but does not notify IRCops
-Kill    Kills the user
-Gline   Glines the user for the specified duration
-
-Valid FILTER Flags
-------------------
-
-p    Block private and channel messages
-n    Block private and channel notices
-P    Block part messages
-q    Block quit messages
-o    Don't match against opers
-c    Strip all formatting codes from the message before matching
-*    Represents all of the above flags
--    Does nothing, a non-op for when you do not want to specify any
-     flags
-
-The reason for the filter will be used as the reason for the action,
-unless the action is 'none', and is sent to the user when their text is
-blocked by 'block' and 'silent' actions.
-
-A gline 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.
-
-When only one parameter is provided (the filter pattern) the provided
-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 <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>
-
-Retrieves a list of users with more clones than the specified
-limit.">
-
-<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
-most information the IRCD has stored on the target, including all
-metadata.
-
-With the second parameter given, runs the command remotely on the
-specified server.">
-
-<helpop key="alltime" value="/ALLTIME
-
-Shows the time on all servers on the network.">
-
-<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>.">
-
-<helpop key="rsquit" value="/RSQUIT <target mask> [<reason>]
-
-Causes a remote server matching <target mask> to be disconnected from
-the network.">
-
-<helpop key="globops" value="/GLOBOPS <message>
-
-Sends a message to all users with the +g snomask.">
-
-<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).
-
-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="sajoin" value="/SAJOIN <nick> <channel>
-
-Forces the user to join the channel.">
-
-<helpop key="sapart" value="/SAPART <nick> <channel>
-
-Forces the user to part the channel.">
-
-<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>
-
-Changes the user's nick to the new nick.">
-
-<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>
-
-Sets your idle time (in seconds) to the specified value.">
-
-<helpop key="sethost" value="/SETHOST <host>
-
-Sets your host to the specified host.">
-
-<helpop key="setident" value="/SETIDENT <ident>
-
-Sets your ident to the specified ident.">
-
-<helpop key="swhois" line="/SWHOIS <nick> <swhois>
-
-Sets the user's swhois field to the given swhois.">
-
-<helpop key="mkpasswd" value="/MKPASSWD <hashtype> <plaintext>
-
-Encodes the plaintext to a hash of the given type and displays
-the result.">
-
-<helpop key="opermotd" value="/OPERMOTD
-
-Displays the Oper MOTD.">
-
-<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>
-
-Allows a previously locked user to change nicks again.">
-
-<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>
-
-Changes the name of the user to the new name.">
-
-<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>]
-
-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
-to remove a shun (just the nick!user@host section).
-
-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>
-
-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.">
-
-<helpop key="restart" value="/RESTART <password>
-
-This command restarts the local server. A single parameter is
-required, which must match the password in the configuration for the
-command to function.">
-
-<helpop key="commands" value="/COMMANDS
-
-Shows all currently available commands.">
-
-<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>
-
-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>
-
-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>
-
-Disconnects the server matching the given server mask from this server.">
-
-<helpop key="modules" value="/MODULES
-
-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>
-
-Loads the specified module into the local server.">
-
-<helpop key="unloadmodule" value="/UNLOADMODULE <filename.so>
-
-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>
-
-Unloads and reloads a module on the local server. This module cannot
-have the static flag set (see the output of /MODULES).">
-
-<helpop key="gloadmodule" value="/GLOADMODULE <filename.so>
-
-Loads the specified module on all linked servers.">
-
-<helpop key="gunloadmodule" value="/GUNLOADMODULE <filename.so>
-
-Unloads a module from all linked servers. The module cannot have the
-static flag set (see the output of /MODULES).">
-
-<helpop key="greloadmodule" value="/GRELOADMODULE <filename.so>
-
-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>]
-
-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
-to remove a ban (just the user@host section).
-
-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>]
-
-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
-to remove a ban (just the ipmask).
-
-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>]
-
-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
-to remove a ban (just the nickmask).
-
-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>]
-
-Sets or removes a g-line (host based ban) on host mask.
-You must specify all three parameters to add a ban, and one
-parameter to remove a ban (just the user@host section).
-
-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>]
-
-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).
-
-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.
-
-This command has a few important limitations. Bans on *@<ip> can only
-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>
-
-Sends a message to all +w users.">
-
-<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
-to remove an rline (just the regex).
-
-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
-
-This command clears the DNS cache of the local server.">
-
-<helpop key="close" value="/CLOSE
-
-Closes all unregistered connections to the local server.">
-
-<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 #
-######################
-
-<helpop key="umodes" value="User Modes
-----------
-
- c            Blocks private messages and notices from users who do
-              not share a common channel with you (requires the
-              commonchans module).
- d            Deaf mode. User will not receive any messages or notices
-              from channels they are in (requires the deaf module).
- g            In combination with /ACCEPT, provides for server side
-              ignore (requires the callerid module).
- h            Marks as 'available for help' in WHOIS (IRCop only,
-              requires the helpop module).
- i            Makes invisible to /WHO if the user using /WHO is not in
-              a common channel.
- k            Prevents the user from being kicked from channels, or
-              having op modes removed from them (services only,
-              requires the servprotect module).
- o            Marks as a IRC operator.
- s <mask>     Receives server notices specified by <mask>
-              (IRCop only).
- r            Marks as a having a registered nickname
-              (requires the services account module).
- w            Receives wallops messages.
- x            Gives a cloaked hostname (requires the cloaking module).
- B            Marks as a bot (requires the botmode module).
- G            Censors messages sent to the user based on filters
-              configured for the network (requires the censor module).
- H            Hides an oper's oper status from WHOIS (requires the
-              hideoper module).
- I            Hides a user's entire channel list in WHOIS from
-              non-IRCops (requires the hidechans module).
- L            Stops redirections done by m_redirect (mode must be
-              enabled in the config).
- R            Blocks private messages from unregistered users
-              (requires the services account module).
- S            Strips formatting codes out of private messages
-              to the user (requires the stripcolor module).
- W            Receives notification when a user uses WHOIS on them
-              (IRCop only, requires the showwhois module).">
-
-<helpop key="chmodes" value="Channel Modes
--------------
-
- v <nickname>       Gives voice to <nickname>, allowing them to speak
-                    while the channel is +m.
- h <nickname>       Gives halfop status to <nickname> (requires the
-                    customprefix module).
- o <nickname>       Gives op status to <nickname>.
- a <nickname>       Gives protected status to <nickname>, preventing
-                    them from being kicked (+q only, requires the
-                    customprefix module).
- q <nickname>       Gives owner status to <nickname>, preventing them
-                    from being kicked (Services or +q only, requires
-                    the customprefix module).
-
- b <hostmask>       Bans <hostmask> from the channel.
- e <hostmask>       Excepts <hostmask> from bans (requires the
-                    banexception module).
- I <hostmask>       Excepts <hostmask> from +i, allowing matching
-                    users to join while the channel is invite-only
-                    (requires the inviteexception module).
-
- c                  Blocks messages that contain formatting codes
-                    (requires the blockcolor module).
- d <time>           Blocks messages to a channel from new users
-                    until they have been in the channel for <time>
-                    seconds (requires the delaymsg module).
- f [*]<lines>:<sec> Kicks on text flood equal to or above the
-                    specified rate. With *, the user is banned
-                    (requires the messageflood module).
- g <mask>           Blocks messages matching the given glob mask
-                    (requires the 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
-                    the joinflood module).
- 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 users' whoises
-                    and replacing it with * in /LIST.
- r                  Marks the channel as registered with Services
-                    (requires the services account module).
- s                  Make channel secret, hiding it in users' whoises
-                    and /LIST.
- t                  Prevents users without +h or +o from changing the
-                    topic.
- u                  Makes the channel an auditorium; normal users only
-                    see themselves or themselves and the operators,
-                    while operators see all the users (requires the
-                    auditorium module).
- 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 the autoop module)
- z                  Blocks non-SSL clients from joining the channel.
-
- A                  Allows anyone to invite users to the channel
-                    (normally only chanops can invite, requires
-                    the allowinvite module).
- B                  Blocks messages with too many capital letters,
-                    as determined by the network configuration
-                    (requires the blockcaps module).
- C                  Blocks any CTCPs to the channel (requires the
-                    noctcp module).
- D                  Delays join messages from users until they message
-                    the channel (requires the delayjoin module).
- F <changes>:<sec>  Blocks nick changes when they equal or exceed the
-                    specified rate (requires the nickflood module).
- G                  Censors messages to the channel based on the
-                    network configuration (requires the censor module).
- 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 the
-                    chanhistory module).
- J <seconds>        Prevents rejoin after kick for the specified
-                    number of seconds. This prevents auto-rejoin
-                    (requires the kicknorejoin module).
- K                  Blocks /KNOCK on the channel.
- L <channel>        If the channel reaches its limit set by +l,
-                    redirect users to <channel> (requires the
-                    redirect module).
- M                  Blocks unregistered users from speaking (requires
-                    the services account module).
- N                  Prevents users on the channel from changing nick
-                    (requires the nonicks module).
- O                  Channel is IRCops only (can only be set by IRCops,
-                    requires the operchans module).
- P                  Makes the channel permanent; Bans, invites, the
-                    topic, modes, and such will not be lost when it
-                    empties (can only be set by IRCops, requires
-                    the permchannels module).
- Q                  Only ulined servers and their users can kick
-                    (requires the nokicks module)
- R                  Blocks unregistered users from joining (requires
-                    the services account module).
- S                  Strips formatting codes from messages to the
-                    channel (requires the stripcolor module).
- T                  Blocks /NOTICEs to the channel from users who are
-                    not at least halfop (requires the nonotice module).
- X <type>:<status>  Makes users of <status> or higher exempt to the
-                    specified restriction <type>. For example: flood:h
-                    (requires the exemptchanops module).
- Possible restriction types to exempt with +X are:
-
- auditorium-see      Permission required to see the full user list of
-                     a +u channel (requires the auditorium module).
- auditorium-vis      Permission required to be visible in a +u channel
-                     (requires the auditorium module).
- blockcaps           Channel mode +B
- blockcolor          Channel mode +c
- censor              Channel mode +G
- filter              Channel mode +g
- flood               Channel mode +f
- nickflood           Channel mode +F
- noctcp              Channel mode +C
- nonick              Channel mode +N
- nonotice            Channel mode +T
- regmoderated        Channel mode +M
- stripcolor          Channel mode +S
- topiclock           Channel mode +t
-
--------------
-NOTE: A large number of these modes are dependent upon server-side modules
-being loaded by a server/network administrator. The actual modes available
-on your network may be very different to this list. Please consult your
-help channel if you have any questions.">
-
-######################
-#   Stats Symbols    #
-######################
-
-<helpop key="stats" value="/STATS <symbol>
-
-Shows various server statistics. Depending on configuration, some
-symbols may be only available to opers.
-
-Valid symbols are:
-
-e  Show E-lines (local ban exemptions)
-g  Show G-lines (host bans)
-k  Show K-lines (local host bans)
-q  Show Q-lines (nick mask bans)
-R  Show R-lines (regular expression bans)
-Z  Show Z-lines (IP mask bans)
-
-s  Show filters
-C  Show channel bans
-H  Show shuns
-
-c  Show link blocks
-d  Show configured DNSBLs and related statistics
-m  Show command statistics, number of times commands have been used
-o  Show a list of all valid oper usernames and hostmasks
-p  Show open client ports, and the port type (ssl, plaintext, etc)
-u  Show server uptime
-z  Show memory usage statistics
-i  Show connect class permissions
-l  Show all client connections with information (sendq, commands, bytes, time connected)
-L  Show all client connections with information and IP address
-P  Show online opers and their idle times
-T  Show bandwidth/socket statistics
-U  Show U-lined servers
-Y  Show connection classes
-O  Show opertypes and the allowed user and channel modes it can set
-E  Show socket engine events
-S  Show currently held registered nicknames
-G  Show how many local users are connected from each country according to GeoIP
-
-Note that all /STATS use is broadcast to online IRC operators.">
-
-######################
-#      SNOMASKS      #
-######################
-
-<helpop key="snomasks" value="Server Notice Masks
-
- a      Allows receipt of local announcement messages.
- A      Allows receipt of remote announcement messages.
- c      Allows receipt of local connect messages.
- C      Allows receipt of remote connect messages.
- d      Allows receipt of general (and sometimes random) debug messages.
- f      Allows receipt of flooding notices.
- g      Allows receipt of globops (requires the globops module).
- j      Allows receipt of channel creation notices (requires the chancreate module).
- J      Allows receipt of remote channel creation notices (requires the chancreate module).
- k      Allows receipt of local kill messages.
- K      Allows receipt of remote kill messages.
- l      Allows receipt of local linking related messages.
- L      Allows receipt of remote linking related messages.
- n      See local nickname changes (requires the seenicks module).
- N      See remote nickname changes (requires the seenicks modules).
- o      Allows receipt of oper-up, oper-down, and oper-failure messages.
- O      Allows receipt of remote oper-up, oper-down, and oper-failure messages.
- q      Allows receipt of local quit messages.
- Q      Allows receipt of remote quit messages.
- r      Allows receipt of local oper commands (requires the operlog module).
- R      Allows receipt of remote oper commands (requires the operlog module).
- t      Allows receipt of attempts to use /STATS (local and remote).
- v      Allows receipt of oper-override notices (requires the override module).
- x      Allows receipt of local Xline notices (g/Z/q/k/e/R/shuns).
- X      Allows receipt of remote Xline notices (g/Z/q/k/e/R/shuns).">
-
-######################
-#      EXTBANS       #
-######################
-
-<helpop key="extbans" value="Extended Bans
-----------
-
-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 the channelban module).
- r:<realname>  Matches users with a matching realname (requires the
-               gecosban module).
- s:<server>    Matches users on a matching server (requires the
-               serverban module).
- z:<certfp>    Matches users having the given SSL certificate
-               fingerprint (requires the sslmodes module).
- O:<opertype>  Matches IRCops of a matching type, mostly useful as an
-               an invite exception (requires the operchans module).
- R:<account>   Matches users logged into a matching account (requires
-               the services account module).
- U:<banmask>   Matches unregistered users matching the given banmask.
-               (requires the services account module).
-
-Acting extbans:
-
- c:<banmask>   Blocks any messages that contain formatting codes from
-               matching users (requires the blockcolor module).
- m:<banmask>   Blocks messages from matching users (requires the muteban
-               module). Users with +v or above are not affected.
- p:<banmask>   Blocks part messages from matching users (requires
-               the nopartmsg module).
- A:<banmask>   Blocks invites by matching users even when +A is set
-               (requires the allowinvite module).
- B:<banmask>   Blocks all capital or nearly all capital messages from
-               matching users (requires the blockcaps module).
- C:<banmask>   Blocks CTCPs from matching users (requires the noctcp
-               module).
- N:<banmask>   Blocks nick changes from matching users (requires
-               the nonicks module).
- Q:<banmask>   Blocks kicks by matching users (requires the nokicks
-               module).
- S:<banmask>   Strips formatting codes from messages from matching
-               users (requires the stripcolor module).
- T:<banmask>   Blocks notices from matching users (requires the
-               nonotice module).
-
-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 the banredirect module).">
index bdd3c4605862113cf2ea94f0fc209c0c42e5c84f..e12508659e0bf2ac70a05abbae6026c5e05444d0 100644 (file)
@@ -1,16 +1,20 @@
-# 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.
 #
 # The way the new helpop system works is simple. You use one or more helpop tags.
-#   <helpop key="moo" value="something here">.
+#   <helpop key="moo" value="something here">
 # key is what the user is looking for (i.e. /helpop moo), and value is what they get back
 # (note that it can span multiple lines!).
 #   -- w00t 16/dec/2006
 #
 
+<config format="xml">
+
 <alias text="HELP" replace="HELPOP $2-">
 
+<helpmsg nohelp="There is no help for the topic you searched for. Please try again.">
+
 <helpop key="start" value="InspIRCd Help System
 
 This system provides help for commands and modes.
@@ -24,53 +28,799 @@ parameter for this command.
 /HELPOP SNOMASKS -      To see a list of oper snotice masks
 /HELPOP EXTBANS  -      To see a list of extended bans">
 
-<helpop key="nohelp" value="There is no help for the topic
-you searched for. Please try again.">
-
 <helpop key="cuser" value="User Commands
 -------------
 
-PRIVMSG   NOTICE   NICK      JOIN      PART
-CYCLE     KNOCK    MODE      DEVOICE   TOPIC
-KICK      FPART    REMOVE    TBAN      INVITE
-UNINVITE  AWAY     DCCALLOW  SILENCE   ACCEPT
-MKPASSWD  VHOST    TITLE     SETNAME
+ACCEPT      ADMIN       AWAY        COMMANDS    CYCLE       DCCALLOW
+FPART       INFO        INVITE      ISON        JOIN        KICK
+KNOCK       LINKS       LIST        LUSERS      MAP         MKPASSWD
+MODE        MODULES     MONITOR     MOTD        NAMES       NICK
+NOTICE      OPER        PART        PASS        PING        PONG
+PRIVMSG     QUIT        REMOVE      SETNAME     SILENCE     SQUERY
+SSLINFO     STATS       TBAN        TIME        TITLE       TOPIC
+UNINVITE    USER        USERHOST    VERSION     VHOST       WATCH
+WHO         WHOIS       WHOWAS">
+
+<helpop key="squery" value="/SQUERY <target> :<message>
+
+Sends a message to the network service specified in <target>.">
+
+<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>
+
+Uninvite a user from a channel, same syntax as INVITE.">
+
+<helpop key="tban" value="/TBAN <channel> <duration> <banmask>
+
+Sets a timed ban. The duration of the ban can be specified in the
+form of 1y2w3d4h5m6s - meaning one year, two weeks, three days,
+four hours, five minutes and six seconds. All fields in this
+format are optional. Alternatively, the 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> [<time>]]|[LIST|HELP]
+
+/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
+
+Duration is optional, and may be specified in seconds or in the
+form of 1y2w3d4h5m6s - meaning one year, two weeks, three days,
+four hours, five minutes and six seconds. All fields in this
+format are optional.">
+
+<helpop key="accept" value="/ACCEPT *|(+|-)<nick>[,(+|-)<nick>]+
+
+Manages your accept list. This list is used to determine who can
+private message you when you have user mode +g set.
+
+/ACCEPT * - List accepted nicks
+/ACCEPT +<nick> - Add a nick
+/ACCEPT -<nick> - Remove a nick
+
+This command accepts multiple nicks like so:
+/ACCEPT +<nick>,-<nick>,+<nick>">
+
+<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 <username> <password>
+
+Authenticate for a WHOIS title line and optionally a vhost using the
+specified username and password.">
+
+<helpop key="watch" value="/WATCH C|L|l|S|(+|-)<nick> [(+|-)<nick>]+
+
+/WATCH         - List watched nicks that are online
+/WATCH l       - 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 +<nick> -<nick> +<nick>">
+
+<helpop key="monitor" value="/MONITOR C|L|S|(+|-) <nick>[,<nick>]+
+
+/MONITOR L        - List all monitored nicks, not differentiating between
+                    online and offline nicks
+/MONITOR C        - Clear all monitored nicks
+/MONITOR S        - List all monitored nicks, indicating which are online
+                    and which are offline
+/MONITOR + <nick> - Add a nick
+/MONITOR - <nick> - Remove a nick
+
+This command accepts multiple nicks like so:
+/MONITOR + <nick>,<nick>,<nick>
+/MONITOR - <nick>,<nick>,<nick>">
+
+<helpop key="vhost" value="/VHOST <username> <password>
+
+Authenticate for a vhost using the specified username and password.">
+
+<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
+a message stating they were removed from the channel and by whom.">
+
+<helpop key="rmode" value="/RMODE <channel> <mode> [<pattern>]
+
+Removes listmodes from a channel, optionally matching a glob-based pattern.
+E.g. '/RMODE #channel b m:*' will remove all mute extbans on the channel.">
+
+<helpop key="fpart" value="/FPART <channel> <nick> [:<reason>]
+
+This behaves identically to /REMOVE. /REMOVE is a built-in mIRC command
+which caused trouble for some users.">
+
+<helpop key="silence" value="/SILENCE [(+|-)<mask> [CcdiNnPpTtx]]
+
+A server-side ignore of the given n!u@h mask. If the optional flags field is
+specified then it must contain one or more flags which specify what kind of
+messages should be blocked and how they should be blocked.
+
+/SILENCE - Shows a list of silenced masks
+/SILENCE +<mask> [<flags>] - Add a mask
+/SILENCE -<mask> - Remove a mask
+
+Valid SILENCE Flags
+-------------------
+ C    Matches a CTCP targeted at a user.
+ c    Matches a CTCP targeted at a channel.
+ d    Default behaviour; equivalent to CciNnPpTt.
+ i    Matches an invite to a channel.
+ N    Matches a NOTICE targeted at a user.
+ n    Matches a NOTICE targeted at a channel.
+ P    Matches a PRIVMSG targeted at a user.
+ p    Matches a PRIVMSG targeted at a channel.
+ T    Matches a TAGMSG targeted at a user.
+ t    Matches a TAGMSG targeted at a channel.
+ x    Exempt the mask from silence rules.
+
+Any combination of flags is valid.">
+
+<helpop key="knock" value="/KNOCK <channel> :<reason>
+
+Sends a notice to a channel indicating you wish to join.">
+
+<helpop key="user" value="/USER <username> <unused> <unused> :<realname>
+
+This command is used by your client to register your
+IRC session, providing your ident and real name to the
+server.
+
+You should not use it during an established connection.">
+
+<helpop key="nick" value="/NICK <newnick>
+
+Change your nickname to <newnick>.">
+
+<helpop key="quit" value="/QUIT [:<message>]
+
+Quit from IRC and end your current session.">
+
+<helpop key="version" value="/VERSION [<servername>]
+
+Returns the server's version information.">
+
+<helpop key="ping" value="/PING <servername> [:<servername>]
+
+Ping a server. The server will answer with a PONG.">
+
+<helpop key="pong" value="/PONG <servername>
+
+Your client should send this to answer server PINGs. You
+should not issue this command manually.">
+
+<helpop key="admin" value="/ADMIN [<servername>]
+
+Shows the administrative information for the given server.">
+
+<helpop key="privmsg" value="/PRIVMSG <target>[,<target>]+ :<message>
+
+Sends a message to a user or channel specified in <target>.">
+
+<helpop key="notice" value="/NOTICE <target>[,<target>]+ :<message>
+
+Sends a notice to a user or channel specified in <target>.">
+
+<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>]+
+
+Return a list of users on the channel(s) you provide.">
 
-WHOIS     WHOWAS   ISON      USERHOST  WATCH
-LIST      NAMES    WHO       MOTD      RULES
-ADMIN     MAP      LINKS     LUSERS    TIME
-STATS     VERSION  INFO      MODULES   COMMANDS
-SSLINFO
+<helpop key="part" value="/PART <channel>[,<channel>]+ [:<reason>]
 
-USER      PASS     PING     PONG       QUIT
+Leaves one or more channels you specify.">
+
+<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> [<mode-parameters>]]
+
+Change or view 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>.
+A user may only set modes upon themselves, and may not set the
++o user mode, and a user may only change channel modes of
+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>]
+
+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 <pattern> [<flags>][%[<fields>[,<querytype>]]] <pattern>
+
+Looks up information about users matching the provided pattern. You can specify
+a flag specific pattern, a channel name, user hostname, a user server name, a
+user real name, or a user nickname. Matching users will only be included in the
+WHO response if:
+
+ 1) The specified pattern is an exact channel name that does not have the
+    private or secret channel modes set and the user does not have the invisible
+    user mode set.
+ 2) The specified pattern is an exact nickname.
+ 3) You share one or more common channels with the user.
+ 4) The user does not have the invisible user mode set.
+ 5) You are a server operator with the users/auspex privilege.
+
+If you specify any fields the response returned will be a WHOX response rather
+than a RFC 1459 WHO response.
+
+Valid WHO Flags
+---------------
+
+The following flags use <pattern> to match against the specified user data:
+
+ A     Show users who have an away message matching <pattern>.
+ a     Show users who have an account name matching <pattern>.
+ h     Show users who have a hostname matching <pattern>. If the 'x' modifier
+       is specified then this will match against the real hostname instead of
+       the display hostname.
+ i     Show users who have an IP address matching <pattern>.
+ m     Show users who have the modes listed in <pattern>. The pattern
+       should be in the same format as a mode change e.g. +ow-i (server
+       operators only).
+ n     Show users who have a nickname matching <pattern>.
+ p     Show users who are connected to a port in the <pattern> range (server
+       operators only).
+ r     Show users who have a real name matching <pattern>.
+ s     Show users who are on a server with a name matching <pattern>. If the 'x'
+       modifier is specified then this will match against the real server name
+       instead of the masked server name.
+ t     Show users who have connected in the last <pattern> seconds.
+ u     Show users who have an ident (username) matching <pattern>.
+
+The following flags filter users by their status:
+
+ f     Only show users on remote (far) servers.
+ l     Only show users on the local server.
+ o     Only show server operators.
+
+The following flags modify the command output:
+
+ x     Show sensitive data like real user hostnames and, when hideserver is
+       enabled, real server hostnames.
+
+You may combine one flag from the first group and multiple from the others in
+one WHO command.
+
+Valid WHO Fields
+----------------
+
+ a     Include the user's account name in the response.
+ c     Include the first common channel name in the response.
+ d     Include the user's server distance from you in the response.
+ f     Include the user's away status, oper status, and highest channel prefix
+       in the response.
+ h     Include the user's hostname in the response. If the 'x' flag was
+       specified then this is the real host rather than the display host.
+ i     Include the user's IP address in the response.
+ l     Include the user's idle time in the response.
+ n     Include the user's nickname in the response.
+ o     Include the user's channel operator rank level in the response.
+ r     Include the user's real name in the response.
+ s     Include the user's server name in the response. If the 'x' flag was
+       specified then this is the real server name rather than the masked server
+       name.
+ t     Include the query type in the response.
+ u     Include the user's ident in the response.
+
+">
+
+<helpop key="motd" value="/MOTD [<servername>]
+
+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 <username> <password>
+
+Attempts to authenticate as a server operator.
+
+Both successful and unsuccessful oper attempts are
+logged, and sent to online server operators.">
+
+<helpop key="list" value="/LIST [<pattern>]
+
+Creates a list of all existing channels matching the glob pattern
+<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 <nick> [<nick>]+
+
+Returns the hostname and nickname of a user, and some other
+miscellaneous information.">
+
+<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>]+
+
+Returns a subset of the nicks you give, showing only those
+that are currently online.">
+
+<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,
+anyone can invite people to the channel, as long as the person
+doing the invite is a member of the channel they wish to invite
+the user to.
+
+Invited users may override bans, +k, and similar in addition to
++i, depending on configuration.
+
+If a time is provided, the invite expires after that time and the user
+can no longer use it to enter the channel. The time can be specified
+in the form of 1y2w3d4h5m6s - meaning one year, two weeks, three days,
+four hours, five minutes and six seconds. All fields in this format
+are optional. Alternatively, the time may just be specified as a number
+of seconds.
+
+/INVITE without a parameter will list pending invitations for channels
+you have been invited to.">
+
+<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
+server.
+
+You should not use it during an established connection.">
+
+<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.">
+
+<helpop key="links" value="/LINKS
+
+Shows all linked servers.">
+
+<helpop key="map" value="/MAP
+
+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 [<servername>] <nick>[,<nick>]+
+
+Returns the WHOIS information of a user, their channels, hostname,
+etc. If a servername 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 [<servername>]
+
+Returns the local time of the server, or remote time of another
+server.">
+
+<helpop key="info" value="/INFO [<servername>]
+
+Returns information on the developers and supporters who made this
+IRC server possible.">
+
+<helpop key="setname" value="/SETNAME :<realname>
+
+Sets your real name to the specified real name.">
 
-OPER">
 
 <helpop key="coper" value="Oper Commands
 -------------
 
-OPERMOTD  CHECK     CLONES      USERIP   TLINE
-ALLTIME   WALLOPS   GLOBOPS     MODENOTICE
-CLOAK
+ALLTIME        CBAN           CHECK          CHGHOST        CHGIDENT
+CHGNAME        CLEARCHAN      CLOAK          CLONES         CONNECT
+DIE            ELINE          FILTER         GLINE          GLOADMODULE
+GLOBOPS        GRELOADMODULE  GUNLOADMODULE  KILL           KLINE
+LOADMODULE     LOCKSERV       MODENOTICE     NICKLOCK       NICKUNLOCK
+OJOIN          OPERMOTD       QLINE          RCONNECT       REHASH
+RELOADMODULE   RESTART        RLINE          RSQUIT         SAJOIN
+SAKICK         SAMODE         SANICK         SAPART         SAQUIT
+SATOPIC        SETHOST        SETIDENT       SETIDLE        SHUN
+SQUIT          SWHOIS         TLINE          UNLOADMODULE   UNLOCKSERV
+USERIP         WALLOPS        ZLINE">
+
+<helpop key="userip" value="/USERIP <nick> [<nick>]+
+
+Returns the IP address and nickname of the given user(s).">
+
+<helpop key="tline" value="/TLINE <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). Mask should be given as either
+nick!user@host or user@IP (wildcards and CIDR blocks are accepted).">
 
-SETHOST   SETIDENT  CHGHOST     CHGIDENT CHGNAME
-SETIDLE   SWHOIS
+<helpop key="lockserv" value="/LOCKSERV [:<message>]
 
-SANICK    NICKLOCK  NICKUNLOCK
+Locks out all new connections notifying connecting users that the
+service is temporarily closed and to try again later.">
 
-SAJOIN    SAPART    SAMODE      SATOPIC  SAKICK
+<helpop key="unlockserv" value="/UNLOCKSERV
 
-KILL      SAQUIT    GLINE       ZLINE    QLINE
-KLINE     RLINE     ELINE       CBAN     SHUN
-FILTER
+Opens the server up again for new connections.">
 
-CONNECT   SQUIT     RCONNECT    RSQUIT
+<helpop key="filter" value="/FILTER <pattern> [<action> <flags> [<duration>] :<reason>]
 
-DIE            RESTART      REHASH
-CLEARCACHE     LOADMODULE   UNLOADMODULE
-RELOADMODULE   GLOADMODULE  GUNLOADMODULE
-GRELOADMODULE  CLOSE        JUMPSERVER
-LOCKSERV       UNLOCKSERV">
+This command will add a global filter when more than one parameter is
+given, for messages of the types specified by the flags, with the given
+filter pattern, action, duration (when the action is 'gline', 'zline'
+or 'shun'), and reason.
+
+The filter will take effect when a message of any type specified by
+the flags and matching the pattern is sent to the server, and
+perform the specified action.
+
+Valid FILTER Actions
+--------------------
+
+None    Does nothing
+Warn    Lets the message through and informs +s server operators
+        of the message and all relevant info
+Block   Blocks message and informs +s server operators of the blocked
+        message and all relevant info
+Silent  Blocks message, but does not notify server operators
+Kill    Kills the user
+Gline   G-lines the user for the specified duration
+Zline   Z-lines the user for the specified duration
+Shun    Shuns the user for the specified duration (requires the shun module)
+
+Valid FILTER Flags
+------------------
+
+p    Block private and channel messages
+n    Block private and channel notices
+P    Block part messages
+q    Block quit messages
+o    Don't match against opers
+c    Strip all formatting codes from the message before matching
+*    Represents all of the above flags
+-    Does nothing, a non-op for when you do not want to specify any
+     flags
+
+The reason for the filter will be used as the reason for the action,
+unless the action is 'none', and is sent to the user when their text is
+blocked by 'block' and 'silent' actions.
+
+A G-line, Z-line or shun 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.
+
+When only one parameter is provided (the filter pattern) the provided
+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 <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>
+
+Retrieves a list of users with more clones than the specified
+limit.">
+
+<helpop key="check" value="/CHECK <nick>|<ipmask>|<hostmask>|<channel> [<servername>]
+
+Allows opers to look up advanced information on nicknames, IP addresses,
+hostmasks or channels, in a similar way to WHO but in more detail,
+displaying most information the server has stored on the target,
+including all metadata.
+
+With the second parameter given, runs the command remotely on the
+specified server, useful especially if used on a nickname that is
+online on a remote server.">
+
+<helpop key="alltime" value="/ALLTIME
+
+Shows the date and time of all servers on the network.">
+
+<helpop key="rconnect" value="/RCONNECT <remote-server-mask> <target-server-mask>
+
+The server matching <remote-server-mask> will try to connect to the first
+server in the config file matching <target-server-mask>.">
+
+<helpop key="rsquit" value="/RSQUIT <target-server-mask> [:<reason>]
+
+Causes a remote server matching <target-server-mask> to be disconnected from
+the network.">
+
+<helpop key="globops" value="/GLOBOPS :<message>
+
+Sends a message to all users with the +g snomask.">
+
+<helpop key="cban" value="/CBAN <channel> [<duration> [:<reason>]]
+
+Sets or removes a global channel ban. You must specify all three parameters
+to add a ban, and one parameter to remove a ban (just the channel).
+
+The duration may be specified in seconds, or in the format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, four hours,
+five minutes and six seconds. All fields in this format are optional.">
+
+<helpop key="sajoin" value="/SAJOIN [<nick>] <channel>[,<channel>]+
+
+Forces the user to join the channel(s).
+If no nick is given, it joins the oper doing the /SAJOIN.">
+
+<helpop key="sapart" value="/SAPART <nick> <channel>[,<channel>]+ [:<reason>]
+
+Forces the user to part the channel(s), with an optional reason.">
+
+<helpop key="samode" value="/SAMODE <target> (+|-)<modes> [<mode-parameters>]
+
+Applies the given mode change to the channel or nick specified.">
+
+<helpop key="sanick" value="/SANICK <nick> <newnick>
+
+Changes the user's nick to the new nick.">
+
+<helpop key="sakick" value="/SAKICK <channel> <nick> [:<reason>]
+
+Kicks the given user from the specified channel, with an optional reason.">
+
+<helpop key="satopic" value="/SATOPIC <channel> :<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 <duration>
+
+Sets your idle time to the specified value.
+
+The time can be specified in the form of 1y2w3d4h5m6s - meaning one year,
+two weeks, three days, four hours, five minutes and six seconds.
+All fields in this format are optional. Alternatively, the time may
+just be specified as a number of seconds.">
+
+<helpop key="sethost" value="/SETHOST <host>
+
+Sets your host to the specified host.">
+
+<helpop key="setident" value="/SETIDENT <ident>
+
+Sets your ident to the specified ident.">
+
+<helpop key="swhois" line="/SWHOIS <nick> :<swhois>
+
+Sets the user's swhois field to the given swhois message.
+This will be visible in their /WHOIS.
+
+To remove this message again, use:
+/SWHOIS <nick> :">
+
+<helpop key="mkpasswd" value="/MKPASSWD <hashtype> <plaintext>
+
+Encodes the plaintext to a hash of the given type and displays
+the result.">
+
+<helpop key="opermotd" value="/OPERMOTD [<servername>]
+
+Displays the Oper MOTD.">
+
+<helpop key="nicklock" value="/NICKLOCK <nick> <newnick>
+
+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>
+
+Allows a previously locked user to change nicks again.">
+
+<helpop key="chghost" value="/CHGHOST <nick> <host>
+
+Changes the host of the user to the specified host.">
+
+<helpop key="chgname" value="/CHGNAME <nick> :<realname>
+
+Changes the real name of the user to the specified real name.">
+
+<helpop key="chgident" value="/CHGIDENT <nick> <ident>
+
+Changes the ident of the user to the specified ident.">
+
+<helpop key="shun" value="/SHUN <nick!user@host> [<duration> :<reason>]
+
+Sets or removes a shun (global server-side ignore) on a nick!user@host mask.
+You must specify all three parameters to add a shun, and one parameter
+to remove a shun (just the nick!user@host).
+
+The duration may be specified in seconds, or in the format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, four hours,
+five minutes and six seconds. All fields in this format are optional.">
+
+<helpop key="die" value="/DIE <servername>
+
+This command shuts down the local server. A single parameter is
+required, which must match the name of the local server.">
+
+<helpop key="restart" value="/RESTART <servername>
+
+This command restarts the local server. A single parameter is
+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 <nick>[,<nick>]+ :<reason>
+
+This command will disconnect a user from IRC with the given reason.">
+
+<helpop key="rehash" value="/REHASH [<servermask>]
+
+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>
+
+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>
+
+Disconnects the server matching the given server mask from this server.">
+
+<helpop key="modules" value="/MODULES [<servername>]
+
+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 <modulename>
+
+Loads the specified module into the local server.">
+
+<helpop key="unloadmodule" value="/UNLOADMODULE <modulename>
+
+Unloads a module from the local server.">
+
+<helpop key="reloadmodule" value="/RELOADMODULE <modulename>
+
+Unloads and reloads a module on the local server.">
+
+<helpop key="gloadmodule" value="/GLOADMODULE <modulename> [<servermask>]
+
+Loads the specified module on all linked servers.">
+
+<helpop key="gunloadmodule" value="/GUNLOADMODULE <modulename> [<servermask>]
+
+Unloads a module from all linked servers.">
+
+<helpop key="greloadmodule" value="/GRELOADMODULE <modulename> [<servermask>]
+
+Unloads and reloads a module on all linked servers.">
+
+<helpop key="kline" value="/KLINE <user@host> [<duration> :<reason>]
+
+Sets or removes a K-line (local user@host based ban) on a user@host mask.
+You must specify all three parameters to add a ban, and one parameter
+to remove a ban (just the user@host).
+
+The duration may be specified in seconds, or in the format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, four hours,
+five minutes and six seconds. All fields in this format are optional.">
+
+<helpop key="zline" value="/ZLINE <ipmask> [<duration> :<reason>]
+
+Sets or removes a Z-line (global IP based ban) on an IP mask.
+You must specify all three parameters to add a ban, and one parameter
+to remove a ban (just the ipmask).
+
+The duration may be specified in seconds, or in the format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, four hours,
+five minutes and six seconds. All fields in this format are optional.">
+
+<helpop key="qline" value="/QLINE <nickmask> [<duration> :<reason>]
+
+Sets or removes a Q-line (global nick based ban) on a nick mask.
+You must specify all three parameters to add a ban, and one parameter
+to remove a ban (just the nickmask).
+
+The duration may be specified in seconds, or in the format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, four hours,
+five minutes and six seconds. All fields in this format are optional.">
+
+<helpop key="gline" value="/GLINE <user@host> [<duration> :<reason>]
+
+Sets or removes a G-line (global user@host based ban) on a user@host mask.
+You must specify all three parameters to add a ban, and one
+parameter to remove a ban (just the user@host).
+
+The duration may be specified in seconds, or in the format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, four hours,
+five minutes and six seconds. All fields in this format are optional.">
+
+<helpop key="eline" value="/ELINE <user@host> [<duration> :<reason>]
+
+Sets or removes a E-line (global user@host ban exception) on a user@host mask.
+You must specify at least 3 parameters to add an exception, and one
+parameter to remove an exception (just the user@host).
+
+The duration may be specified in seconds, or in the format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, four hours,
+five minutes and six seconds. All fields in this format are optional.
+
+This command has a few important limitations. Bans on *@<ip> can only
+be negated by an E-line on *@<ip>, bans on *@<host> can be negated by
+E-lines on *@<ip>, or *@<host>, and bans on <ident>@* or <ident>@<host>
+can be negated by any E-line that matches.">
+
+<helpop key="wallops" value="/WALLOPS :<message>
+
+Sends a message to all +w users.">
+
+<helpop key="rline" value="/RLINE <regex> [<duration> :<reason>]
+
+Sets or removes an R-line (global regex ban) on a n!u@h\srealname mask. You
+must specify all three parameters to add an R-line, and one parameter
+to remove an R-line (just the regex).
+
+The duration may be specified in seconds, or in the format
+1y2w3d4h5m6s - meaning one year, two weeks, three days, four hours,
+five minutes and six seconds. All fields in this format are optional.">
+
+<helpop key="clearchan" value="/CLEARCHAN <channel> [KILL|KICK|G|Z] [:<reason>]
+
+Quits or kicks all non-opers from a channel, optionally G/Z-lines them.
+Useful for quickly nuking bot channels.
+
+The default method, KILL, simply disconnects the victims from the server,
+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.">
 
 <helpop key="umodes" value="User Modes
 ----------
@@ -80,37 +830,41 @@ LOCKSERV       UNLOCKSERV">
               commonchans module).
  d            Deaf mode. User will not receive any messages or notices
               from channels they are in (requires the deaf module).
- g            In combination with /ACCEPT, provides for server side
+ g            In combination with /ACCEPT, provides for server-side
               ignore (requires the callerid module).
- h            Marks as 'available for help' in WHOIS (IRCop only,
-              requires the helpop module).
+ h            Marks as 'available for help' in WHOIS (server operators
+              only, requires the helpop module).
  i            Makes invisible to /WHO if the user using /WHO is not in
               a common channel.
  k            Prevents the user from being kicked from channels, or
               having op modes removed from them (services only,
               requires the servprotect module).
- o            Marks as a IRC operator.
+ o            Marks as a server operator.
  s <mask>     Receives server notices specified by <mask>
-              (IRCop only).
+              (server operators only).
  r            Marks as a having a registered nickname
               (requires the services account module).
  w            Receives wallops messages.
  x            Gives a cloaked hostname (requires the cloaking module).
+ z            Only allow private messages from SSL users (requires the
+              sslmode module).
  B            Marks as a bot (requires the botmode module).
+ D            Privdeaf mode. User will not receive any private messages
+              or notices from users (requires the deaf module).
  G            Censors messages sent to the user based on filters
               configured for the network (requires the censor module).
  H            Hides an oper's oper status from WHOIS (requires the
               hideoper module).
  I            Hides a user's entire channel list in WHOIS from
-              non-IRCops (requires the hidechans module).
- L            Stops redirections done by m_redirect (mode must be
-              enabled in the config).
+              non-server operators (requires the hidechans module).
+ L            Stops redirections done by the redirect module (requires
+              the redirect module).
  R            Blocks private messages from unregistered users
               (requires the services account module).
  S            Strips formatting codes out of private messages
               to the user (requires the stripcolor module).
- W            Receives notification when a user uses WHOIS on them
-              (IRCop only, requires the showwhois module).">
+ W            Receives notifications when a user uses WHOIS on them
+              (server operators only, requires the showwhois module).">
 
 <helpop key="chmodes" value="Channel Modes
 -------------
@@ -172,7 +926,8 @@ LOCKSERV       UNLOCKSERV">
                     For example, +w o:R:Brain will op anyone identified
                     to the account 'Brain' on join.
                     (requires the autoop module)
- z                  Blocks non-SSL clients from joining the channel.
+ z                  Blocks non-SSL clients from joining the channel
+                    (requires the sslmodes module).
 
  A                  Allows anyone to invite users to the channel
                     (normally only chanops can invite, requires
@@ -184,6 +939,10 @@ LOCKSERV       UNLOCKSERV">
                     noctcp module).
  D                  Delays join messages from users until they message
                     the channel (requires the delayjoin module).
+ E [~|*]<lines>:<sec>[:<difference>][:<backlog>]    Allows blocking of
+                    similar messages (requires the repeat module).
+                    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
                     specified rate (requires the nickflood module).
  G                  Censors messages to the channel based on the
@@ -195,7 +954,8 @@ LOCKSERV       UNLOCKSERV">
  J <seconds>        Prevents rejoin after kick for the specified
                     number of seconds. This prevents auto-rejoin
                     (requires the kicknorejoin module).
- K                  Blocks /KNOCK on the channel.
+ K                  Blocks /KNOCK on the channel (requires the
+                    knock module).
  L <channel>        If the channel reaches its limit set by +l,
                     redirect users to <channel> (requires the
                     redirect module).
@@ -203,14 +963,14 @@ LOCKSERV       UNLOCKSERV">
                     the services account module).
  N                  Prevents users on the channel from changing nick
                     (requires the nonicks module).
- O                  Channel is IRCops only (can only be set by IRCops,
-                    requires the operchans module).
+ O                  Channel is server operators only (can only be set
+                    by server operators, requires the operchans module).
  P                  Makes the channel permanent; Bans, invites, the
                     topic, modes, and such will not be lost when it
-                    empties (can only be set by IRCops, requires
-                    the permchannels module).
- Q                  Only ulined servers and their users can kick
-                    (requires the nokicks module)
+                    empties (can only be set by server operators,
+                    requires the permchannels module).
+ Q                  Only U-lined servers and their users can kick
+                    (requires the nokicks module).
  R                  Blocks unregistered users from joining (requires
                     the services account module).
  S                  Strips formatting codes from messages to the
@@ -220,6 +980,24 @@ LOCKSERV       UNLOCKSERV">
  X <type>:<status>  Makes users of <status> or higher exempt to the
                     specified restriction <type>. For example: flood:h
                     (requires the exemptchanops module).
+ Possible restriction types to exempt with +X are:
+
+ auditorium-see      Permission required to see the full user list of
+                     a +u channel (requires the auditorium module).
+ auditorium-vis      Permission required to be visible in a +u channel
+                     (requires the auditorium module).
+ blockcaps           Channel mode +B
+ blockcolor          Channel mode +c
+ censor              Channel mode +G
+ filter              Channel mode +g
+ flood               Channel mode +f
+ nickflood           Channel mode +F
+ noctcp              Channel mode +C
+ nonick              Channel mode +N
+ nonotice            Channel mode +T
+ regmoderated        Channel mode +M
+ stripcolor          Channel mode +S
+ topiclock           Channel mode +t
 
 -------------
 NOTE: A large number of these modes are dependent upon server-side modules
@@ -227,14 +1005,55 @@ being loaded by a server/network administrator. The actual modes available
 on your network may be very different to this list. Please consult your
 help channel if you have any questions.">
 
+<helpop key="stats" value="/STATS <symbol> [<servername>]
+
+Shows various server statistics. Depending on configuration, some
+symbols may be only available to opers.
+
+Valid symbols are:
+
+e  Show E-lines (global user@host ban exceptions)
+g  Show G-lines (global user@host bans)
+k  Show K-lines (local user@host bans)
+q  Show Q-lines (global nick bans)
+R  Show R-lines (global regular expression bans)
+Z  Show Z-lines (global IP mask bans)
+
+s  Show filters (global)
+C  Show channel bans (global)
+H  Show shuns (global)
+
+c  Show link blocks
+d  Show configured DNSBLs and related statistics
+m  Show command statistics, number of times commands have been used
+o  Show a list of all valid oper usernames and hostmasks
+p  Show open client ports, and the port type (ssl, plaintext, etc)
+u  Show server uptime
+z  Show memory usage statistics
+i  Show connect class permissions
+l  Show all client connections with information (sendq, commands, bytes, time connected)
+L  Show all client connections with information and IP address
+P  Show online opers and their idle times
+T  Show bandwidth/socket statistics
+U  Show U-lined servers
+Y  Show connection classes
+O  Show opertypes and the allowed user and channel modes it can set
+E  Show socket engine events
+S  Show currently held registered nicknames
+G  Show how many local users are connected from each country
+
+Note that all /STATS use is broadcast to online server operators.">
+
 <helpop key="snomasks" value="Server Notice Masks
 
  a      Allows receipt of local announcement messages.
  A      Allows receipt of remote announcement messages.
  c      Allows receipt of local connect messages.
  C      Allows receipt of remote connect messages.
- d      Allows receipt of general (and sometimes random) debug messages.
- f      Allows receipt of flooding notices.
+ d      Allows receipt of local DNSBL messages (requires the dnsbl module).
+ D      Allows receipt of remote DNSBL messages (requires the dnsbl module).
+ f      Allows receipt of local filter messages (requires the filter module).
+ F      Allows receipt of remote filter messages (requires the filter module).
  g      Allows receipt of globops (requires the globops module).
  j      Allows receipt of channel creation notices (requires the chancreate module).
  J      Allows receipt of remote channel creation notices (requires the chancreate module).
@@ -242,8 +1061,8 @@ help channel if you have any questions.">
  K      Allows receipt of remote kill messages.
  l      Allows receipt of local linking related messages.
  L      Allows receipt of remote linking related messages.
- n      See local nickname changes (requires the seenicks module).
- N      See remote nickname changes (requires the seenicks modules).
+ n      Allows receipt of local nickname changes (requires the seenicks module).
+ N      Allows receipt of remote nickname changes (requires the seenicks modules).
  o      Allows receipt of oper-up, oper-down, and oper-failure messages.
  O      Allows receipt of remote oper-up, oper-down, and oper-failure messages.
  q      Allows receipt of local quit messages.
@@ -251,9 +1070,9 @@ help channel if you have any questions.">
  r      Allows receipt of local oper commands (requires the operlog module).
  R      Allows receipt of remote oper commands (requires the operlog module).
  t      Allows receipt of attempts to use /STATS (local and remote).
- v      Allows receipt of oper-override notices (requires the override module).
- x      Allows receipt of local Xline notices (g/Z/q/k/e/R/shuns).
- X      Allows receipt of remote Xline notices (g/Z/q/k/e/R/shuns).">
+ v      Allows receipt of oper override notices (requires the override module).
+ x      Allows receipt of local X-line notices (G/Z/Q/K/E/R/SHUN/CBan).
+ X      Allows receipt of remote X-line notices (G/Z/Q/K/E/R/SHUN/CBan).">
 
 <helpop key="extbans" value="Extended Bans
 ----------
@@ -271,16 +1090,22 @@ setting +I <extban>.
 
 Matching extbans:
 
+ a:<mask>      Matches user with both a matching banmask and real name,
+               where <mask> is in the format nick!user@host+realname
+               (requires gecosban module).
  j:<channel>   Matches anyone in the given channel. Does not support
                wildcards (requires the channelban module).
- r:<realname>  Matches users with a matching realname (requires the
+ n:<class>     Matches users in a matching connect class (requires
+               the classban module).
+ r:<realname>  Matches users with a matching real name (requires the
                gecosban module).
  s:<server>    Matches users on a matching server (requires the
                serverban module).
  z:<certfp>    Matches users having the given SSL certificate
                fingerprint (requires the sslmodes module).
- O:<opertype>  Matches IRCops of a matching type, mostly useful as an
-               an invite exception (requires the operchans module).
+ O:<opertype>  Matches server operators of a matching type, mostly
+               useful as an invite exception (requires the
+               operchans module).
  R:<account>   Matches users logged into a matching account (requires
                the services account module).
  U:<banmask>   Matches unregistered users matching the given banmask.
index d54cdc916923854e1b18b2357a8ca3bebfffc12c..18b9cd366f6298400a6fb365e53f0eb4da3edb65 100644 (file)
 #                                                                      #
 ########################################################################
 
+#-#-#-#-#-#-#-#-#-#  CONFIGURATION FORMAT  #-#-#-#-#-#-#-#-#-#-#-#-#-#-
+#                                                                     #
+# In order to maintain compatibility with older configuration files,  #
+# you can change the configuration parser to parse as it did in       #
+# previous releases. When using the "compat" format, you need to use  #
+# C++ escape sequences (e.g. \n) instead of XML ones (e.g. &nl;) and  #
+# can not use <define> to create macros.                              #
+#<config format="compat">
+
 #-#-#-#-#-#-#-#-#-#  INCLUDE CONFIGURATION  #-#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 # This optional tag allows you to include another config file         #
 #                                                                     #
 # Variables may be redefined and may reference other variables.       #
 # Value expansion happens at the time the tag is read.                #
-#                                                                     #
-# Using variable definitions REQUIRES that the config format be       #
-# changed to "xml" from the default "compat" that uses escape         #
-# sequences such as "\"" and "\n", and does not support <define>      #
-<config format="xml">
 <define name="bindip" value="1.2.2.3">
 <define name="localips" value="&bindip;/24">
 
@@ -93,8 +97,7 @@
         #id="97K"
 
         # network: Network name given on connect to clients.
-        # Should be the same on all servers on the network and
-        # not contain spaces.
+        # Should be the same on all servers on the network.
         network="Omega">
 
 
 #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
 #                                                                     #
 #  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!                                #
 
       # to this bind section.
       type="clients"
 
-      # ssl: If you want the port(s) in this bind tag to use SSL, set this
-      # to either "gnutls" or "openssl". The appropriate SSL module must be
-      # loaded for SSL to work. If you do not want the port(s) in this bind
-      # tag 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
+      # docs 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
+      # the daemon from the operating system before data is ready.
+      # In Linux, the value indicates the time period we'll wait for a
+      # connection to come up with data. Don't set it too low!
+      # In BSD the value is ignored; only zero and non-zero is possible.
+      # Windows ignores this parameter completely.
+      # Note: This does not take effect on rehash.
+      # 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">
 
-<bind address="" port="7000,7001" type="servers">
-<bind address="1.2.3.4" port="7005" type="servers" ssl="openssl">
+# EXPERIMENTAL: Listener that binds on a UNIX endpoint instead of a TCP/IP endpoint:
+#<bind path="/tmp/inspircd.sock" type="clients">
 
+# You can define a custom <sslprofile> tag which defines the SSL configuration
+# for this listener. See the docs 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.
 
-#-#-#-#-#-#-#-#-#-#-  DIE/RESTART CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-
-#                                                                     #
-#   You can configure the passwords here which you wish to use for    #
-#   the /DIE and /RESTART commands. Only trusted ircops 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.
-       #
-       # IMPORTANT: leaving this field empty does not disable the use of
-       # the DIE command. In order to prevent the use of this command you
-       # should remove it from the command privileges of your opers.
-       diepass=""
-
-       # restartpass: Password for opers to use if they need to restart
-       # a server.
-       #
-       # IMPORTANT: leaving this field empty does not disable the use of
-       # the RESTART command. In order to prevent the use of this command
-       # you should remove it from the command privileges of your opers.
-       restartpass="">
+<bind address="" port="7000,7001" type="servers">
+<bind address="1.2.3.4" port="7005" type="servers" ssl="openssl">
 
 
 #-#-#-#-#-#-#-#-#-#-  CONNECTIONS CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
 #  -- It is important to note that connect tags are read from the  -- #
 #     TOP DOWN. This means that you should have more specific deny    #
 #    and allow tags at the top, progressively more general, followed  #
-#        by a <connect allow="*" (should you wish to have one).       #
+#        by a <connect allow="*"> (should you wish to have one).      #
 #                                                                     #
 # Connect blocks are searched twice for each user - once when the TCP #
 # connection is accepted, and once when the user completes their      #
          # a CIDR range here.
          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, or sha256) 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 <hashtype> <plaintext>
+         #hash="bcrypt"
 
          # password: Password to use for this block/user(s)
          password="secret"
 
          # maxchans: Maximum number of channels a user in this class
-         # be in at one time. This overrides every other maxchans setting.
-         #maxchans="30"
+         # be in at one time.
+         maxchans="20"
 
-         # timeout: How long (in seconds) the server will wait before
-         # disconnecting a user if they do not do anything on connect.
+         # timeout: How long the server will wait before disconnecting
+         # a user if they do not do anything on connect.
          # (Note, this is a client-side thing, if the client does not
-         # send /nick, /user or /pass)
+         # send /NICK, /USER or /PASS)
          timeout="10"
 
          # localmax: Maximum local connections per IP (or CIDR mask, see below).
          # 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
+         # in this class. This can save a lot of resources on very busy servers.
+         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.
          useident="no"
 
+         # webirc: Restricts usage of this class to the specified WebIRC gateway.
+         # This setting only has effect when the cgiirc module is loaded.
+         #webirc="name"
+
          # limit: How many users are allowed in this class
          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.
-         # This entry is highly recommended to use for/with IP Cloaking/masking.
-         # For the example to work, this also requires that the m_cloaking
+         # modes: User modes that are set on users in this block on connect.
+         # 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 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
 
          # Alternate MOTD file for this connect class. The contents of this file are
          # specified using <files secretmotd="filename"> or <execfiles ...>
+         #
+         # NOTE: the following escape sequences for IRC formatting characters can be
+         # used in your MOTD:
+         # Bold:          \b
+         # Color:         \c<fg>[,<bg>]
+         # Italic:        \i
+         # Monospace:     \m  (not widely supported)
+         # Reset:         \x
+         # Reverse:       \r
+         # Strikethrough: \s  (not widely supported)
+         # Underline:     \u
+         # See https://defs.ircdocs.horse/info/formatting.html for more information
+         # on client support for formatting characters.
          motd="secretmotd"
 
-         # Allow color codes to be processed in the message of the day file.
-         # the following characters are valid color code escapes:
-         #   \002 or \b = Bold
-         #   \037 or \u = Underline
-         #   \003 or \c = Color (with a code postfixed to this char)
-         #   \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
          allow="*"
 
          # maxchans: Maximum number of channels a user in this class
-         # be in at one time. This overrides every other maxchans setting.
-         #maxchans="30"
+         # be in at one time.
+         maxchans="20"
 
-         # timeout: How long (in seconds) the server will wait before
-         # disconnecting a user if they do not do anything on connect.
+         # timeout: How long the server will wait before disconnecting
+         # a user if they do not do anything on connect.
          # (Note, this is a client-side thing, if the client does not
-         # send /nick, /user or /pass)
+         # send /NICK, /USER or /PASS)
          timeout="10"
 
-         # pingfreq: How often (in seconds) the server tries to ping connecting clients.
-         pingfreq="120"
+         # pingfreq: How often the server tries to ping connecting clients.
+         pingfreq="2m"
 
          # hardsendq: maximum amount of data allowed in a client's send queue
          # before they are dropped. Keep this value higher than the length of
 
          # softsendq: amount of data in a client's send queue before the server
          # begins delaying their commands in order to allow the sendq to drain
-         softsendq="8192"
+         softsendq="10240"
 
          # recvq: amount of data allowed in a client's queue before they are dropped.
-         # Entering "8K" is equivalent to "8192", see above.
-         recvq="8K"
+         # Entering "10K" is equivalent to "10240", see above.
+         recvq="10K"
 
          # threshold: This specifies the amount of command penalty a user is allowed to have
          # before being quit or fakelagged due to flood. Normal commands have a penalty of 1,
          # globalmax: Maximum global (network-wide) connections per IP.
          globalmax="3"
 
+         # resolvehostnames: If disabled, no DNS lookups will be performed on connecting users
+         # in this class. This can save a lot of resources on very busy servers.
+         resolvehostnames="yes"
+
          # useident: Defines if users in this class must respond to a ident query or not.
          useident="no"
 
          # limit: How many users are allowed in this class
          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.
-         # This entry is highly recommended to use for/with IP Cloaking/masking.
-         # For the example to work, this also requires that the m_cloaking
+         # modes: User modes that are set on users in this block on connect.
+         # 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 cloaking
          # module be loaded as well.
          modes="+x">
 
 
 # This file has all the information about oper classes, types and o:lines.
 # You *MUST* edit it.
-<include file="conf/examples/opers.conf.example">
+<include file="examples/opers.conf.example">
 
 # This file has all the information about server links and ulined servers.
 # You *MUST* edit it if you intend to link servers.
-<include file="conf/examples/links.conf.example">
+<include file="examples/links.conf.example">
 
 #-#-#-#-#-#-#-#-#-#-  MISCELLANEOUS CONFIGURATION  -#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 # Files block - contains files whose contents are used by the ircd
 #
 #   motd - displayed on connect and when a user executes /MOTD
-#   rules - displayed when the user executes /RULES
 # Modules can also define their own files
-<files motd="conf/examples/motd.txt.example" rules="conf/examples/rules.txt.example">
+<files motd="examples/motd.txt.example">
 
 # Example of an executable file include. Note this will be read on rehash,
 # not when the command is run.
-#<execfiles rules="wget -O - https://www.example.com/rules.txt">
-
-#-#-#-#-#-#-#-#-#-#-#-# MAXIMUM CHANNELS -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-
-<channels
-          # users: Maximum number of channels a user can be in at once.
-          users="20"
-
-          # opers: Maximum number of channels an oper can be in at once.
-          opers="60">
+#<execfiles motd="wget -O - https://www.example.com/motd.txt">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-# DNS SERVER -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # If these values are not defined, InspIRCd uses the default DNS resolver
      #
      # server="127.0.0.1"
 
-     # timeout: seconds to wait to try to resolve DNS/hostname.
+     # timeout: time to wait to try to resolve DNS/hostname.
      timeout="5">
 
 # An example of using an IPv6 nameserver
 
 #<pid file="/path/to/inspircd.pid">
 
-#-#-#-#-#-#-#-#-#-#-#-#-#- BANLIST LIMITS #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+#-#-#-#-#-#-#-#-#-#-#-#-#- LIST MODE LIMITS #-#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# Use these tags to customise the ban limits on a per channel basis.  #
-# The tags are read from top to bottom, and any tag found which       #
-# matches the channels name applies the banlimit to that channel.     #
+# The <maxlist> tag is used customise the maximum number of each list #
+# mode that can be set on a channel.                                  #
+# The tags are read from top to bottom and the list mode limit from   #
+# the first tag found which matches the channel name and mode type is #
+# applied to that channel.                                            #
 # It is advisable to put an entry with the channel as '*' at the      #
-# bottom of the list. If none are specified or no maxbans tag is      #
-# matched, the banlist size defaults to 64 entries.                   #
+# bottom of the list. If none are specified or no maxlist tag is      #
+# matched, the banlist size defaults to 100 entries.                  #
 #                                                                     #
 
-<banlist chan="#largechan" limit="128">
-<banlist chan="*" limit="69">
-
-#-#-#-#-#-#-#-#-#-#-#-  DISABLED FEATURES  -#-#-#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# This tag is optional, and specifies one or more features which are  #
-# not available to non-operators.                                     #
-#                                                                     #
-# For example you may wish to disable NICK and prevent non-opers from #
-# changing their nicknames.                                           #
-# Note that any disabled commands take effect only after the user has #
-# 'registered' (e.g. after the initial USER/NICK/PASS on connection)  #
-# so for example disabling NICK will not cripple your network.        #
-#                                                                     #
-# You can also define if you want to disable any channelmodes         #
-# or usermodes from your users.                                       #
-#                                                                     #
-# `fakenonexistant' will make the ircd pretend that nonexistant       #
-# commands simply don't exist to non-opers ("no such command").       #
-#                                                                     #
-#<disabled commands="TOPIC MODE" usermodes="" chanmodes="" fakenonexistant="yes">
-
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-  RTFM LINE  -#-#-#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-#   Just remove this... Its here to make you read ALL of the config   #
-#   file options ;)                                                   #
-
-<die value="You should probably edit your config *PROPERLY* and try again.">
+# Allows #largechan to have up to 200 ban entries.
+#<maxlist mode="ban" chan="#largechan" limit="200">
 
+# Allows #largechan to have up to 200 ban exception entries.
+#<maxlist mode="e" chan="#largechan" limit="200">
 
+# Allows all channels and list modes not previously matched to have
+# up to 100 entries.
+<maxlist chan="*" limit="100">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-  SERVER OPTIONS   -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
          # the correct parameters are.
          syntaxhints="no"
 
-         # cyclehosts: If enabled, when a user gets a host set, it will cycle
-         # them in all their channels. If not, it will simply change their host
-         # without cycling them.
-         cyclehosts="yes"
+         # casemapping: This sets the case mapping method to be used by the
+         # server. This MUST be the same on all servers. Possible values are:
+         # "ascii" (recommended)
+         # "rfc1459" (default, required for linking to 2.0 servers)
+         # NOTE: if you are using the nationalchars module this setting will be
+         # ignored. You should use <nationalchars:casemapping> instead.
+         casemapping="ascii"
 
          # cyclehostsfromuser: If enabled, the source of the mode change for
          # cyclehosts will be the user who cycled. This can look nicer, but
          # 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"
          # in the topic. If set to no, it will only show the nick of the topic setter.
          hostintopic="yes"
 
-         # pingwarning: If a server does not respond to a ping within x seconds,
+         # pingwarning: If a server does not respond to a ping within this period,
          # it will send a notice to opers with snomask +l informing that the server
          # is about to ping timeout.
          pingwarning="15"
 
-         # serverpingfreq: How often pings are sent between servers (in seconds).
-         serverpingfreq="60"
+         # serverpingfreq: How often pings are sent between servers.
+         serverpingfreq="1m"
+
+         # splitwhois: Whether to split private/secret channels from normal channels
+         # in WHOIS responses. Possible values for this are:
+         # 'no' - list all channels together in the WHOIS response regardless of type.
+         # 'split' - split private/secret channels to a separate WHOIS response numeric.
+         # 'splitmsg' - the same as split but also send a message explaining the split.
+         splitwhois="no"
 
          # defaultmodes: What modes are set on a empty channel when a user
          # joins it and it is unregistered.
-         defaultmodes="nt"
+         defaultmodes="not"
 
-         # moronbanner: This is the text that is sent to a user when they are
+         # xlinemessage: This is the text that is sent to a user when they are
          # banned from the server.
-         moronbanner="You're banned! Email abuse@example.com with the ERROR line below for help."
+         xlinemessage="You're banned! Email irc@example.com with the ERROR line below for help."
+
+         # allowzerolimit: If enabled then allow a limit of 0 to be set on channels.
+         # This is non-standard behaviour and should only be enabled if you need to
+         # link with servers running 2.0. Defaults to yes.
+         allowzerolimit="no"
 
          # exemptchanops: Allows users with with a status mode to be exempt
          # from various channel restrictions. Possible restrictions are:
          # See m_exemptchanops in modules.conf.example for more details.
          exemptchanops="censor:o filter:o nickflood:o nonick:v regmoderated:o"
 
-         # invitebypassmodes: This allows /invite to bypass other channel modes.
+         # invitebypassmodes: This allows /INVITE to bypass other channel modes.
          # (Such as +k, +j, +l, etc.)
          invitebypassmodes="yes"
 
          # nosnoticestack: This prevents snotices from 'stacking' and giving you
          # the message saying '(last message repeated X times)'. Defaults to no.
-         nosnoticestack="no"
-
-         # welcomenotice: When turned on, this sends a NOTICE to connecting users
-         # with the text Welcome to <networkname>! after successful registration.
-         # Defaults to yes.
-         welcomenotice="yes">
+         nosnoticestack="no">
 
 
 #-#-#-#-#-#-#-#-#-#-#-# PERFORMANCE CONFIGURATION #-#-#-#-#-#-#-#-#-#-#
              # 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.
+             # Setting this above the limit imposed by your OS can have undesired
+             # effects.
              somaxconn="128"
 
-             # limitsomaxconn: By default, somaxconn (see above) is limited to a
-             # safe maximum value in the 2.0 branch for compatibility reasons.
-             # This setting can be used to disable this limit, forcing InspIRCd
-             # to use the value specified above.
-             limitsomaxconn="true"
-
              # softlimit: This optional feature allows a defined softlimit for
              # connections. If defined, it sets a soft max connections value.
              softlimit="12800"
 
+             # clonesonconnect: If this is set to false, we won't check for clones
+             # on initial connection, but only after the DNS check is done.
+             # This can be useful where your main class is more restrictive
+             # than some other class a user can be assigned after DNS lookup is complete.
+             # Turning this option off will make the server spend more time on users we may
+             # potentially not want. Normally this should be neglible, though.
+             # Default value is true
+             clonesonconnect="true"
+
+             # timeskipwarn: The time period that a server clock can jump by before
+             # operators will be warned that the server is having performance issues.
+             timeskipwarn="2s"
+
              # quietbursts: When syncing or splitting from a network, a server
              # can generate a lot of connect and quit messages to opers with
              # +C and +Q snomasks. Setting this to yes squelches those messages,
              # which makes it easier for opers, but degrades the functionality of
              # bots like BOPM during netsplits.
-             quietbursts="yes"
-
-             # nouserdns: If enabled, no DNS lookups will be performed on
-             # connecting users. This can save a lot of resources on very busy servers.
-             nouserdns="no">
+             quietbursts="yes">
 
 #-#-#-#-#-#-#-#-#-#-#-# SECURITY CONFIGURATION  #-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 
 <security
+          # allowcoreunload: If this value is set to yes, Opers will be able to
+          # unload core modules (e.g. core_privmsg).
+          allowcoreunload="no"
 
           # announceinvites: This option controls which members of the channel
           # receive an announcement when someone is INVITEd. Available values:
           #             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.
+          # be hidden from non-opers in /LINKS and /MAP.
           hideulines="no"
 
-          # flatlinks: If this value is set to yes, /map and /links will
+          # flatlinks: If this value is set to yes, /MAP and /LINKS will
           # be flattened when shown to non-opers.
           flatlinks="no"
 
-          # hidewhois: When defined, the given text will be used in place
-          # of the server a user is on when whoised by a non-oper. Most
-          # networks will want to set this to something like "*.netname.net"
-          # to conceal the actual server a user is on.
-          # Note that enabling this will cause users' idle times to only be
-          # shown when the format /WHOIS <nick> <nick> is used.
-          hidewhois=""
+          # hideserver: When defined, the given text will be used in place
+          # of the server name in public messages. As with <server:name> this
+          # does not need to resolve but does need to be a valid hostname.
+          #
+          # NOTE: enabling this will cause users' idle times to only be shown
+          # when a remote whois (/WHOIS <nick> <nick>) is used.
+          #hideserver="*.example.com"
 
-          # hidebans: If this value is set to yes, when a user is banned ([gkz]lined)
+          # hidebans: If this value is set to yes, when a user is banned ([KGZ]-lined)
           # only opers will see the ban message when the user is removed
           # from the server.
           hidebans="no"
 
-          # hidekills: If defined, replaces who set a /kill with a custom string.
+          # hidekills: If defined, replaces who executed a /KILL with a custom string.
           hidekills=""
 
           # hideulinekills: Hide kills from clients of ulined servers from server notices.
           hidesplits="no"
 
           # maxtargets: Maximum number of targets per command.
-          # (Commands like /notice, /privmsg, /kick, etc)
+          # (Commands like /NOTICE, /PRIVMSG, /KICK, etc)
           maxtargets="20"
 
-          # customversion: Displays a custom string when a user /version's
-          # the ircd. This may be set for security reasons or vanity reasons.
+          # customversion: A custom message to be displayed in the comments field
+          # of the VERSION command response. This does not hide the InspIRCd version.
           customversion=""
 
-          # operspywhois: show opers (users/auspex) the +s and +p channels a user is in. Values:
-          #  splitmsg  Split secret/private from public channels with an explanatory message
-          #  yes       Show secret/private channels
-          #  no        Do not show secret/private channels
-          operspywhois="no"
-
           # runasuser: If this is set, InspIRCd will attempt to switch
           # to run as this user, which allows binding of ports under 1024.
           # You should NOT set this unless you are starting as root.
 
           # restrictbannedusers: If this is set to yes, InspIRCd will not allow users
           # banned on a channel to change nickname or message channels they are
-          # banned on.
+          # banned on. This can also be set to silent to restrict the user but not
+          # notify them.
           restrictbannedusers="yes"
 
           # genericoper: Setting this value to yes makes all opers on this server
-          # appear as 'is an IRC operator' in their WHOIS, regardless of their
+          # appear as 'is a server operator' in their WHOIS, regardless of their
           # oper type, however oper types are still used internally. This only
           # affects the display in WHOIS.
           genericoper="no"
 
-          # userstats: /stats commands that users can run (opers can run all).
+          # userstats: /STATS commands that users can run (opers can run all).
           userstats="Pu">
 
 #-#-#-#-#-#-#-#-#-#-#-#-# LIMITS CONFIGURATION  #-#-#-#-#-#-#-#-#-#-#-#
 
 <limits
         # maxnick: Maximum length of a nickname.
-        maxnick="31"
+        maxnick="30"
 
         # maxchan: Maximum length of a channel name.
         maxchan="64"
         maxmodes="20"
 
         # maxident: Maximum length of a ident/username.
-        maxident="11"
+        maxident="10"
+
+        # maxhost: Maximum length of a hostname.
+        maxhost="64"
 
         # maxquit: Maximum length of a quit message.
         maxquit="255"
         # maxkick: Maximum length of a kick message.
         maxkick="255"
 
-        # maxgecos: Maximum length of a GECOS (realname).
-        maxgecos="128"
+        # maxreal: Maximum length of a real name.
+        maxreal="128"
 
         # maxaway: Maximum length of an away message.
         maxaway="200">
 
+#-#-#-#-#-#-#-#-#-#-#-#-# PATHS CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
+#                                                                     #
+# This configuration tag defines the location that InspIRCd stores    #
+# various types of files such as configuration files, log files and   #
+# modules. You will probably not need to change these from the values #
+# set when InspIRCd was built unless you are using a binary package   #
+# where you do not have the ability to set build time configuration.  #
+#<path configdir="conf" datadir="data" logdir="logs" moduledir="modules">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Logging
 # the behaviour of the logging of the IRCd.
 #
 # An example log tag would be:
-#  <log method="file" type="OPER" level="default" target="logs/opers.log">
-# which would log all information on /oper (failed and successful) to
+#  <log method="file" type="OPER" level="default" target="opers.log">
+# which would log all information on /OPER (failed and successful) to
 # a file called opers.log.
 #
 # There are many different types which may be used, and modules may
 #  - USERS - information relating to user connection and disconnection
 #  - 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
 #  - 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.
 
-<log method="file" type="* -USERINPUT -USEROUTPUT" level="default" target="logs/ircd.log">
+<log method="file" type="* -USERINPUT -USEROUTPUT" level="default" target="ircd.log">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-  WHOWAS OPTIONS   -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# This tag lets you define the behaviour of the /whowas command of    #
+# This tag lets you define the behaviour of the /WHOWAS command of    #
 # your server.                                                        #
 #                                                                     #
 
 <whowas
         # groupsize: Maximum entries per nick shown when performing
-        # a /whowas nick.
+        # a /WHOWAS <nick>.
         groupsize="10"
 
         # maxgroups: Maximum number of nickgroups that can be added to
-        # the list so that /whowas does not use a lot of resources on
+        # the list so that /WHOWAS does not use a lot of resources on
         # large networks.
         maxgroups="100000"
 
          # nick: Nick to disallow. Wildcards are supported.
          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: Reason to display on /NICK.
+         reason="Reserved for a network service">
 
 <badhost
          # host: ident@hostname to ban.
 <badhost host="root@*" reason="Don't IRC as root!">
 <badhost host="*@198.51.100.0/24" reason="This subnet is bad.">
 
-# exception: Hosts that are exempt from [kgz]lines.
+# exception: Hosts that are exempt from [KGZ]-lines.
 <exception
            # host: ident@hostname to exempt.
            # Wildcards and CIDR (if you specify an IP) can be used.
-           host="*@ircop.example.com"
+           host="*@serverop.example.com"
 
-           # reason: Reason for exception. Only shown in /stats e
+           # reason: Reason for exception. Only shown in /STATS e.
            reason="Oper's hostname">
 
 #-#-#-#-#-#-#-#-#-#-#- INSANE BAN OPTIONS  -#-#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# This optional tag allows you to specify how wide a gline, eline,    #
-# kline, zline or qline can be before it is forbidden from being      #
-# set. By setting hostmasks="yes", you can allow all G, K, E lines,   #
+# This optional tag allows you to specify how wide a G-line, E-line,  #
+# K-line, Z-line or Q-line can be before it is forbidden from being   #
+# set. By setting hostmasks="yes", you can allow all G-, K-, E-lines, #
 # no matter how many users the ban would cover. This is not           #
-# recommended! By setting ipmasks="yes", you can allow all Z lines,   #
+# recommended! By setting ipmasks="yes", you can allow all Z-lines,   #
 # no matter how many users these cover too. Needless to say we        #
 # don't recommend you do this, or, set nickmasks="yes", which will    #
-# allow any qline.                                                    #
+# allow any Q-line.                                                   #
 #                                                                     #
 
 <insane
         # will be banning 955 or more users.
         trigger="95.5">
 
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- YAWN  -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-#   You should already know what to do here :)                        #
-
-<die value="User error. You didn't edit your config properly. Go back and try again.">
-
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# MODULES #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 #    ____                _   _____ _     _       ____  _ _   _        #
 #   |  _ \ ___  __ _  __| | |_   _| |__ (_)___  | __ )(_) |_| |       #
 # provide almost all the features of InspIRCd. :)                     #
 #                                                                     #
 # The default does nothing -- we include it for simplicity for you.   #
-<include file="conf/examples/modules.conf.example">
+<include file="examples/modules.conf.example">
 
-# Here are some pre-built modules.conf files that closely match the
-# default configurations of some popular IRCd's. You still may want to
-# look over them and make sure if everything is correct for you and setup
-# the proper SSL information.
+#-#-#-#-#-#-#-#-#-#-#-# 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:                              #
 #
-# *NOTE*: These files have no comments for what the modules do. If you
-# are interested in that, please read the modules.conf.example. It is also
-# recommended that you make your own modules file based on modules.conf.example.
-
-# Settings similar to UnrealIRCd defaults.
-#<include file="conf/examples/modules/unrealircd.conf.example">
-
-# Settings similar to Charybdis IRCd defaults.
-#<include file="conf/examples/modules/charybdis.conf.example">
-
+# 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">
 
 #########################################################################
 #                                                                       #
index 8d62bb42fac4ede3004f793d8ce91cb911ac49ea..09b3bb3d5b345b5d6ab4c0663419cfb9727e8cfa 100644 (file)
@@ -10,7 +10,7 @@
 #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
 #                                                                     #
 #  If you want to link servers to InspIRCd you must load the          #
-#  m_spanningtree.so module!                                          #
+#  spanningtree module!                                               #
 #                                                                     #
 #                                                                     #
 
 
       # allowmask: Range of IP addresses to allow for this link.
       # Can be a CIDR (see example).
-      allowmask="203.0.113.0/24"
+      allowmask="203.0.113.0/24 127.0.0.0/8 2001:db8::/32"
 
       # timeout: If defined, this option defines how long the server
       # will wait to consider the connect attempt failed and try the
       # failover (see above).
-      timeout="300"
+      timeout="5m"
 
-      # 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 docs 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://docs.inspircd.org/2/modules/spanningtree
-      # for more information. This will require an SSL link for both inbound
-      # and outbound connections.
+      # authenticated using SSL certificate fingerprints. See
+      # https://docs.inspircd.org/3/modules/spanningtree for more information.
+      # This will require an SSL link for both inbound and outbound connections.
       #fingerprint=""
 
       # bind: Local IP address to bind to.
       # servers will not be shown when users do a /MAP or /LINKS.
       hidden="no"
 
-      # passwords: the passwords we send and receive.
+      # passwords: The passwords we send and receive.
       # The remote server will have these passwords reversed.
       # Passwords that contain a space character or begin with
       # a colon (:) are invalid and may not be used.
       sendpass="outgoing!password"
       recvpass="incoming!password">
 
-# A duplicate of the first link block without comments
+# A duplicate of the first link block without comments,
 # if you like copying & pasting.
 <link name="hub.example.org"
       ipaddr="penguin.example.org"
       port="7000"
-      allowmask="203.0.113.0/24"
-      timeout="300"
+      allowmask="203.0.113.0/24 127.0.0.0/8 2001:db8::/32"
+      timeout="5m"
       ssl="gnutls"
       bind="1.2.3.4"
       statshidden="no"
       sendpass="penguins"
       recvpass="polarbears">
 
-# Simple autoconnect block. This enables automatic connection of a server
+# Simple autoconnect block. This enables automatic connections to a server.
 # Recommended setup is to have leaves connect to the hub, and have no
 # automatic connections started by the hub.
-<autoconnect period="300" server="hub.example.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
 # space separated servers to autoconnect; they will be tried in a round
 # robin fashion until one succeeds. Period defines the time for restarting
 # a single loop.
-<autoconnect period="120"
+<autoconnect period="2m"
        server="hub.us.example.org hub.eu.example.org leaf.eu.example.org">
 
 
-#-#-#-#-#-#-#-#-#-#-#-#- ULINES CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
-# This tag defines a ulined server. A U-Lined server has special      #
-# permissions, and should be used with caution. Services servers are  #
-# usually u-lined in this manner.                                     #
-#                                                                     #
-# The 'silent' value, if set to yes, indicates that this server should#
-# not generate quit and connect notices, which can cut down on noise  #
-# to opers on the network.                                            #
-#                                                                     #
+#-#-#-#-#-#-#-#-#-#-#-#-# U-LINES CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
+# This tag defines a U-lined server. A U-lined server has special       #
+# permissions, and should be used with caution. Services servers are    #
+# usually U-lined in this manner.                                       #
+#                                                                       #
+# The 'silent' value, if set to yes, indicates that this server should  #
+# not generate quit and connect notices, which can cut down on noise    #
+# to opers on the network.                                              #
+#                                                                       #
 <uline server="services.example.com" silent="yes">
+
+# Once you have edited this file you can remove this line. This is just to
+# ensure that you don't hastily include the file without reading it.
+<die reason="Using links.conf.example without editing it is a security risk">
index 65d34e70fb9366dd1631e7dd0c176ca1bbea4d7c..517c5572033a28c6cefa838bcf50575af8d980c9 100644 (file)
@@ -10,7 +10,7 @@
 #                                                                     #
 #  By default, ALL modules are commented out. You must uncomment them #
 #  or add lines to your config to load modules. Please refer to       #
-#  https://docs.inspircd.org/2/modules for a list of modules and      #
+#  https://docs.inspircd.org/3/modules for a list of modules and      #
 #  each modules link for any additional conf tags they require.       #
 #                                                                     #
 #    ____                _   _____ _     _       ____  _ _   _        #
@@ -19,8 +19,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. This module is at the bottom of #
 # this file.                                                          #
 # 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">
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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="sha256">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 "!".
@@ -72,9 +65,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.   #
@@ -86,7 +79,7 @@
 #                    read from the top of the file to the bottom.     #
 #                                                                     #
 # usercommand -      If this is true, the alias can be run simply as  #
-#                    /aliasname. Defaults to true.                    #
+#                    /ALIASNAME. Defaults to true.                    #
 #                                                                     #
 # channelcommand -   If this is true, the alias can be used as an     #
 #                    in-channel alias or 'fantasy command', prefixed  #
 #                    the user receives a 'no such nick' 401 numeric.  #
 #                                                                     #
 # uline       -      Setting this to true will ensure that the user   #
-#                    given in 'requires' is also on a u-lined server, #
+#                    given in 'requires' is also on a U-lined server, #
 #                    as well as actually being on the network. If the #
-#                    user is online, but not on a u-lined server,     #
+#                    user is online, but not on a U-lined server,     #
 #                    then an oper alert is sent out as this is        #
 #                    possibly a sign of a user trying to impersonate  #
 #                    a service.                                       #
 #                    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.
 #
-#<alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3"
+#<alias text="ID" format="#*" replace="SQUERY ChanServ :IDENTIFY $2 $3"
 #  requires="ChanServ" uline="yes">
 #
-#<alias text="ID" replace="PRIVMSG NickServ :IDENTIFY $2"
+#<alias text="ID" replace="SQUERY NickServ :IDENTIFY $2"
 #  requires="NickServ" uline="yes">
 #
 # 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-"
+#<alias text="NICKSERV" format=":IDENTIFY *" replace="SQUERY NickServ :IDENTIFY $3-"
 #  requires="NickServ" uline="yes">
 #
 # You may also add aliases to trigger based on something said in a
 # command must be preceded by the fantasy prefix when used.
 #
 #<alias text="CS" usercommand="no" channelcommand="yes"
-#  replace="PRIVMSG ChanServ :$1 $chan $2-" requires="ChanServ" uline="yes">
+#  replace="SQUERY ChanServ :$1 $chan $2-" requires="ChanServ" uline="yes">
 #
 # This would be used as "!cs <command> <options>", with the channel
 # being automatically inserted after the command in the message to
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Anticaps module: Adds channel mode +B which allows you to punish
+# users that send overly capitalised messages to channels. Unlike the
+# blockcaps module this module is more flexible as it has more options
+# for punishment and allows channels to configure their own punishment
+# policies.
+#<module name="anticaps">
+#
+# You may also configure the characters which anticaps considers to be
+# lower case and upper case. Any characters not listed here are assumed
+# to be punctuation and will be ignored when counting:
+# <anticaps lowercase="abcdefghijklmnopqrstuvwxyz"
+#           uppercase="ABCDEFGHIJKLMNOPQRSTUVWXYZ">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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:
 #
 # 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.              #
+# delay          -   How much time between two messages to force them #
+#                    to be recognised as unrelated.                   #
 # action         -   Any of 'notice', 'noticeopers', 'silent', 'kill' #
 #                    or 'killopers'. Define how to take action when   #
 #                    a user uses /amsg or /ame.                       #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Block CAPS module: Adds channel mode +B, blocks all-CAPS messages.
-#<module name="m_blockcaps.so">
+#
+# NOTE: This module is deprecated and will be removed in a future version
+# of InspIRCd. You should use the anticaps module shown above instead.
+#<module name="blockcaps">
 #
 #-#-#-#-#-#-#-#-#-#-#-  BLOCKCAPS CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# percent        - How many percent of text must be caps before text  #
+# percent        - The percentage of a message which must be upper    #
+#                  case before it will be blocked.                    #
+#                                                                     #
+# minlen         - The minimum length a message must be before it     #
 #                  will be blocked.                                   #
 #                                                                     #
-# minlen         - The minimum length a line must be for the block    #
-#                  percent to have any effect.                        #
+# lowercase      - The characters which will be considered lower      #
+#                  case.                                              #
 #                                                                     #
-# 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.                                #
+# uppercase      - The characters which will be considered upper      #
+#                  case.                                              #
+#
 #<blockcaps percent="50"
 #           minlen="5"
-#           capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! ">
+#           lowercase="abcdefghijklmnopqrstuvwxyz"
+#           uppercase="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 module: Adds user mode +g which activates hybrid-style
 # callerid: block all private messages unless you /ACCEPT first.
-#<module name="m_callerid.so">
+#<module name="callerid">
 #
 #-#-#-#-#-#-#-#-#-#-#- CALLERID  CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
-# maxaccepts     - Maximum number of entries a user can add to his    #
-#                  /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? #
+# maxaccepts     - Maximum number of entries a user can add to their  #
+#                  /ACCEPT list. Default is 30 entries.               #
+# tracknick      - Preserve /ACCEPT entries when a user changes nick? #
 #                  If no (the default), the user is removed from      #
-#                  everyone's accept list if he changes nickname.     #
-# cooldown       - Amount of time (in seconds) that must pass since   #
-#                  the last notification sent to a user before he can #
-#                  be sent another. Default is 60 (1 minute).         #
-#<callerid maxaccepts="16"
-#          operoverride="no"
+#                  everyone's accept list if their nickname changes.  #
+# cooldown       - Amount of time that must pass since the last       #
+#                  notification sent to a user before they can be     #
+#                  sent another. Default is 1 minute.                 #
+#<callerid maxaccepts="30"
 #          tracknick="no"
-#          cooldown="60">
+#          cooldown="1m">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # CAP module: Provides the CAP negotiation mechanism required by the
-# m_sasl, m_namesx, m_uhnames, and m_ircv3 modules.
-# It is also recommended for the STARTTLS support in m_ssl_gnutls.
-#<module name="m_cap.so">
+# 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.
 # 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">
+# Censor module: Adds channel and user mode +G which block phrases that
+# are listed in the server bad words list.
+#<module name="censor">
 #
 #-#-#-#-#-#-#-#-#-#-#-  CENSOR  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# Optional - If you specify to use the m_censor module, then you must #
-# specify some censor tags. See also:                                 #
-# https://docs.inspircd.org/2/modules/censor                          #
-#
-#<include file="conf/examples/censor.conf.example">
+# If you have the censor module loaded you should specify one or more #
+# phrases to replace/block in user messages. The config for this is   #
+# formatted as follows:                                               #
+#                                                                     #
+# Replaces "eggplant" with "aubergine" within messages:               #
+# <badword text="eggplant" replace="aubergine">                       #
+#                                                                     #
+# Blocks messages that contain "fluffy capybaras":                    #
+#<badword text="fluffy capybaras">                                    #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# CGI:IRC module: Adds support for automatic host changing in CGI:IRC
-# (http://cgiirc.sourceforge.net).
-#<module name="m_cgiirc.so">
+# CGI:IRC module: Enables forwarding the real IP address of a user from
+# a gateway to the IRC server.
+#<module name="cgiirc">
 #
 #-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
 #
-# Optional - If you specify to use m_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: https://docs.inspircd.org/2/modules/cgiirc 
-#
-# Set to yes if you want to notice opers when CGI:IRC clients connect.
+# If you use the cgiirc module then you must specify the gateways which
+# are authorised to forward IP/host information to your server. There
+# are currently two ways to do this:
+#
+# The webirc method is the recommended way to allow gateways to forward
+# IP/host information. When using this method the gateway sends a WEBIRC
+# message to the server on connection. For more details please read the
+# IRCv3 WebIRC specification at: https://ircv3.net/specs/extensions/webirc.html
+#
+# When using this method you must specify a wildcard mask or CIDR range
+# to allow gateway connections from and at least one of either a SSL
+# client certificate fingerprint for the gateway or a password to be
+# sent in the WEBIRC command.
+#
+# <cgihost type="webirc"
+#          fingerprint="bd90547b59c1942b85f382bc059318f4c6ca54c5"
+#          mask="192.0.2.0/24">
+# <cgihost type="webirc"
+#          password="$2a$10$WEUpX9GweJiEF1WxBDSkeODBstIBMlVPweQTG9cKM8/Vd58BeM5cW"
+#          hash="bcrypt"
+#          mask="*.webirc.gateway.example.com">
+#
+# Alternatively if your gateway does not support sending the WEBIRC
+# message then you can configure InspIRCd to look for the client IP
+# address in the ident sent by the user. This is not recommended as it
+# only works with IPv4 connections.
+#
+# When using this method you must specify a wildcard mask or CIDR range to allow
+# gateway connections from. You can also optionally configure the static value
+# that replaces the IP in the ident to avoid leaking the real IP address of
+# gateway clients (defaults to "gateway" if not set).
+#
+# <cgihost type="ident"
+#          mask="198.51.100.0/24"
+#          newident="wibble">
+# <cgihost type="ident"
+#          mask="*.ident.gateway.example.com"
+#          newident="wobble">
+#
+# By default gateway connections are logged to the +w snomask. If you
+# do not want this to happen then you can uncomment this to disable it.
 # <cgiirc opernotice="no">
-#
-# The type field indicates where the module should get the real
-# client's IP address from, for further information, please see the
-# CGI:IRC documentation.
-#
-# Old style:
-# <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.example.com">                      # Get IP from WEBIRC
-#
+
 # IMPORTANT NOTE:
 # ---------------
 #
-# When you connect CGI:IRC clients, there are two connect classes which
+# When you connect gateway clients, there are two connect classes which
 # apply to these clients. When the client initially connects, the connect
-# class which matches the CGI:IRC site's host is checked. Therefore you
-# must raise the maximum local/global clients for this ip as high as you
-# want to allow cgi clients. After the client has connected and is
-# determined to be a cgi:irc client, the class which matches the client's
+# class which matches the gateway site's host is checked. Therefore you
+# must raise the maximum local/global clients for this IP as high as you
+# want to allow gateway clients. After the client has connected and is
+# determined to be a gateway client, the class which matches the client's
 # real IP is then checked. You may set this class to a lower value, so that
 # the real IP of the client can still be restricted to, for example, 3
 # sessions maximum.
 # 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.
-#<chanfilter hidemask="yes">
+#
+# If maxlen is set then it defines the maximum length of a filter entry.
+#
+# If notifyuser is set to no, the user will not be notified when
+# his/her message is blocked.
+#<chanfilter hidemask="yes" maxlen="50" notifyuser="yes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Channel history module: Displays the last 'X' lines of chat to a user
 # 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'.
 # If notice is set to yes, joining users will get a NOTICE before playback
 # telling them about the following lines being the pre-join history.
 # If bots is set to yes, it will also send to users marked with +B
-#<chanhistory maxlines="20" notice="yes" bots="yes">
+#<chanhistory maxlines="50" notice="yes" bots="yes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Channel logging module: Used to send snotice output to channels, to
 # 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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Channelban: Implements extended ban j:, which stops anyone already
-# in a channel matching a ban like +b j:#channel*mask from joining.
+# in a channel matching a ban like +b j:#channel from joining.
+# It is also possible to ban based on their status in that channel,
+# like so: +b j:@#channel, this example prevents the ops from joining.
 # 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="m_channelban.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Chanprotect module: Gives +q and +a channel modes.
-#
-# IMPORTANT: This module has been removed in the next major version of
-# InspIRCd. You should use m_customprefix instead.
-#<module name="m_chanprotect.so">
-
-#<chanprotect
-       # noservices: With this set to yes, when a user joins an empty channel,
-       # the server will set +q on them. If set to no, it will only set +o
-       # on them until they register the channel.
-       #noservices="no"
-
-       # qprefix: Prefix (symbol) to use for +q users.
-       #qprefix="~"
-
-       # aprefix: Prefix (symbol) to use for +a users.
-       #aprefix="&amp;"
-
-       # deprotectself: If this value is set (true, yes or 1), it will allow
-       # +a and +q users to remove the +a and +q from themselves, otherwise,
-       # the status will have to be removed by services.
-       #deprotectself="yes"
-
-       # deprotectothers: If this value is set to yes, true, or 1, then any
-       # user with +q or +a may remove the +q or +a from other users.
-       #deprotectothers="yes">
-
+# channel names. To disallow them, load the channames module and
+# add characters 42 and 63 to denyrange (see above).
+#<module name="channelban">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Check module: Adds the /CHECK command.
 # 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.
 # 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  #
 # 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.
 # 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. Wildcards are accepted.
+#<module name="classban">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Clear chan module: Allows opers to masskick, masskill or
+# mass G/Z-line all users on a channel using /CLEARCHAN.
+#<module name="clearchan">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Cloaking module: Adds usermode +x and cloaking support.
-# Relies on the module m_md5.so being loaded.
-# To cloak users when they connect, load m_conn_umodes and set
+# Cloaking module: Adds user mode +x and cloaking support.
+# 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 m_conn_umodes module for more information.
-#<module name="m_cloaking.so">
+# 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.                                   #
+# the network for consistent cloaking and must be at least thirty     #
+# characters long.                                                    #
 #                                                                     #
-# There are four methods of cloaking:                                 #
+# There are two methods of cloaking:                                  #
 #                                                                     #
-#   half           Cloak only the "unique" portion of a host; show    #
-#                  the last 2 parts of the domain, /16 subnet of IPv4 #
-#                  or /48 subnet of the IPv6 address.                 #
+#   half           Cloak only the "unique" portion of a host; by      #
+#                  default show the last 2 parts of the domain,       #
+#                  /16 subnet of IPv4 or /48 subnet of the IPv6       #
+#                  address.                                           #
+#                  To change the number of shown parts, modify the    #
+#                  domainparts option.                                #
 #                                                                     #
 #   full           Cloak the users completely, using three slices for #
 #                  common CIDR bans (IPv4: /16, /24; IPv6: /48, /64). #
 #                                                                     #
-# These methods use a single key that can be any length of text.      #
+# The methods use a single key that can be any length of text.        #
 # An optional prefix may be specified to mark cloaked hosts.          #
 #                                                                     #
-# The following methods are maintained for backwards compatibility;   #
-# they are slightly less secure, and always hide unresolved IPs.      #
-#                                                                     #
-#   compat-host    InspIRCd 1.2-compatible host-based cloaking.       #
-#   compat-ip      InspIRCd 1.2-compatible ip-always cloaking.        #
-#                                                                     #
-# If you use a compat cloaking mode then you must specify key1, key2, #
-# key3, key4; the values must be less than 0x80000000 and should be   #
-# picked at random. Prefix is mandatory, will default to network name #
-# if not specified, and will always have a "-" appended.              #
-#                                                                     #
-# IMPORTANT: The compat-host and compat-ip modes have been removed in #
-# the next major version of InspIRCd. You should ONLY use them if you #
-# need backwards compatibility with InspIRCd 1.2.                     #
+# IMPORTANT: Changing these details will break all of your existing   #
+# bans. If you do not want this to happen you can define multiple     #
+# cloak tags. The first will be used for cloaking and the rest will   #
+# be used for checking if a user is banned in a channel.              #
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 #
 #<cloak mode="half"
-#       key="secret"
+#       key="changeme"
+#       domainparts="3"
+#       prefix="net-">
+#
+#<cloak mode="full"
+#       key="changeme"
 #       prefix="net-">
-
-#-#-#-#-#-#-#-#-#-#-#-#- CLOSE MODULE #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Clones module: Adds an oper command /CLONES for detecting cloned
 # 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.
-#<module name="m_conn_join.so">
+# 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="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">
+# Join users to #chat after 15 seconds if they aren't on any channels.
+#<autojoin channel="#chat" delay="15">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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      #
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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
+# 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
 #
 # This allows for 10 connections in an hour with a 10 minute ban if
 # that is exceeded.
-#<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128">
+#<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128"
+# A custom ban message may optionally be specified.
+# banmessage="Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Connection throttle module.
-#<module name="m_connflood.so">
+#<module name="connflood">
 #
 #-#-#-#-#-#-#-#-#-#-#- CONNTHROTTLE CONFIGURATION  -#-#-#-#-#-#-#-#-#-#
-#  seconds, maxconns -  Amount of connections per <seconds>.
+#  period, maxconns -  Amount of connections per <period>.
 #
 #  timeout           -  Time to wait after the throttle was activated
 #                       before deactivating it. Be aware that the time
 #   quitmsg="Throttled" bootwait="10">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Custom prefixes: Allows for channel prefixes to be added.
-# This replaces m_chanprotect and m_halfop.
-#<module name="m_customprefix.so">
+# Custom prefixes: Allows for channel prefixes to be configured.
+#<module name="customprefix">
 #
 # name       The name of the mode, must be unique from other modes.
 # letter     The letter used for this mode. Required.
 # rank       A numeric rank for this prefix, defining what permissions it gives.
 #            The rank of voice, halfop and op is 10000, 20000, and 30000,
 #            respectively.
-# ranktoset  The numeric rank required to set/unset this mode. Defaults to rank.
+# ranktoset  The numeric rank required to set this mode. Defaults to rank.
+# ranktounset The numeric rank required to unset this mode. Defaults to ranktoset.
 # depriv     Can you remove the mode from yourself? Defaults to yes.
 #<customprefix name="founder" letter="q" prefix="~" rank="50000" ranktoset="50000">
 #<customprefix name="admin" letter="a" prefix="&amp;" rank="40000" ranktoset="50000">
 #<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.
+# You can also override the configuration of prefix modes added by both the core
+# and other modules by adding a customprefix tag with change="yes" specified.
+# <customprefix name="op" change="yes" rank="30000" ranktoset="30000">
+# <customprefix name="voice" change="yes" rank="10000" ranktoset="10000" depriv="no">
+#
+# 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.
 #
 #<title name="foo" password="bar" 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="fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" hash="sha256" title="Official Chat Helper">
+#<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.
 #<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">
+# Deaf module: Adds support for user modes +d and +D:
+# d - deaf to channel messages and notices.
+# D - deaf to user messages and notices.
+# The +D user mode is not enabled by default to enable link compatibility
+# with 2.0 servers.
+#<module name="deaf">
+#
+#-#-#-#-#-#-#-#-#-#-#-#-  DEAF CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
+#  bypasschars       - Characters that bypass deaf to a regular user.
+#  bypasscharsuline  - Characters that bypass deaf to a U-lined user (services).
+#                      Both of these take a list of characters that must match
+#                      the starting character of a message.
+#                      If 'bypasscharsuline' is empty, then 'bypasschars' will
+#                      match for both regular and U-lined users.
+#  enableprivdeaf    - Whether to enable user mode +D (privdeaf).
+#  privdeafuline     - Whether U-lined users bypass user mode +D (privdeaf).
+#
+#<deaf bypasschars="" bypasscharsuline="!" enableprivdeaf="no" privdeafuline="yes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Delay join module: Adds the channel mode +D which delays all JOIN
 # 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.   #
 # Glob masks are accepted here also.                                  #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Devoice module: Let users devoice themselves using /DEVOICE #chan.
-#<module name="m_devoice.so">
+# Disable module: Provides support for disabling commands and modes.  #
+#<module name="disable">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- DISABLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+#                                                                     #
+# If you have the disable module loaded then you need to specify the  #
+# commands and modes that you want disabled. Users who have not fully #
+# connected yet are exempt from this module so you can e.g. disable   #
+# the NICK command but still allow users to connect to the server.    #
+#                                                                     #
+# commands - A space-delimited list of commands that can not be used  #
+#            by users. You can exempt server operators from this with #
+#            the servers/use-disabled-commands privilege.             #
+#                                                                     #
+# chanmodes - One or more channel modes that can not be added/removed #
+#             by users. You can exempt server operators from this     #
+#             with the servers/use-disabled-modes privilege.          #
+#                                                                     #
+# usermodes - One or more user modes that can not be added/removed by #
+#             users. You can exempt server operators from this with   #
+#             the servers/use-disabled-modes privilege.               #
+#                                                                     #
+# fakenonexistent - Whether to pretend that a disabled command/mode   #
+#                   does not exist when executed/changed by a user.   #
+#                   Defaults to no.                                   #
+#                                                                     #
+# notifyopers - Whether to send a notice to snomask `a` when a user   #
+#               is prevented from using a disabled command/mode.      #
+#               Defaults to no.                                       #
+#                                                                     #
+#<disabled commands="KICK TOPIC"                                      #
+#          chanmodes="kp"                                             #
+#          usermodes="iw"                                             #
+#          fakenonexistent="yes"                                      #
+#          notifyopers="no">                                          #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 docs page for m_dnsbl at   #
-# https://docs.inspircd.org/2/modules/dnsbl                           #
+# For configuration options please see the docs page for dnsbl at     #
+# https://docs.inspircd.org/3/modules/dnsbl                           #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Exempt channel operators module: Provides support for allowing      #
 # nonick, nonotice, regmoderated, stripcolor, and topiclock.          #
 # See <options:exemptchanops> in inspircd.conf.example for a more     #
 # detailed list of the restriction modes that can be exempted.        #
-# These are settable using /mode #chan +X <restriction>:<status>      #
-#<module name="m_exemptchanops.so">                                   #
+# These are settable using: /MODE #chan +X <restriction>:<status>     #
+#<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.                  #
-# pcre   - PCRE regexps, provided via m_regex_pcre, needs libpcre.    #
-# tre    - TRE regexps, provided via m_regex_tre, requires libtre.    #
-# posix  - POSIX regexps, provided via m_regex_posix, not available   #
+# 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 m_regex_stdlib, see comment   #
+# stdlib - stdlib regexps, provided via regex_stdlib, see comment     #
 #          at the <module> tag for info on availability.              #
 #                                                                     #
-#<filteropts engine="glob">                                           #
+# If notifyuser is set to no, the user will not be notified when      #
+# his/her message is blocked.                                         #
+#<filteropts engine="glob" notifyuser="yes">
+#                                                                     #
+# Your choice of regex engine must match on all servers network-wide. #
+#                                                                     #
+# To learn more about the configuration of this module, read          #
+# examples/filter.conf.example, which covers the various types of     #
+# filters and shows how to add exemptions.                            #
 #                                                                     #
-# Your choice of regex engine must match on all servers network-wide.
-#
-# You may specify specific channels that are exempt from being filtered:
-#<exemptfromfilter channel="#blah">
-#
 #-#-#-#-#-#-#-#-#-#-#-  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.                                                      #
+# <keyword> tags.                                                     #
 #                                                                     #
-#<include file="conf/examples/filter.conf.example">
+#<include file="examples/filter.conf.example">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
+# Flash Policy Daemon module: Allows Flash IRC clients (e.g. LightIRC)#
+# to connect. If no file is specified, it'll serve a default policy   #
+# allowing all IPs to connect to all plaintext IRC ports              #
+#<bind address="" port="8430" type="flashpolicyd">                    #
+#<flashpolicyd timeout="5" file="">                                   #
+#<module name="flashpolicyd">                                         #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# GeoIP module: Allows the server admin to match users by country code.
-# This module is in extras. Re-run configure with:
-# ./configure --enable-extras=m_geoip.cpp
-# and run make install, then uncomment this module to enable it.
-# This module requires GeoIP to be installed on your system,
-# use your package manager to find the appropriate packages
-# or check the InspIRCd docs page for this module.
-#<module name="m_geoip.so">
-#
-# 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
-# turkey:
-#
-# <connect deny="*" geoip="TR,RU">
-#
-# The country code must be in capitals and should be an ISO country
-# code such as TR, GB, or US. Unknown IPs (localhost, LAN IPs, etc)
-# will be assigned the country code "UNK". Since connect classes are
-# matched from top down, your deny classes must be above your allow
-# classes for them to match.
+# Real name ban: Implements two extended bans:                        #
+# 'a', which matches a n!u@h+realname mask like +b a:*!*@host+*real*  #
+# 'r', which matches a realname mask like +b r:*realname?here*        #
+#<module name="gecosban">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Geolocation ban module: Adds support for extban 'G' which matches   #
+# against the ISO 3166-1 alpha-2 codes for the countries that users   #
+# are connecting from. Users connecting from unknown origins such as  #
+# internal networks can be matched against using the XX alpha-2 code. #
+# A full list of ISO 3166-1 alpha-2 codes can be found at             #
+# https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2                    #
+#<module name="geoban">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Geolocation connect class module: Adds support for limiting connect #
+# classes to users from specific countries. With this module you can  #
+# specify a space-delimited list of two character the ISO 3166-1      #
+# alpha-2 codes in the "country" field of a connect class. e.g. to    #
+# deny connections from users in Russia or Turkey:                    #
+#                                                                     #
+# <connect deny="*" country="TR RU">                                  #
+#                                                                     #
+# Users connecting from unknown origins such as internal networks can #
+# be matched against using the XX alpha-2 code. A full list of ISO    #
+# 3166-1 alpha-2 codes can be found at                                #
+# https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2                    #
+#<module name="geoclass">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# MaxMindDB geolocation module: Provides geolocation information for  #
+# other modules that need it using the libMaxMindDB library.          #
+#                                                                     #
+# This module is in extras. Re-run configure with:                    #
+# ./configure --enable-extras=m_geo_maxmind.cpp
+# and run make install, then uncomment this module to enable it.      #
+#                                                                     #
+# This module requires libMaxMindDB to be installed on your system.   #
+# Use your package manager to find the appropriate packages or check  #
+# the InspIRCd documentation page for this module.                    #
+#<module name="geo_maxmind">
+#                                                                     #
+# If you use the geo_maxmind module you MUST provide a database file  #
+# to look up geolocation information in. You can either purchase this #
+# from MaxMind at https://www.maxmind.com/en/geoip2-country-database  #
+# or use the free CC-BY-SA licensed GeoLite2 Country database which   #
+# can be downloaded at https://dev.maxmind.com/geoip/geoip2/geolite2/ #
+#<maxmind file="GeoLite2-Country.mmdb">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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-
 # 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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Halfop module: Provides the +h (halfops) channel status mode.
-#
-# IMPORTANT: This module has been removed in the next major version of
-# InspIRCd. You should use m_customprefix instead.
-#<module name="m_halfop.so">
+# HAProxy module: Adds support for the HAProxy PROXY v2 protocol. To
+# use this module specify hook="haproxy" in the <bind> tag that HAProxy
+# has been configured to connect to.
+#<module name="haproxy">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# HELPOP module: Provides the /HELPOP command.
-#<module name="m_helpop.so">
+# HELPOP module: Provides the /HELPOP command
+#<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.                                   #
-#<include file="conf/examples/helpop-full.conf.example">
+# If you specify to use the helpop module, then specify below the     #
+# path to the helpop.conf file.                                       #
+#                                                                     #
+#<include file="examples/helpop.conf.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.
 # This setting is not recommended for most mainstream networks.
 #<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 mode module: Allows for hiding mode changes from users who do not
+# have sufficient channel privileges.
+#<module name="hidemode">
+#
+# Hide bans (+b) from people who are not voiced:
+#<hidemode mode="ban" rank="10000">
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 https://docs.inspircd.org/2/modules/hostchange for help.        #
+# See https://docs.inspircd.org/3/modules/hostchange for help.        #
 #                                                                     #
-#<host suffix="invalid.org" separator="." prefix="">
-#<hostchange mask="*@42.theanswer.example.org" action="addnick">
-#<hostchange mask="*root@*" action="suffix">
+#<hostchange mask="*@42.theanswer.example.org" action="addaccount" suffix=".users.example.com">
+#<hostchange mask="*root@*" action="addnick" prefix="example/users/">
 #<hostchange mask="a@example.com" action="set" value="foo.bar.baz">
-#<hostchange mask="localhost" ports="7000,7001,7005-7007" action="set" value="blahblah.foo">
+#<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.
+# 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.
 # <bind address="127.0.0.1" port="8067" type="httpd">
 # <bind address="127.0.0.1" port="8097" type="httpd" ssl="gnutls">
 #
 # You can adjust the timeout for HTTP connections below. All HTTP
-# connections will be closed after (roughly) this many seconds.
+# connections will be closed after (roughly) this time period.
 #<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="secrets" password="mypasshere" whitelist="127.0.0.*,10.*">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # HTTP config module: Allows the server configuration to be viewed over
-# HTTP via the /config path. Requires m_httpd.so to be loaded for it to
-# function.
+# HTTP via the /config path. Requires the httpd module to be loaded for
+# it to function.
 #
 # IMPORTANT: This module exposes extremely sensitive information about
 # your server and users so you *MUST* protect it using a local-only
-# <bind> tag and/or the m_httpd_acl.so module. See above for details.
-#<module name="m_httpd_config.so">
+# <bind> tag and/or the httpd_acl module. See above for details.
+#<module name="httpd_config">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # HTTP stats module: Provides server statistics over HTTP via the /stats
-# path. Requires m_httpd.so to be loaded for it to function.
+# path. Requires the httpd module to be loaded for it to function.
 #
 # IMPORTANT: This module exposes extremely sensitive information about
 # your server and users so you *MUST* protect it using a local-only
-# <bind> tag and/or the m_httpd_acl.so module. See above for details.
-#<module name="m_httpd_stats.so">
+# <bind> tag and/or the httpd_acl module. See above for details.
+#<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.      #
-# The bind value indicates which IP to bind outbound requests to.     #
+# 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.               #
+# prefixunqueried: If on, the idents of users being in a connect class#
+# with ident lookups disabled (i.e. <connect useident="off">) will be #
+# prefixed with a "~". If off, the ident of those users will not be   #
+# prefixed. Default is off.                                           #
 #
-#<ident timeout="5">
+#<ident timeout="5" prefixunqueried="no">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Invite exception module: Adds support for channel invite exceptions
 # (+I).
-#<module name="m_inviteexception.so">
+#<module name="inviteexception">
 # bypasskey: If this is enabled, exceptions will bypass +k as well as +i
 #<inviteexception bypasskey="yes">
 
 # 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.net/irc/
+# https://ircv3.net/irc/
 #
-#<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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
+# IRCv3 account-tag module. Adds the 'account' tag which contains the
+# services account name of the message sender.
+#<module name="ircv3_accounttag">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Jump server module: Adds support for the RPL_REDIR numeric.
-# This module is oper-only.
-# 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">
+# IRCv3 batch module: Provides the batch IRCv3 extension which allows
+# the server to inform a client that a group of messages are related to
+# each other.
+#<module name="ircv3_batch">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 cap-notify module: Provides the cap-notify IRCv3 extension.
+# Required for IRCv3 conformance.
+#<module name="ircv3_capnotify">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 chghost module: Provides the chghost IRCv3 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 client-to-client tags module: Provides the message-tags IRCv3
+# extension which allows clients to add extra data to their messages.
+# This is used to support new IRCv3 features such as replies and ids.
+#<module name="ircv3_ctctags">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 echo-message module: Provides the echo-message IRCv3
+# 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
+# extension which notifies supporting clients when a user invites
+# another user into a channel. This respects <options:announceinvites>.
+#<module name="ircv3_invitenotify">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 server-time module. Adds the 'time' tag which adds a timestamp
+# to all messages received from the server.
+#<module name="ircv3_servertime">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# IRCv3 Strict Transport Security module: Provides the sts IRCv3
+# extension which allows clients connecting insecurely to upgrade their
+# connections to TLS.
+#<module name="ircv3_sts">
+#
+# If using the ircv3_sts module you MUST define a STS policy to send
+# to clients using the <sts> tag. This tag takes the following
+# attributes:
+#
+# host     - A glob match for the SNI hostname to apply this policy to.
+# duration - The amount of time that the policy lasts for. Defaults to
+#            approximately two months by default.
+# port     - The port on which TLS connections to the server are being
+#            accepted. You MUST have a CA-verified certificate on this
+#            port. Self signed certificates are not acceptable.
+# preload  - Whether client developers can include your certificate in
+#            preload lists.
+#
+# <sts host="*.example.com" duration="60d" port="6697" preload="yes">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Join flood module: Adds support for join flood protection +j X:Y.
+# 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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Anti auto rejoin: Adds support for prevention of auto-rejoin (+J).
-#<module name="m_kicknorejoin.so">
-# Set the maximum time that is accepted as a parameter for +J here.
-#<kicknorejoin maxtime="1m">
+#<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.
 # If set to "both" then (surprise!) both will be sent.
 #<knock notify="notice">
 
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# LDAP module: Allows other SQL modules to access a LDAP database
+# through a unified API.
+# This modules is in extras. Re-run configure with:
+# ./configure --enable-extras=m_ldap.cpp
+# and run make install, then uncomment this module to enable it.
+#
+#<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.       #
+#                                                                     #
+# The binddn and bindauth indicate the DN to bind to for searching,   #
+# and the password for the distinguished name. Some LDAP servers will #
+# allow anonymous searching in which case these two values do not     #
+# need defining, otherwise they should be set similar to the examples #
+# above.                                                              #
+#                                                                     #
+# The searchscope value indicates the subtree to search under. On our #
+# test system this is 'subtree'. Your mileage may vary.               #
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # LDAP authentication module: Adds the ability to authenticate users  #
-# via LDAP. This is an extra module which must be enabled explicitly  #
-# by symlinking it from modules/extra, and requires the OpenLDAP libs #
-# This module is in extras. To enable it, Re-run configure with:      #
-# ./configure --enable-extras=m_ldapauth.cpp                          #
-# and run make install, then uncomment this module.                   #
-#<module name="m_ldapauth.so">
+# via LDAP.                                                           #
+#<module name="ldapauth">
 #                                                                     #
 # Configuration:                                                      #
 #                                                                     #
-# <ldapauth baserdn="ou=People,dc=brainbox,dc=cc"                     #
+# <ldapauth dbid="ldapdb"                                             #
+#           baserdn="ou=People,dc=brainbox,dc=cc"                     #
 #           attribute="uid"                                           #
-#           server="ldap://brainwave.brainbox.cc"                     #
-#           allowpattern="Guest*"                                     #
+#           allowpattern="Guest* Bot*"                                #
 #           killreason="Access denied"                                #
-#           searchscope="subtree"                                     #
-#           binddn="cn=Manager,dc=brainbox,dc=cc"                     #
-#           bindauth="mysecretpass"                                   #
 #           verbose="yes"                                             #
 #           host="$uid.$ou.inspircd.org"                              #
 #           useusername="no">                                         #
 # The attribute value indicates the attribute which is used to locate #
 # a user account by name. On POSIX systems this is usually 'uid'.     #
 #                                                                     #
+# The allowpattern value allows you to specify a space separated list #
+# of wildcard masks which will always be allowed to connect           #
+# 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.                                                   #
 #                                                                     #
-# The server parameter indicates the LDAP server to connect to. The   #
-# ldap:// style scheme before the hostname proper is MANDATORY.       #
-#                                                                     #
-# The allowpattern value allows you to specify a wildcard mask which  #
-# will always be allowed to connect regardless of if they have an     #
-# account, for example guest users.                                   #
-#                                                                     #
 # Killreason indicates the QUIT reason to give to users if they fail  #
 # to authenticate.                                                    #
 #                                                                     #
-# The searchscope value indicates the subtree to search under. On our #
-# test system this is 'subtree'. Your mileage may vary.               #
-#                                                                     #
 # Setting the verbose value causes an oper notice to be sent out for  #
 # every failed authentication to the server, with an error string.    #
 #                                                                     #
-# The binddn and bindauth indicate the DN to bind to for searching,   #
-# and the password for the distinguished name. Some LDAP servers will #
-# allow anonymous searching in which case these two values do not     #
-# need defining, otherwise they should be set similar to the examples #
-# above.                                                              #
-#                                                                     #
 # ldapwhitelist indicates that clients connecting from an IP in the   #
 # provided CIDR do not need to authenticate against LDAP. It can be   #
 # repeated to whitelist multiple CIDRs.                               #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # LDAP oper configuration module: Adds the ability to authenticate    #
-# opers via LDAP. This is an extra module which must be enabled       #
-# explicitly by symlinking it from modules/extra, and requires the    #
-# OpenLDAP libs. Re-run configure with:                               #
-# ./configure --enable-extras=m_ldapoper.cpp
-# and run make install, then uncomment this module to enable it.      #
-#<module name="m_ldapoper.so">
+# opers via LDAP.                                                     #
+#<module name="ldapoper">
 #                                                                     #
 # Configuration:                                                      #
 #                                                                     #
-# <ldapoper baserdn="ou=People,dc=brainbox,dc=cc"
-#           server="ldap://brainwave.brainbox.cc"
-#           searchscope="subtree"
-#           binddn="cn=Manager,dc=brainbox,dc=cc"
-#           bindauth="mysecretpass"
+# <ldapoper dbid="ldapdb"
+#           baserdn="ou=People,dc=brainbox,dc=cc"
 #           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.     #
 # 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://test.org/map" in   #
+# message to see a website, set by maphide="https://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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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 docs for more   #
-# info https://docs.inspircd.org/2/modules/mssql                      #
+# Modenotice module: Adds the /MODENOTICE command that allows opers to
+# send notices to all users having the given user mode(s) set.
+#<module name="modenotice">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# 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
 # 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 docs for    #
-# more: https://docs.inspircd.org/2/modules/mysql                     #
+# mysql is more complex than described here, see the docs for more    #
+# info: https://docs.inspircd.org/3/modules/mysql                     #
 #
 #<database module="mysql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database2">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Named modes module: Allows for the display and set/unset of channel
 # 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
+# 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">
+#<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"
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 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">
+# No CTCP module: Adds the channel mode +C and user mode +T to block
+# CTCPs and extban 'C' to block CTCPs sent by specific users.
+#<module name="noctcp">
+#
+# The +T user mode is not enabled by default to enable link compatibility
+# with 2.0 servers. You can enable it by uncommenting this:
+#<noctcp enableumode="yes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">
+# channel which makes them immune to kicks.
+#<module name="ojoin">
 #
 # Specify the prefix that +Y will grant here.
 # Leave 'prefix' empty if you do not wish +Y to grant a prefix.
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Oper channels mode: Adds the +O channel mode and extban O:<mask>
 # to ban, except, etc. specific oper types. For example
-# /mode #channel +iI O:* is equivalent to channel mode +O, but you
+# /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".       #
 # 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.
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Oper prefixing module: Adds a channel prefix mode +y which is given
-# to all IRC operators automatically on all channels they are in.
+# to all server 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="m_operprefix.so">
+# Load this module if you want all your server operators to have
+# channel operator powers.
+#<module name="operprefix">
 #
 # You may additionally customise the prefix character.
 #<operprefix prefix="!">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Oper MOTD module: Provides support for separate message of the day
+# 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 file here.   #
 #                                                                     #
 # onoper        - If on, the message is sent on /OPER, otherwise it's #
 #                 only sent when /OPERMOTD is used.                   #
 #                                                                     #
-# processcolors - Allow color codes to be processed in the opermotd.  #
-#                 Read the comment above <connect:allowmotdcolors> in #
-#                 inspircd.conf.example for details.                  #
-#                                                                     #
-#<opermotd file="conf/examples/opermotd.txt.example" onoper="yes" processcolors="false">
+#<opermotd file="examples/opermotd.txt.example" onoper="yes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 docs:        #
-# https://docs.inspircd.org/2/modules/override                        #
+# Much of override's configuration relates to your oper blocks.       #
+# For more information on how to allow opers to override, see:        #
+# https://docs.inspircd.org/3/modules/override                        #
+#                                                                     #
+# noisy         - If enabled, all oper overrides will be announced    #
+#                 via channel notice.                                 #
+#                                                                     #
+# requirekey    - If enabled, overriding on join requires a channel   #
+#                 key of "override" to be specified.                  #
+#                                                                     #
+# enableumode   - If enabled, user mode +O is required for override.  #
+#                                                                     #
+#<override noisy="yes" requirekey="no" enableumode="true">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Oper levels module: Gives each oper a level and prevents actions
 # 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.
 
                # cmd: Command for the user to run when it receives a connect
                # password.
-               cmd="PRIVMSG $nickrequired :IDENTIFY $pass">
+               cmd="SQUERY $nickrequired :IDENTIFY $pass">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 #-#-#-#-#-#-#-#-#-#-#-#
 #
 #
 #     <oper name="Brain"
 #           host="ident@dialup15.isp.test.com"
-#           hash="sha256"
-#           password="01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
+#           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
 # 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.
 #
 # If 'listmodes' is true then all list modes (+b, +I, +e, +g...) will be
 # saved. Defaults to false.
-#<permchanneldb filename="data/permchannels.conf" listmodes="true">
-#<include file="data/permchannels.conf">
+#<permchanneldb filename="permchannels.conf" listmodes="true">
+#<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.">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 docs for    #
-# more: https://docs.inspircd.org/2/modules/pgsql                     #
+# pgsql is more complex than described here, see the docs for         #
+# more: https://docs.inspircd.org/3/modules/pgsql                     #
 #
 #<database module="pgsql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database" ssl="no">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">
+#
+# If notifyuser is set to no, the user will not be notified when
+# his/her message is blocked.
+#<muteban notifyuser="yes">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Redirect module: Adds channel redirection mode +L.                  #
-# Optional: <redirect:antiredirect> to add usermode +L to stop forced #
-# redirection and instead print an error.                             #
-#                                                                     #
-# Note: You can not update this with a simple rehash, it requires     #
-# reloading the module for it to take effect.                         #
-# 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">
-#<redirect antiredirect="true">
+# Redirect module: Adds channel mode +L which redirects users to      #
+# another channel when the channel has reached its user limit and     #
+# user mode +L which stops redirection.                               #
+#<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
+# You must have at least 1 provider loaded to use the filter or R-line
 # 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
+# R-line 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="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 the filter or R-line modules.
 # 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.
 # 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).
 # 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
-# registered users and opers can create new channels.
+# Remove module: Adds the /REMOVE command which is a peaceful
+# alternative to /KICK. It also provides the /FPART command which works
+# in the same way as /REMOVE.
+#<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">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Repeat module: Allows to block, kick or ban upon similar messages
+# being uttered several times. Provides channel mode +E.
+#
+# Syntax: [~|*]<lines>:<sec>[:<difference>][:<backlog>]
+#         ~ 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.
+# seconds    - How old the message has to be before it's invalidated.
+# difference - Edit distance, in percent, between two strings to trigger on.
+# backlog    - When set, the function goes into mode 2. In this mode the
+#              function will trigger if this many of the last <lines> matches.
+#
+# As this module can be rather CPU-intensive, it comes with some options.
+# maxbacklog  - Maximum size that can be specified for backlog. 0 disables
+#               multiline matching.
+# maxdistance - Max percentage of difference between two lines we'll allow
+#               to match. Set to 0 to disable edit-distance matching.
+# maxlines    - Max lines of backlog to match against.
+# maxtime     - Maximum period of time a user can set. 0 to allow any.
+# size        - Maximum number of characters to check for, can be used to
+#               truncate messages 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" maxdistance="50" maxlines="20" maxtime="0" size="512">
+#<module name="repeat">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Restricted channels module: Allows only opers with the
+# channels/restricted-create priv and/or registered users to
+# create channels.
 #
 # You probably *DO NOT* want to load this module on a public network.
 #
-#<module name="m_regonlycreate.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Remove module: Adds the /REMOVE command which is a peaceful
-# alternative to /KICK.
-#<module name="m_remove.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Restricted channels module: Allows only opers to create channels.
+#<module name="restrictchans">
 #
-# You probably *DO NOT* want to load this module on a public network.
+# allowregistered: should registered users be allowed to bypass the restrictions?
+#<restrictchans allowregistered="no">
 #
-#<module name="m_restrictchans.so">
+# Allow any channel matching #user-* to be created, bypassing restrictchans checks
+#<allowchannel name="#user-*">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">
-#
-# Uncomment this to allow users to message ulines (e.g. services):
-#<restrictmsg uline="yes">
+#<module name="restrictmsg">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# R-Line module: Ban users through regular expression patterns.
-#<module name="m_rline.so">
+# R-line module: Ban users through regular expression patterns.
+#<module name="rline">
 #
 #-#-#-#-#-#-#-#-#-#-#-#- RLINE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
 #
 # If you wish to re-check a user when they change nickname (can be
 # useful under some situations, but *can* also use CPU with more users
 # on a server) then set 'matchonnickchange' to yes.
+# If you additionally want Z-lines to be added on matches, then
+# set 'zlineonmatch' to yes.
 # 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
+# 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
+# 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">
+#<rline matchonnickchange="yes" zlineonmatch="no" engine="pcre">
 #
-# Generally, you will NOT want to use 'glob' here, as this turns
-# rline into just another gline. The exceptions are that rline will
+# Generally, you will NOT want to use 'glob' here, as this turns an
+# R-line into just another G-line. The exceptions are that R-lines will
 # always use the full "nick!user@host realname" string, rather than only
 # user@host, but beware that only the ? and * wildcards are available,
 # and are the only way to specify where the space can occur if you do
 # use glob. For this reason, is recommended to use a real regex engine
 # so that at least \s or [[:space:]] is available.
 
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# RMODE module: Adds the /RMODE command.
+# Allows channel operators to remove list modes en masse, optionally
+# matching a glob-based pattern.
+# Syntax: /RMODE <channel> <mode> [<pattern>]
+# E.g. '/RMODE #channel b m:*' will remove all mute extbans on the channel.
+#<module name="rmode">
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # SAJOIN module: Adds the /SAJOIN command which forcibly joins a user
 # to the given channel.
 # This module is oper-only.
 # To use, SAJOIN must be in one of your oper class blocks.
-#<module name="m_sajoin.so">
+# Opers need the users/sajoin-others priv to be able to /SAJOIN users
+# other than themselves.
+#<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
 # 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">
 # Define the following to your services server name to improve security
 # by ensuring the SASL messages are only sent to the services server
 # and not to all connected servers. This prevents a rogue server from
-# capturing SASL messages.
+# capturing SASL messages and disables the SASL cap when services is
+# down.
 #<sasl target="services.mynetwork.com">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 # Define the following variable to change how long a user must wait   #
 # before issuing a LIST. If not defined, defaults to 60 seconds.      #
 #                                                                     #
-#<securelist waittime="60">                                           #
+#<securelist waittime="1m">                                           #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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.
+# Services support module: Adds several user modes such as +R and +M.
 # This module implements the 'identified' state via account names,
 # and is similar in operation to the way asuka and ircu handle services.
 #
 # +b R: (stop matching account names from joining)
 # +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">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- SETNAME CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+#                                                                     #
+# operonly - Whether the SETNAME command should only be usable by     #
+#            server operators. Defaults to no.                        #
+#                                                                     #
+# notifyopers - Whether to send a snotice to snomask `a` when a user  #
+#               changes their real name. Defaults to to yes if        #
+#               oper-only and no if usable by everyone.               #
+#                                                                     #
+#<setname notifyopers="yes"
+#         operonly="no">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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">
+# Wildcards are accepted.
+#<module name="serverban">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SHA1 module: Allows other modules to generate SHA1 hashes.
+# Required by the WebSocket module.
+#<module name="sha1">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Show whois module: Adds the +W usermode which allows opers to see
+# 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="showfile">
+#                                                                     #
+#-#-#-#-#-#-#-#-#-#-# SHOWFILE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
+#                                                                     #
+# name    - The name of the command which displays this file. This is #
+#           the only mandatory setting, all others are optional.      #
+# file    - The text file to be shown to the user.                    #
+#           By default same as the command name.                      #
+# method  - How should the file be shown?                             #
+#           * numeric: Send contents using a numeric                  #
+#             (similar to /MOTD; the default).                        #
+#           * notice:  Send contents as a series of notices.          #
+#           * msg:     Send contents as a series of private messages. #
+#                                                                     #
+# When using the method "numeric", the following extra settings are   #
+# available:                                                          #
+#                                                                     #
+# introtext    - Introductory line, "Showing <name>" by default.      #
+# intronumeric - Numeric used for the introductory line.              #
+# numeric      - Numeric used for sending the text itself.            #
+# endtext      - Ending line, "End of <name>" by default.             #
+# endnumeric   - Numeric used for the ending line.                    #
+#                                                                     #
+#<showfile name="RULES"
+#          file="rules.txt"
+#          introtext="Server rules:"
+#          endtext="End of server rules.">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Show whois module: Adds the +W user mode 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. This setting
-# is not reloadable via /REHASH, changing it requires /RELOADMODULE.
+# users/auspex priv will see real hosts of people, though.
 #<showwhois opersonly="yes"
 #
 # You may also set whether or not users should receive whois notices,
 # 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
 #<shun enabledcommands="ADMIN PING PONG QUIT PART JOIN" notifyuser="yes" affectopers="no">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# SSL channel mode module: Adds support for SSL-only channels via
-# channel mode +z and the 'z' extban which matches SSL client
-# certificate fingerprints.
+# SSL mode module: Adds support for SSL-only channels via the '+z'
+# channel mode, SSL-only private messages via the '+z' user mode and
+# the 'z:' extban which matches SSL client certificate fingerprints.
+#
 # Does not do anything useful without a working SSL module and the
-# m_sslinfo module (see below).
-#<module name="m_sslmodes.so">
+# sslinfo module (see below).
+#<module name="sslmodes">
+#
+# The +z user mode is not enabled by default to enable link compatibility
+# with 2.0 servers. You can enable it by uncommenting this:
+#<sslmodes enableumode="yes">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# SSL rehash signal module: Allows the SSL modules to be rehashed by
+# sending SIGUSR1 to a running InspIRCd process.
+# This modules is in extras. Re-run configure with:
+# ./configure --enable-extras=m_sslrehashsignal.cpp
+# and run make install, then uncomment this module to enable it.
+#<module name="sslrehashsignal">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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 docs:      #
-# https://docs.inspircd.org/2/modules/ssl_gnutls                      #
+# ssl_gnutls is too complex to describe here, see the docs:           #
+# https://docs.inspircd.org/3/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 docs:     #
-# https://docs.inspircd.org/2/modules/ssl_openssl                     #
+# ssl_openssl is too complex to describe here, see the docs:          #
+# https://docs.inspircd.org/3/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          #
 # ./configure --enable-extras=m_sqlite3.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 docs for   #
-# more: https://docs.inspircd.org/2/modules/sqlite3                   #
+# sqlite is more complex than described here, see the docs for more   #
+# info: https://docs.inspircd.org/3/modules/sqlite3                   #
 #
 #<database module="sqlite" hostname="/full/path/to/database.db" id="anytext">
 
 # SQL authentication module: Allows IRCd connections to be tied into
 # a database table (for example a forum).
 #
-#<module name="m_sqlauth.so">
+#<module name="sqlauth">
 #
 #-#-#-#-#-#-#-#-#-#-#- SQLAUTH CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
-# m_sqlauth.so is too complex to describe here, see the docs:         #
-# https://docs.inspircd.org/2/modules/sqlauth                         #
+# sqlauth is too complex to describe here, see the docs:              #
+# https://docs.inspircd.org/3/modules/sqlauth                         #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# SQL oper module: Allows you to store oper credentials in an SQL table
+# SQL oper module: Allows you to store oper credentials in an SQL
+# table. You can add additional table columns like you would config
+# tags in opers.conf. Opers in opers.conf will override opers from
+# this module.
 #
-#<module name="m_sqloper.so">
+#<module name="sqloper">
 #
 #-#-#-#-#-#-#-#-#-#-#- SQLOPER CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 # dbid       - Database ID to use (see SQL modules).                  #
-# hash       - Hashing provider to use for password hashing.          #
 #                                                                     #
-# See also: https://docs.inspircd.org/2/modules/sqloper               #
+# See also: https://docs.inspircd.org/3/modules/sqloper               #
 #                                                                     #
-#<sqloper dbid="1" hash="md5">
+#<sqloper dbid="1">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# StartTLS module: Implements STARTTLS, which allows clients          #
+# connected to non SSL enabled ports to enable SSL, if a proper SSL   #
+# module is loaded (either ssl_gnutls or ssl_openssl).                #
+#<module name="starttls">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# SVSHold module: Implements SVSHOLD. Like Q:Lines, but can only be   #
+# SVSHold module: Implements SVSHOLD. Like Q-lines, but can only be   #
 # added/removed by Services.                                          #
-#<module name="m_svshold.so">
-# If silent is true no snotices will be generated by SVSHOLD.
+#<module name="svshold">
+# SVSHOLD does not generate server notices by default, you can turn
+# notices on by uncommenting the next line.
 #<svshold silent="false">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # 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
 # 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   -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 # 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.test.cc">
-#<vhost user="foo" password="fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" hash="sha256" host="some.other.host.example.com">
+#<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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# XLine database: Stores all *Lines (G/Z/K/R/any added by other modules)
+# 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">
+#
+# Whether to re-encode messages as UTF-8 before sending to WebSocket
+# clients. This is recommended as the WebSocket protocol requires all
+# text frames to be sent as UTF-8. If you do not have this enabled
+# messages will be sent as binary frames instead.
+#<websocket sendastext="yes">
+#
+# If you use the websocket module you MUST specify one or more origins
+# which are allowed to connect to the server. You should set this as
+# strict as possible to prevent malicious webpages from connecting to
+# your server.
+# <wsorigin allow="https://*.example.com/">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# X-line 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">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 #    ____                _   _____ _     _       ____  _ _   _        #
 #   |  _ <  __/ (_| | (_| |   | | | | | | \__ \ | |_) | | |_|_|       #
 #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
 #                                                                     #
-# 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.                                 #
 
 # 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
deleted file mode 100644 (file)
index 39238ec..0000000
+++ /dev/null
@@ -1,302 +0,0 @@
-<module name="m_md5.so">
-<module name="m_sha256.so">
-<module name="m_alias.so">
-<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">
-<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">
-<callerid maxaccepts="16"
-          operoverride="no"
-          tracknick="no"
-          cooldown="60">
-
-<module name="m_cap.so">
-<module name="m_cban.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# CGI:IRC module: Adds support for automatic host changing in CGI:IRC
-# (http://cgiirc.sourceforge.net).
-#<module name="m_cgiirc.so">
-#
-#-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
-#
-# Optional - If you specify to use m_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: https://docs.inspircd.org/2/modules/cgiirc
-#
-# Set to yes if you want to notice opers when CGI clients connect
-# <cgiirc opernotice="no">
-#
-# The type field indicates where the module should get the real
-# client's IP address from, for further information, please see the
-# 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
-# New style:
-# <cgihost type="webirc" password="foobar"
-#   mask="somebox.mysite.com">                      # Get IP from WEBIRC
-#
-# IMPORTANT NOTE:
-# ---------------
-#
-# When you connect CGI:IRC clients, there are two connect classes which
-# apply to these clients. When the client initially connects, the connect
-# class which matches the cgi:irc site's host is checked. Therefore you
-# must raise the maximum local/global clients for this ip as high as you
-# want to allow cgi clients. After the client has connected and is
-# determined to be a cgi:irc client, the class which matches the client's
-# real IP is then checked. You may set this class to a lower value, so that
-# the real IP of the client can still be restricted to, for example, 3
-# sessions maximum.
-#
-
-<module name="m_chancreate.so">
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-
-<channames
-       # denyrange: characters or range of characters to deny in channel
-       # names.
-       denyrange="2"
-
-       # allowrange: characters or range of characters to specifically allow
-       # in channel names.
-       allowrange="">
-
-<module name="m_channelban.so">
-<module name="m_chghost.so">
-<hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
-<module name="m_chgident.so">
-<module name="m_chgname.so">
-<module name="m_cloaking.so">
-#
-#-#-#-#-#-#-#-#-#-#-#- CLOAKING  CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# If you specify the m_cloaking.so 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.        #
-#                                                                     #
-# There are four methods of cloaking:                                 #
-#                                                                     #
-#   half           Cloak only the "unique" portion of a host; show    #
-#                  the last 2 parts of the domain, /16 subnet of IPv4 #
-#                  or /48 subnet of the IPv6 address.                 #
-#                                                                     #
-#   full           Cloak the users completely, using three slices for #
-#                  common CIDR bans (IPv4: /16, /24; IPv6: /48, /64)  #
-#                                                                     #
-# These methods use a single key that can be any length of text.      #
-# An optional prefix may be specified to mark cloaked hosts.          #
-#                                                                     #
-# The following methods are maintained for backwards compatibility;   #
-# they are slightly less secure, and always hide unresolved IPs       #
-#                                                                     #
-#   compat-host    InspIRCd 1.2-compatible host-based cloaking        #
-#   compat-ip      InspIRCd 1.2-compatible ip-always cloaking         #
-#                                                                     #
-# You must specify key1, key2, key3, key4 for the compat cloaking     #
-# modes; the values must be less than 0x80000000 and should be picked #
-# at random. Prefix is mandatory, will default to network name if not #
-# specified, and will always have a "-" appended.                     #
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-#
-<cloak mode="half"
-       key="secret"
-       prefix="net-">
-
-<module name="m_close.so">
-<module name="m_conn_umodes.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Connectban: Provides IP connection throttling. Any IP range that connects
-# too many times (configurable) in an hour is zlined for a (configurable)
-# duration, and their count resets to 0.
-#
-# ipv4cidr and ipv6cidr allow you to turn the comparison from individual
-# IP addresses (32 and 128 bits) into CIDR masks, to allow for throttling
-# over whole ISPs/blocks of IPs, which may be needed to prevent attacks.
-#
-#<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">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Connection throttle module.
-#<module name="m_connflood.so">
-#
-#-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
-#  seconds, maxconns -  Amount of connections per <seconds>.
-#
-#  timeout           -  Time to wait after the throttle was activated
-#                       before deactivating it. Be aware that the time
-#                       is seconds + timeout.
-#
-#  quitmsg           -  The message that users get if they attempt to
-#                       connect while the throttle is active.
-#
-#  bootwait          -  Amount of time to wait before enforcing the
-#                       throttling when the server just booted.
-#
-#<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">
-<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">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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.
-# On POSIX-compliant systems, regex syntax can be found by using the
-# command: 'man 7 regex'.
-#<module name="m_regex_posix.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Registered users only channel creation
-# Allows only registered users and opers to create new channels.
-#
-# You probably *DO NOT* want to load this module on a public network.
-#
-#<module name="m_regonlycreate.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Ban users through regular expression patterns
-#<module name="m_rline.so">
-#
-#-#-#-#-#-#-#-#-#-#-#-#- RLINE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
-#
-# If you wish to re-check a user when they change nickname (can be
-# useful under some situations, but *can* also use CPU with more users
-# on a server) then set the following configuration value:
-# 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
-# until you load it or change the engine to one that is loaded.
-#
-#<rline matchonnickchange="yes" engine="pcre">
-#
-# Generally, you will NOT want to use 'glob' here, as this turns
-# rline into just another gline. The exceptions are that rline will
-# always use the full nick!user@host realname string, rather than only
-# user@host, but beware that only the ? and * wildcards are available,
-# and are the only way to specify where the space can occur if you do
-# 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">
-<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
-# certificate fingerprints.
-# Does not do anything useful without a working SSL module (see below).
-#<module name="m_sslmodes.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-#
-#-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# m_ssl_gnutls.so is too complex to describe here, see the docs:      #
-# https://docs.inspircd.org/2/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
-# "* <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.
-# 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">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-#
-#-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# m_ssl_openssl.so is too complex to describe here, see the docs:     #
-# https://docs.inspircd.org/2/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">
-<watch maxentries="32">
-<module name="m_xline_db.so">
-
-<module name="m_spanningtree.so">
diff --git a/docs/conf/modules/unrealircd.conf.example b/docs/conf/modules/unrealircd.conf.example
deleted file mode 100644 (file)
index 1d2ea39..0000000
+++ /dev/null
@@ -1,399 +0,0 @@
-<module name="m_md5.so">
-<module name="m_sha256.so">
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Alias module: Allows you to define server-side command aliases.
-<module name="m_alias.so">
-<fantasy prefix="!" allowbots="no">
-# Aliases
-<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.
-#
-#<alias text="ID" format="#*" replace="PRIVMSG ChanServ :IDENTIFY $2 $3"
-#  requires="ChanServ" uline="yes">
-#
-#<alias text="ID" replace="PRIVMSG NickServ :IDENTIFY $2"
-#  requires="NickServ" uline="yes">
-#
-# 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.
-#
-#<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">
-<auditorium showops="yes" operoverride="yes">
-<module name="m_banexception.so">
-<module name="m_blockcaps.so">
-<blockcaps percent="50"
-           minlen="5"
-           capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! ">
-<module name="m_blockcolor.so">
-<module name="m_botmode.so">
-<module name="m_censor.so">
-<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">
-#
-#-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
-#
-# Optional - If you specify to use m_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: https://docs.inspircd.org/2/modules/cgiirc
-#
-# Set to yes if you want to notice opers when CGI clients connect
-# <cgiirc opernotice="no">
-#
-# The type field indicates where the module should get the real
-# client's IP address from, for further information, please see the
-# 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
-# New style:
-# <cgihost type="webirc" password="foobar"
-#   mask="somebox.mysite.com">                      # Get IP from WEBIRC
-#
-# IMPORTANT NOTE:
-# ---------------
-#
-# When you connect CGI:IRC clients, there are two connect classes which
-# apply to these clients. When the client initially connects, the connect
-# class which matches the cgi:irc site's host is checked. Therefore you
-# must raise the maximum local/global clients for this ip as high as you
-# want to allow cgi clients. After the client has connected and is
-# determined to be a cgi:irc client, the class which matches the client's
-# real IP is then checked. You may set this class to a lower value, so that
-# the real IP of the client can still be restricted to, for example, 3
-# sessions maximum.
-#
-
-<module name="m_chanfilter.so">
-<chanfilter hidemask="yes">
-
-<module name="m_chanprotect.so">
-
-<chanprotect
-       noservices="no"
-       qprefix="~"
-       aprefix="&amp;"
-       deprotectself="yes"
-       deprotectothers="yes">
-
-<module name="m_check.so">
-<module name="m_chghost.so">
-<hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
-
-<module name="m_chgident.so">
-<module name="m_chgname.so">
-<module name="m_cloaking.so">
-<cloak mode="half"
-       key="secret"
-       prefix="net-">
-
-<module name="m_close.so">
-<module name="m_clones.so">
-<module name="m_commonchans.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-#
-#-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
-#
-# If you have m_conn_join.so loaded, you can configure it using the
-# follow values:
-#
-#<autojoin channel="#one,#two,#three">
-
-<module name="m_conn_umodes.so">
-<module name="m_cycle.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Connection throttle module.
-#<module name="m_connflood.so">
-#
-#-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
-#  seconds, maxconns -  Amount of connections per <seconds>.
-#
-#  timeout           -  Time to wait after the throttle was activated
-#                       before deactivating it. Be aware that the time
-#                       is seconds + timeout.
-#
-#  quitmsg           -  The message that users get if they attempt to
-#                       connect while the throttle is active.
-#
-#  bootwait          -  Amount of time to wait before enforcing the
-#                       throttling when the server just booted.
-#
-#<connflood seconds="30" maxconns="3" timeout="30"
-#   quitmsg="Throttled" bootwait="10">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# DCCALLOW module: Adds the /DCCALLOW command.
-<module name="m_dccallow.so">
-#
-#-#-#-#-#-#-#-#-#-#-#-  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'
-#
-# File configuration:
-#  pattern           - The glob pattern to match against
-#  action            - Action to take if a user attempts to send a file
-#                      that matches this pattern, can be 'block' or 'allow'
-#
-#<dccallow blockchat="yes" length="5m" action="block">
-#<banfile pattern="*.exe" action="block">
-#<banfile pattern="*.txt" action="allow">
-#
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-
-<module name="m_deaf.so">
-<module name="m_denychans.so"> 
-#<badchan name="#gods*" allowopers="yes" reason="Tortoises!">         #
-#<badchan name="#heaven" redirect="#hell" reason="Nice try!">         #
-
-<module name="m_devoice.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Filter module: Provides message filtering, similar to SPAMFILTER.
-<module name="m_filter.so">
-#                                                                     #
-# 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.                                  #
-#                                                                     #
-# Valid engines are:                                                  #
-#                                                                     #
-# glob   - Glob patterns, provided via m_regex_glob.                  #
-# pcre   - PCRE regexps, provided via m_regex_pcre, needs libpcre.    #
-# tre    - TRE regexps, provided via m_regex_tre, requires libtre.    #
-# posix  - POSIX regexps, provided via m_regex_posix, not available   #
-#          on Windows, no dependencies on other operating systems.    #
-# stdlib - stdlib regexps, provided via m_regex_stdlib, see comment   #
-#          at the <module> tag for info on availability.              #
-#                                                                     #
-<filteropts engine="glob">
-#                                                                     #
-# Your choice of regex engine must match on all servers network-wide.
-#
-# You may specify specific channels that are exempt from being filtered:
-#<exemptfromfilter channel="#blah">
-#
-#-#-#-#-#-#-#-#-#-#-#-  FILTER  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# Optional - If you specify to use the m_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">
-<include file="inspircd.helpop-full.example">
-
-<module name="m_hidechans.so">
-<hidechans affectsopers="false">
-
-<module name="m_hideoper.so">
-<module name="m_ident.so">
-<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">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-#
-#-#-#-#-#-#-#-#-#-#-#   OPERJOIN CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# If you are using the m_operjoin.so module, specify options here:    #
-#                                                                     #
-# channel     -      The channel name to join, can also be a comma    #
-#                    separated list eg. "#channel1,#channel2".        #
-#                                                                     #
-# override    -      Lets the oper join walking thru any modes that   #
-#                    might be set, even bans. Use "yes" or "no".      #
-#                                                                     #
-#<operjoin channel="#channel" override="no">
-#
-# Alternatively you can use the autojoin="channellist" in a <type>    #
-# tag to set specific autojoins for a type of oper, for example:      #
-#
-#<type name="Helper" autojoin="#help" classes="...">
-
-<module name="m_operlog.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Oper MOTD module: Provides support for separate message of the day
-# on oper-up.
-# This module is oper-only.
-#<module name="m_opermotd.so">
-#
-#-#-#-#-#-#-#-#-#-#-#   OPERMOTD CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# If you are using the m_opermotd.so 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">
-#-#-#-#-#-#-#-#-#-#-#   OVERRIDE CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# m_override.so is too complex to describe here, see the docs:        #
-# https://docs.inspircd.org/2/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="m_redirect.so">
-<module name="m_regex_glob.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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.
-# On POSIX-compliant systems, regex syntax can be found by using the
-# command: 'man 7 regex'.
-#<module name="m_regex_posix.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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,
-# this is the provider you want. You need libtre installed in order
-# to compile and load this module.
-#<module name="m_regex_tre.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Registered users only channel creation module. If enabled, only
-# registered users and opers can create new channels.
-#
-# You probably *DO NOT* want to load this module on a public network.
-#
-#<module name="m_regonlycreate.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-<showwhois opersonly="yes" showfromopers="yes">
-
-<module name="m_shun.so">
-<shun enabledcommands="PING PONG QUIT PART JOIN" notifyuser="no" affectopers="no">
-
-<module name="m_sslmodes.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-#
-#-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# m_ssl_gnutls.so is too complex to describe here, see the docs:      #
-# https://docs.inspircd.org/2/modules/ssl_gnutls                      #
-
-<module name="m_sslinfo.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# 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">
-#
-#-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# m_ssl_openssl.so is too complex to describe here, see the docs:     #
-# https://docs.inspircd.org/2/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">
-<watch maxentries="32">
-
-<module name="m_spanningtree.so">
index 75b54faf049066889c11c10004f63c5dcfe9a3b9..7cad2589c4306ef98637697b67ef6baa3f1d6751 100644 (file)
@@ -1,4 +1,4 @@
-#-#-#-#-#-#-#-#-#-#-#-#-  CLASS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-
+#-#-#-#-#-#-#-#-#-#-#-#-#  CLASS CONFIGURATION  #-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 #   Classes are a group of commands which are grouped together and    #
 #   given a unique name. They're used to define which commands        #
 
      # privs: Special privileges that users with this class may utilise.
      #  VIEWING:
-     #   - channels/auspex: allows opers with this priv to see more detail about channels than normal users.
+     #   - channels/auspex: allows opers with this priv to see more details about channels than normal users.
      #   - users/auspex: allows opers with this priv to view more details about users than normal users, e.g. real host and IP.
-     #   - servers/auspex: allows opers with this priv to see more detail about server information than normal users.
-     # ACTIONS:
-     #   - users/mass-message: allows opers with this priv to PRIVMSG and NOTICE to a server mask (e.g. NOTICE $*)
-     #   - channels/high-join-limit: allows opers with this priv to join <channels:opers> total channels instead of <channels:users> total channels.
-     # PERMISSIONS:
-     #   - users/flood/no-fakelag: prevents opers from being penalized with fake lag for flooding (*NOTE)
-     #   - users/flood/no-throttle: allows opers with this priv to send commands without being throttled (*NOTE)
-     #   - users/flood/increased-buffers: allows opers with this priv to send and receive data without worrying about being disconnected for exceeding limits (*NOTE)
+     #   - users/channel-spy: allows opers with this priv to view the private/secret channels that a user is on.
+     #   - servers/auspex: allows opers with this priv to see more details about server information than normal users.
+     #  ACTIONS:
+     #   - users/mass-message: allows opers with this priv to PRIVMSG and NOTICE to a server mask (e.g. NOTICE $*).
+     #   - users/samode-usermodes: allows opers with this priv to change the user modes of any other user using /SAMODE.
+     #  PERMISSIONS:
+     #   - channels/ignore-noctcp: allows opers with this priv to send a CTCP to a +C channel.
+     #   - channels/ignore-nonicks: allows opers with this priv to change their nick when on a +N channel.
+     #   - channels/restricted-create: allows opers with this priv to create channels if the restrictchans module is loaded.
+     #   - users/flood/increased-buffers: allows opers with this priv to send and receive data without worrying about being disconnected for exceeding limits (*NOTE).
+     #   - users/flood/no-fakelag: prevents opers from being penalized with fake lag for flooding (*NOTE).
+     #   - users/flood/no-throttle: allows opers with this priv to send commands without being throttled (*NOTE).
+     #   - users/ignore-callerid: allows opers with this priv to message people using callerid without being on their callerid list.
+     #   - users/ignore-commonchans: allows opers with this priv to send a message to a +c user without sharing common channels.
+     #   - users/ignore-noctcp: allows opers with this priv to send a CTCP to a +T user.
+     #   - users/ignore-privdeaf: allows opers with this priv to message users with +D set.
+     #   - users/sajoin-others: allows opers with this priv to /SAJOIN users other than themselves.
+     #   - servers/use-disabled-commands: allows opers with this priv to use disabled commands.
+     #   - servers/use-disabled-modes: allows opers with this priv to use disabled modes.
      #
      # *NOTE: These privs are potentially dangerous, as they grant users with them the ability to hammer your server's CPU/RAM as much as they want, essentially.
-     privs="users/auspex channels/auspex servers/auspex users/mass-message channels/high-join-limit users/flood/no-throttle users/flood/increased-buffers"
+     privs="users/auspex channels/auspex servers/auspex users/mass-message users/flood/no-throttle users/flood/increased-buffers"
 
-     # usermodes: Oper-only usermodes that opers with this class can use.
+     # usermodes: Oper-only user modes that opers with this class can use.
      usermodes="*"
 
      # chanmodes: Oper-only channel modes that opers with this class can use.
      chanmodes="*">
 
 <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="ServerLink" commands="CONNECT SQUIT RCONNECT RSQUIT MKPASSWD ALLTIME SWHOIS 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" usermodes="*" chanmodes="*">
 <class name="OperChat" commands="WALLOPS GLOBOPS" usermodes="*" chanmodes="*" privs="users/mass-message">
 <class name="HostCloak" commands="SETHOST SETIDENT SETIDLE CHGNAME CHGHOST CHGIDENT" usermodes="*" chanmodes="*" privs="users/auspex">
 
 #                                                                     #
 
 <type
-    # name: Name of type. Used in actual server operator accounts below.
-    # Cannot contain spaces. If you would like a space, use
-    # the _ character instead and it will translate to a space on whois.
+    # name: Name of the type. Used in actual server operator accounts below.
     name="NetAdmin"
 
     # classes: Classes (blocks above) that this type belongs to.
     classes="SACommands OperChat BanControl HostCloak Shutdown ServerLink"
 
-    # vhost: Host opers of this type get when they log in (oper up). This is optional.
+    # vhost: Host that opers of this type get when they log in (oper up). This is optional.
     vhost="netadmin.omega.example.org"
 
+    # maxchans: Maximum number of channels opers of this type can be in at once.
+    maxchans="60"
+
     # 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 to be loaded.
     modes="+s +cCqQ">
 
-<type name="GlobalOp" classes="SACommands OperChat BanControl HostCloak ServerLink" vhost="ircop.omega.example.org">
+<type name="GlobalOp" classes="SACommands OperChat BanControl HostCloak ServerLink" vhost="serverop.omega.example.org">
 <type name="Helper" classes="HostCloak" vhost="helper.omega.example.org">
 
 
@@ -80,9 +92,9 @@
 #   Remember to only make operators out of trustworthy people.        #
 #                                                                     #
 
-# Operator account with a plain-text password.
+# Operator account with a plaintext password.
 <oper
-      # name: Oper login that is used to oper up (/oper name password).
+      # name: Oper login that is used to oper up (/OPER <username> <password>).
       # Remember: This is case sensitive.
       name="Attila"
 
       host="attila@inspircd.org *@2001:db8::/32"
 
       # ** ADVANCED ** This option is disabled by default.
-      # fingerprint: When using the m_sslinfo module, you may specify
-      # a key fingerprint here. This can be obtained by using the /sslinfo
+      # 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.
+      # sslonly: If on, this oper can only oper up if they're using an 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
 
 # Operator with a hashed password. It is highly recommended to use hashed passwords.
 <oper
-      # name: Oper login that is used to oper up (/oper name password).
+      # name: Oper login that is used to oper up (/OPER <username> <password>).
       # 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".
-      # Create hashed passwords with: /mkpasswd <hash> <password>
-      hash="hmac-sha256"
+      # hash: The hash function this password is hashed with. Requires the
+      # module for the selected function (bcrypt, md5, sha1, or sha256) 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 <hashtype> <plaintext>.
+      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 <hashtype> <plaintext>. 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.
       # type: Which type of operator this person is; see the block
       # above for the list of types. NOTE: This is case-sensitive as well.
       type="Helper">
+
+# Once you have edited this file you can remove this line. This is just to
+# ensure that you don't hastily include the file without reading it.
+<die reason="Using opers.conf.example without editing it is a security risk">
index 56a580e3392d678873a71693cf3b6142397de1ea..c07e1379a0a2124008e0ca192539ff0e5a233b0b 100644 (file)
@@ -82,7 +82,7 @@ It just keeps going and going and going and going and goi <BANG>
 All that I know is that nukes are comming from 127.0.0.1
 I know all about the irc and the mirc cops.
 M re ink n ed d, ple s  r fil
-Please refrain from feeding the IRC Operators. Thank you.
+Please refrain from feeding the server operators. Thank you.
 I know all about mirc stuff, hmm.. I think this channel is experiencing packet loss..
 MacDonalds claims Macintosh stole their next idea of the iMac
 I can't hold her any longer, captain, she's gonna bl.. sorry, got caught up in the moment
diff --git a/docs/conf/rules.txt.example b/docs/conf/rules.txt.example
deleted file mode 100644 (file)
index 1a4b99a..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-This is the InspIRCd rules file.
-
-Place any network or server rules here :)
diff --git a/docs/conf/services/anope.conf.example b/docs/conf/services/anope.conf.example
new file mode 100644 (file)
index 0000000..5c8f859
--- /dev/null
@@ -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="SQUERY $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 (file)
index 0000000..d509522
--- /dev/null
@@ -0,0 +1,53 @@
+# This file contains aliases and nickname reservations which are used
+# by Atheme. See https://atheme.github.io/atheme.html 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="SQUERY $requirement :$2-" requires="ALIS"      uline="yes">
+<alias text="CHANFIX"   replace="SQUERY $requirement :$2-" requires="ChanFix"   uline="yes">
+<alias text="GAMESERV"  replace="SQUERY $requirement :$2-" requires="GameServ"  uline="yes">
+<alias text="GLOBAL"    replace="SQUERY $requirement :$2-" requires="Global"    uline="yes" operonly="yes">
+<alias text="GROUPSERV" replace="SQUERY $requirement :$2-" requires="GroupServ" uline="yes">
+<alias text="HELPSERV"  replace="SQUERY $requirement :$2-" requires="HelpServ"  uline="yes">
+<alias text="INFOSERV"  replace="SQUERY $requirement :$2-" requires="InfoServ"  uline="yes">
+<alias text="PROXYSCAN" replace="SQUERY $requirement :$2-" requires="Proxyscan" uline="yes" operonly="yes">
+<alias text="RPGSERV"   replace="SQUERY $requirement :$2-" requires="RPGServ"   uline="yes">
+
+# Short hand aliases for services pseudoclients.
+<alias text="CF" replace="SQUERY $requirement :$2-" requires="ChanFix"   uline="yes">
+<alias text="GL" replace="SQUERY $requirement :$2-" requires="Global"    uline="yes" operonly="yes">
+<alias text="GS" replace="SQUERY $requirement :$2-" requires="GroupServ" uline="yes">
+<alias text="IS" replace="SQUERY $requirement :$2-" requires="InfoServ"  uline="yes">
+<alias text="LS" replace="SQUERY $requirement :$2-" requires="ALIS"      uline="yes">
+<alias text="PS" replace="SQUERY $requirement :$2-" requires="Proxyscan" uline="yes" operonly="yes">
+<alias text="RS" replace="SQUERY $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="SQUERY $requirement :$2-" requires="GameServ" uline="yes">
+#<alias text="HS" replace="SQUERY $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 (file)
index 0000000..9a45c06
--- /dev/null
@@ -0,0 +1,47 @@
+# This file contains aliases and nickname reservations which are used
+# by all common services implementations.
+
+<module name="alias">
+
+# Long hand aliases for services pseudoclients.
+<alias text="BOTSERV"  replace="SQUERY $requirement :$2-" requires="BotServ"  uline="yes">
+<alias text="CHANSERV" replace="SQUERY $requirement :$2-" requires="ChanServ" uline="yes">
+<alias text="HOSTSERV" replace="SQUERY $requirement :$2-" requires="HostServ" uline="yes">
+<alias text="MEMOSERV" replace="SQUERY $requirement :$2-" requires="MemoServ" uline="yes">
+<alias text="NICKSERV" replace="SQUERY $requirement :$2-" requires="NickServ" uline="yes">
+<alias text="OPERSERV" replace="SQUERY $requirement :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="STATSERV" replace="SQUERY $requirement :$2-" requires="StatServ" uline="yes">
+
+# Short hand aliases for services pseudoclients.
+<alias text="BS" replace="SQUERY $requirement :$2-" requires="BotServ"  uline="yes">
+<alias text="CS" replace="SQUERY $requirement :$2-" requires="ChanServ" uline="yes">
+<alias text="HS" replace="SQUERY $requirement :$2-" requires="HostServ" uline="yes">
+<alias text="MS" replace="SQUERY $requirement :$2-" requires="MemoServ" uline="yes">
+<alias text="NS" replace="SQUERY $requirement :$2-" requires="NickServ" uline="yes">
+<alias text="OS" replace="SQUERY $requirement :$2-" requires="OperServ" uline="yes" operonly="yes">
+<alias text="SS" replace="SQUERY $requirement :$2-" requires="StatServ" uline="yes">
+
+# /ID [account] <password>
+# Identifies to a services account.
+<alias text="ID"       format="*" replace="SQUERY $requirement :IDENTIFY $2-" requires="NickServ" uline="yes">
+<alias text="IDENTIFY" format="*" replace="SQUERY $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/docs/rfc/rfc1035.txt b/docs/rfc/rfc1035.txt
deleted file mode 100644 (file)
index b1a9bf5..0000000
+++ /dev/null
@@ -1,3077 +0,0 @@
-Network Working Group                                     P. Mockapetris
-Request for Comments: 1035                                           ISI
-                                                           November 1987
-Obsoletes: RFCs 882, 883, 973
-
-            DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
-
-
-1. STATUS OF THIS MEMO
-
-This RFC describes the details of the domain system and protocol, and
-assumes that the reader is familiar with the concepts discussed in a
-companion RFC, "Domain Names - Concepts and Facilities" [RFC-1034].
-
-The domain system is a mixture of functions and data types which are an
-official protocol and functions and data types which are still
-experimental.  Since the domain system is intentionally extensible, new
-data types and experimental behavior should always be expected in parts
-of the system beyond the official protocol.  The official protocol parts
-include standard queries, responses and the Internet class RR data
-formats (e.g., host addresses).  Since the previous RFC set, several
-definitions have changed, so some previous definitions are obsolete.
-
-Experimental or obsolete features are clearly marked in these RFCs, and
-such information should be used with caution.
-
-The reader is especially cautioned not to depend on the values which
-appear in examples to be current or complete, since their purpose is
-primarily pedagogical.  Distribution of this memo is unlimited.
-
-                           Table of Contents
-
-  1. STATUS OF THIS MEMO                                              1
-  2. INTRODUCTION                                                     3
-      2.1. Overview                                                   3
-      2.2. Common configurations                                      4
-      2.3. Conventions                                                7
-          2.3.1. Preferred name syntax                                7
-          2.3.2. Data Transmission Order                              8
-          2.3.3. Character Case                                       9
-          2.3.4. Size limits                                         10
-  3. DOMAIN NAME SPACE AND RR DEFINITIONS                            10
-      3.1. Name space definitions                                    10
-      3.2. RR definitions                                            11
-          3.2.1. Format                                              11
-          3.2.2. TYPE values                                         12
-          3.2.3. QTYPE values                                        12
-          3.2.4. CLASS values                                        13
-
-
-
-Mockapetris                                                     [Page 1]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-          3.2.5. QCLASS values                                       13
-      3.3. Standard RRs                                              13
-          3.3.1. CNAME RDATA format                                  14
-          3.3.2. HINFO RDATA format                                  14
-          3.3.3. MB RDATA format (EXPERIMENTAL)                      14
-          3.3.4. MD RDATA format (Obsolete)                          15
-          3.3.5. MF RDATA format (Obsolete)                          15
-          3.3.6. MG RDATA format (EXPERIMENTAL)                      16
-          3.3.7. MINFO RDATA format (EXPERIMENTAL)                   16
-          3.3.8. MR RDATA format (EXPERIMENTAL)                      17
-          3.3.9. MX RDATA format                                     17
-          3.3.10. NULL RDATA format (EXPERIMENTAL)                   17
-          3.3.11. NS RDATA format                                    18
-          3.3.12. PTR RDATA format                                   18
-          3.3.13. SOA RDATA format                                   19
-          3.3.14. TXT RDATA format                                   20
-      3.4. ARPA Internet specific RRs                                20
-          3.4.1. A RDATA format                                      20
-          3.4.2. WKS RDATA format                                    21
-      3.5. IN-ADDR.ARPA domain                                       22
-      3.6. Defining new types, classes, and special namespaces       24
-  4. MESSAGES                                                        25
-      4.1. Format                                                    25
-          4.1.1. Header section format                               26
-          4.1.2. Question section format                             28
-          4.1.3. Resource record format                              29
-          4.1.4. Message compression                                 30
-      4.2. Transport                                                 32
-          4.2.1. UDP usage                                           32
-          4.2.2. TCP usage                                           32
-  5. MASTER FILES                                                    33
-      5.1. Format                                                    33
-      5.2. Use of master files to define zones                       35
-      5.3. Master file example                                       36
-  6. NAME SERVER IMPLEMENTATION                                      37
-      6.1. Architecture                                              37
-          6.1.1. Control                                             37
-          6.1.2. Database                                            37
-          6.1.3. Time                                                39
-      6.2. Standard query processing                                 39
-      6.3. Zone refresh and reload processing                        39
-      6.4. Inverse queries (Optional)                                40
-          6.4.1. The contents of inverse queries and responses       40
-          6.4.2. Inverse query and response example                  41
-          6.4.3. Inverse query processing                            42
-
-
-
-
-
-
-Mockapetris                                                     [Page 2]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-      6.5. Completion queries and responses                          42
-  7. RESOLVER IMPLEMENTATION                                         43
-      7.1. Transforming a user request into a query                  43
-      7.2. Sending the queries                                       44
-      7.3. Processing responses                                      46
-      7.4. Using the cache                                           47
-  8. MAIL SUPPORT                                                    47
-      8.1. Mail exchange binding                                     48
-      8.2. Mailbox binding (Experimental)                            48
-  9. REFERENCES and BIBLIOGRAPHY                                     50
-  Index                                                              54
-
-2. INTRODUCTION
-
-2.1. Overview
-
-The goal of domain names is to provide a mechanism for naming resources
-in such a way that the names are usable in different hosts, networks,
-protocol families, internets, and administrative organizations.
-
-From the user's point of view, domain names are useful as arguments to a
-local agent, called a resolver, which retrieves information associated
-with the domain name.  Thus a user might ask for the host address or
-mail information associated with a particular domain name.  To enable
-the user to request a particular type of information, an appropriate
-query type is passed to the resolver with the domain name.  To the user,
-the domain tree is a single information space; the resolver is
-responsible for hiding the distribution of data among name servers from
-the user.
-
-From the resolver's point of view, the database that makes up the domain
-space is distributed among various name servers.  Different parts of the
-domain space are stored in different name servers, although a particular
-data item will be stored redundantly in two or more name servers.  The
-resolver starts with knowledge of at least one name server.  When the
-resolver processes a user query it asks a known name server for the
-information; in return, the resolver either receives the desired
-information or a referral to another name server.  Using these
-referrals, resolvers learn the identities and contents of other name
-servers.  Resolvers are responsible for dealing with the distribution of
-the domain space and dealing with the effects of name server failure by
-consulting redundant databases in other servers.
-
-Name servers manage two kinds of data.  The first kind of data held in
-sets called zones; each zone is the complete database for a particular
-"pruned" subtree of the domain space.  This data is called
-authoritative.  A name server periodically checks to make sure that its
-zones are up to date, and if not, obtains a new copy of updated zones
-
-
-
-Mockapetris                                                     [Page 3]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-from master files stored locally or in another name server.  The second
-kind of data is cached data which was acquired by a local resolver.
-This data may be incomplete, but improves the performance of the
-retrieval process when non-local data is repeatedly accessed.  Cached
-data is eventually discarded by a timeout mechanism.
-
-This functional structure isolates the problems of user interface,
-failure recovery, and distribution in the resolvers and isolates the
-database update and refresh problems in the name servers.
-
-2.2. Common configurations
-
-A host can participate in the domain name system in a number of ways,
-depending on whether the host runs programs that retrieve information
-from the domain system, name servers that answer queries from other
-hosts, or various combinations of both functions.  The simplest, and
-perhaps most typical, configuration is shown below:
-
-                 Local Host                        |  Foreign
-                                                   |
-    +---------+               +----------+         |  +--------+
-    |         | user queries  |          |queries  |  |        |
-    |  User   |-------------->|          |---------|->|Foreign |
-    | Program |               | Resolver |         |  |  Name  |
-    |         |<--------------|          |<--------|--| Server |
-    |         | user responses|          |responses|  |        |
-    +---------+               +----------+         |  +--------+
-                                |     A            |
-                cache additions |     | references |
-                                V     |            |
-                              +----------+         |
-                              |  cache   |         |
-                              +----------+         |
-
-User programs interact with the domain name space through resolvers; the
-format of user queries and user responses is specific to the host and
-its operating system.  User queries will typically be operating system
-calls, and the resolver and its cache will be part of the host operating
-system.  Less capable hosts may choose to implement the resolver as a
-subroutine to be linked in with every program that needs its services.
-Resolvers answer user queries with information they acquire via queries
-to foreign name servers and the local cache.
-
-Note that the resolver may have to make several queries to several
-different foreign name servers to answer a particular user query, and
-hence the resolution of a user query may involve several network
-accesses and an arbitrary amount of time.  The queries to foreign name
-servers and the corresponding responses have a standard format described
-
-
-
-Mockapetris                                                     [Page 4]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-in this memo, and may be datagrams.
-
-Depending on its capabilities, a name server could be a stand alone
-program on a dedicated machine or a process or processes on a large
-timeshared host.  A simple configuration might be:
-
-                 Local Host                        |  Foreign
-                                                   |
-      +---------+                                  |
-     /         /|                                  |
-    +---------+ |             +----------+         |  +--------+
-    |         | |             |          |responses|  |        |
-    |         | |             |   Name   |---------|->|Foreign |
-    |  Master |-------------->|  Server  |         |  |Resolver|
-    |  files  | |             |          |<--------|--|        |
-    |         |/              |          | queries |  +--------+
-    +---------+               +----------+         |
-
-Here a primary name server acquires information about one or more zones
-by reading master files from its local file system, and answers queries
-about those zones that arrive from foreign resolvers.
-
-The DNS requires that all zones be redundantly supported by more than
-one name server.  Designated secondary servers can acquire zones and
-check for updates from the primary server using the zone transfer
-protocol of the DNS.  This configuration is shown below:
-
-                 Local Host                        |  Foreign
-                                                   |
-      +---------+                                  |
-     /         /|                                  |
-    +---------+ |             +----------+         |  +--------+
-    |         | |             |          |responses|  |        |
-    |         | |             |   Name   |---------|->|Foreign |
-    |  Master |-------------->|  Server  |         |  |Resolver|
-    |  files  | |             |          |<--------|--|        |
-    |         |/              |          | queries |  +--------+
-    +---------+               +----------+         |
-                                A     |maintenance |  +--------+
-                                |     +------------|->|        |
-                                |      queries     |  |Foreign |
-                                |                  |  |  Name  |
-                                +------------------|--| Server |
-                             maintenance responses |  +--------+
-
-In this configuration, the name server periodically establishes a
-virtual circuit to a foreign name server to acquire a copy of a zone or
-to check that an existing copy has not changed.  The messages sent for
-
-
-
-Mockapetris                                                     [Page 5]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-these maintenance activities follow the same form as queries and
-responses, but the message sequences are somewhat different.
-
-The information flow in a host that supports all aspects of the domain
-name system is shown below:
-
-                 Local Host                        |  Foreign
-                                                   |
-    +---------+               +----------+         |  +--------+
-    |         | user queries  |          |queries  |  |        |
-    |  User   |-------------->|          |---------|->|Foreign |
-    | Program |               | Resolver |         |  |  Name  |
-    |         |<--------------|          |<--------|--| Server |
-    |         | user responses|          |responses|  |        |
-    +---------+               +----------+         |  +--------+
-                                |     A            |
-                cache additions |     | references |
-                                V     |            |
-                              +----------+         |
-                              |  Shared  |         |
-                              | database |         |
-                              +----------+         |
-                                A     |            |
-      +---------+     refreshes |     | references |
-     /         /|               |     V            |
-    +---------+ |             +----------+         |  +--------+
-    |         | |             |          |responses|  |        |
-    |         | |             |   Name   |---------|->|Foreign |
-    |  Master |-------------->|  Server  |         |  |Resolver|
-    |  files  | |             |          |<--------|--|        |
-    |         |/              |          | queries |  +--------+
-    +---------+               +----------+         |
-                                A     |maintenance |  +--------+
-                                |     +------------|->|        |
-                                |      queries     |  |Foreign |
-                                |                  |  |  Name  |
-                                +------------------|--| Server |
-                             maintenance responses |  +--------+
-
-The shared database holds domain space data for the local name server
-and resolver.  The contents of the shared database will typically be a
-mixture of authoritative data maintained by the periodic refresh
-operations of the name server and cached data from previous resolver
-requests.  The structure of the domain data and the necessity for
-synchronization between name servers and resolvers imply the general
-characteristics of this database, but the actual format is up to the
-local implementor.
-
-
-
-
-Mockapetris                                                     [Page 6]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-Information flow can also be tailored so that a group of hosts act
-together to optimize activities.  Sometimes this is done to offload less
-capable hosts so that they do not have to implement a full resolver.
-This can be appropriate for PCs or hosts which want to minimize the
-amount of new network code which is required.  This scheme can also
-allow a group of hosts can share a small number of caches rather than
-maintaining a large number of separate caches, on the premise that the
-centralized caches will have a higher hit ratio.  In either case,
-resolvers are replaced with stub resolvers which act as front ends to
-resolvers located in a recursive server in one or more name servers
-known to perform that service:
-
-                   Local Hosts                     |  Foreign
-                                                   |
-    +---------+                                    |
-    |         | responses                          |
-    | Stub    |<--------------------+              |
-    | Resolver|                     |              |
-    |         |----------------+    |              |
-    +---------+ recursive      |    |              |
-                queries        |    |              |
-                               V    |              |
-    +---------+ recursive     +----------+         |  +--------+
-    |         | queries       |          |queries  |  |        |
-    | Stub    |-------------->| Recursive|---------|->|Foreign |
-    | Resolver|               | Server   |         |  |  Name  |
-    |         |<--------------|          |<--------|--| Server |
-    +---------+ responses     |          |responses|  |        |
-                              +----------+         |  +--------+
-                              |  Central |         |
-                              |   cache  |         |
-                              +----------+         |
-
-In any case, note that domain components are always replicated for
-reliability whenever possible.
-
-2.3. Conventions
-
-The domain system has several conventions dealing with low-level, but
-fundamental, issues.  While the implementor is free to violate these
-conventions WITHIN HIS OWN SYSTEM, he must observe these conventions in
-ALL behavior observed from other hosts.
-
-2.3.1. Preferred name syntax
-
-The DNS specifications attempt to be as general as possible in the rules
-for constructing domain names.  The idea is that the name of any
-existing object can be expressed as a domain name with minimal changes.
-
-
-
-Mockapetris                                                     [Page 7]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-However, when assigning a domain name for an object, the prudent user
-will select a name which satisfies both the rules of the domain system
-and any existing rules for the object, whether these rules are published
-or implied by existing programs.
-
-For example, when naming a mail domain, the user should satisfy both the
-rules of this memo and those in RFC-822.  When creating a new host name,
-the old rules for HOSTS.TXT should be followed.  This avoids problems
-when old software is converted to use domain names.
-
-The following syntax will result in fewer problems with many
-
-applications that use domain names (e.g., mail, TELNET).
-
-<domain> ::= <subdomain> | " "
-
-<subdomain> ::= <label> | <subdomain> "." <label>
-
-<label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
-
-<ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
-
-<let-dig-hyp> ::= <let-dig> | "-"
-
-<let-dig> ::= <letter> | <digit>
-
-<letter> ::= any one of the 52 alphabetic characters A through Z in
-upper case and a through z in lower case
-
-<digit> ::= any one of the ten digits 0 through 9
-
-Note that while upper and lower case letters are allowed in domain
-names, no significance is attached to the case.  That is, two names with
-the same spelling but different case are to be treated as if identical.
-
-The labels must follow the rules for ARPANET host names.  They must
-start with a letter, end with a letter or digit, and have as interior
-characters only letters, digits, and hyphen.  There are also some
-restrictions on the length.  Labels must be 63 characters or less.
-
-For example, the following strings identify hosts in the Internet:
-
-A.ISI.EDU XX.LCS.MIT.EDU SRI-NIC.ARPA
-
-2.3.2. Data Transmission Order
-
-The order of transmission of the header and data described in this
-document is resolved to the octet level.  Whenever a diagram shows a
-
-
-
-Mockapetris                                                     [Page 8]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-group of octets, the order of transmission of those octets is the normal
-order in which they are read in English.  For example, in the following
-diagram, the octets are transmitted in the order they are numbered.
-
-     0                   1
-     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |       1       |       2       |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |       3       |       4       |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |       5       |       6       |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-Whenever an octet represents a numeric quantity, the left most bit in
-the diagram is the high order or most significant bit.  That is, the bit
-labeled 0 is the most significant bit.  For example, the following
-diagram represents the value 170 (decimal).
-
-     0 1 2 3 4 5 6 7
-    +-+-+-+-+-+-+-+-+
-    |1 0 1 0 1 0 1 0|
-    +-+-+-+-+-+-+-+-+
-
-Similarly, whenever a multi-octet field represents a numeric quantity
-the left most bit of the whole field is the most significant bit.  When
-a multi-octet quantity is transmitted the most significant octet is
-transmitted first.
-
-2.3.3. Character Case
-
-For all parts of the DNS that are part of the official protocol, all
-comparisons between character strings (e.g., labels, domain names, etc.)
-are done in a case-insensitive manner.  At present, this rule is in
-force throughout the domain system without exception.  However, future
-additions beyond current usage may need to use the full binary octet
-capabilities in names, so attempts to store domain names in 7-bit ASCII
-or use of special bytes to terminate labels, etc., should be avoided.
-
-When data enters the domain system, its original case should be
-preserved whenever possible.  In certain circumstances this cannot be
-done.  For example, if two RRs are stored in a database, one at x.y and
-one at X.Y, they are actually stored at the same place in the database,
-and hence only one casing would be preserved.  The basic rule is that
-case can be discarded only when data is used to define structure in a
-database, and two names are identical when compared in a case
-insensitive manner.
-
-
-
-
-Mockapetris                                                     [Page 9]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-Loss of case sensitive data must be minimized.  Thus while data for x.y
-and X.Y may both be stored under a single location x.y or X.Y, data for
-a.x and B.X would never be stored under A.x, A.X, b.x, or b.X.  In
-general, this preserves the case of the first label of a domain name,
-but forces standardization of interior node labels.
-
-Systems administrators who enter data into the domain database should
-take care to represent the data they supply to the domain system in a
-case-consistent manner if their system is case-sensitive.  The data
-distribution system in the domain system will ensure that consistent
-representations are preserved.
-
-2.3.4. Size limits
-
-Various objects and parameters in the DNS have size limits.  They are
-listed below.  Some could be easily changed, others are more
-fundamental.
-
-labels          63 octets or less
-
-names           255 octets or less
-
-TTL             positive values of a signed 32 bit number.
-
-UDP messages    512 octets or less
-
-3. DOMAIN NAME SPACE AND RR DEFINITIONS
-
-3.1. Name space definitions
-
-Domain names in messages are expressed in terms of a sequence of labels.
-Each label is represented as a one octet length field followed by that
-number of octets.  Since every domain name ends with the null label of
-the root, a domain name is terminated by a length byte of zero.  The
-high order two bits of every length octet must be zero, and the
-remaining six bits of the length field limit the label to 63 octets or
-less.
-
-To simplify implementations, the total length of a domain name (i.e.,
-label octets and label length octets) is restricted to 255 octets or
-less.
-
-Although labels can contain any 8 bit values in octets that make up a
-label, it is strongly recommended that labels follow the preferred
-syntax described elsewhere in this memo, which is compatible with
-existing host naming conventions.  Name servers and resolvers must
-compare labels in a case-insensitive manner (i.e., A=a), assuming ASCII
-with zero parity.  Non-alphabetic codes must match exactly.
-
-
-
-Mockapetris                                                    [Page 10]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-3.2. RR definitions
-
-3.2.1. Format
-
-All RRs have the same top level format shown below:
-
-                                    1  1  1  1  1  1
-      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                                               |
-    /                                               /
-    /                      NAME                     /
-    |                                               |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                      TYPE                     |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                     CLASS                     |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                      TTL                      |
-    |                                               |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                   RDLENGTH                    |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
-    /                     RDATA                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-
-where:
-
-NAME            an owner name, i.e., the name of the node to which this
-                resource record pertains.
-
-TYPE            two octets containing one of the RR TYPE codes.
-
-CLASS           two octets containing one of the RR CLASS codes.
-
-TTL             a 32 bit signed integer that specifies the time interval
-                that the resource record may be cached before the source
-                of the information should again be consulted.  Zero
-                values are interpreted to mean that the RR can only be
-                used for the transaction in progress, and should not be
-                cached.  For example, SOA records are always distributed
-                with a zero TTL to prohibit caching.  Zero values can
-                also be used for extremely volatile data.
-
-RDLENGTH        an unsigned 16 bit integer that specifies the length in
-                octets of the RDATA field.
-
-
-
-Mockapetris                                                    [Page 11]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-RDATA           a variable length string of octets that describes the
-                resource.  The format of this information varies
-                according to the TYPE and CLASS of the resource record.
-
-3.2.2. TYPE values
-
-TYPE fields are used in resource records.  Note that these types are a
-subset of QTYPEs.
-
-TYPE            value and meaning
-
-A               1 a host address
-
-NS              2 an authoritative name server
-
-MD              3 a mail destination (Obsolete - use MX)
-
-MF              4 a mail forwarder (Obsolete - use MX)
-
-CNAME           5 the canonical name for an alias
-
-SOA             6 marks the start of a zone of authority
-
-MB              7 a mailbox domain name (EXPERIMENTAL)
-
-MG              8 a mail group member (EXPERIMENTAL)
-
-MR              9 a mail rename domain name (EXPERIMENTAL)
-
-NULL            10 a null RR (EXPERIMENTAL)
-
-WKS             11 a well known service description
-
-PTR             12 a domain name pointer
-
-HINFO           13 host information
-
-MINFO           14 mailbox or mail list information
-
-MX              15 mail exchange
-
-TXT             16 text strings
-
-3.2.3. QTYPE values
-
-QTYPE fields appear in the question part of a query.  QTYPES are a
-superset of TYPEs, hence all TYPEs are valid QTYPEs.  In addition, the
-following QTYPEs are defined:
-
-
-
-Mockapetris                                                    [Page 12]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-AXFR            252 A request for a transfer of an entire zone
-
-MAILB           253 A request for mailbox-related records (MB, MG or MR)
-
-MAILA           254 A request for mail agent RRs (Obsolete - see MX)
-
-*               255 A request for all records
-
-3.2.4. CLASS values
-
-CLASS fields appear in resource records.  The following CLASS mnemonics
-and values are defined:
-
-IN              1 the Internet
-
-CS              2 the CSNET class (Obsolete - used only for examples in
-                some obsolete RFCs)
-
-CH              3 the CHAOS class
-
-HS              4 Hesiod [Dyer 87]
-
-3.2.5. QCLASS values
-
-QCLASS fields appear in the question section of a query.  QCLASS values
-are a superset of CLASS values; every CLASS is a valid QCLASS.  In
-addition to CLASS values, the following QCLASSes are defined:
-
-*               255 any class
-
-3.3. Standard RRs
-
-The following RR definitions are expected to occur, at least
-potentially, in all classes.  In particular, NS, SOA, CNAME, and PTR
-will be used in all classes, and have the same format in all classes.
-Because their RDATA format is known, all domain names in the RDATA
-section of these RRs may be compressed.
-
-<domain-name> is a domain name represented as a series of labels, and
-terminated by a label with zero length.  <character-string> is a single
-length octet followed by that number of characters.  <character-string>
-is treated as binary information, and can be up to 256 characters in
-length (including the length octet).
-
-
-
-
-
-
-
-
-Mockapetris                                                    [Page 13]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-3.3.1. CNAME RDATA format
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                     CNAME                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-CNAME           A <domain-name> which specifies the canonical or primary
-                name for the owner.  The owner name is an alias.
-
-CNAME RRs cause no additional section processing, but name servers may
-choose to restart the query at the canonical name in certain cases.  See
-the description of name server logic in [RFC-1034] for details.
-
-3.3.2. HINFO RDATA format
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                      CPU                      /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                       OS                      /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-CPU             A <character-string> which specifies the CPU type.
-
-OS              A <character-string> which specifies the operating
-                system type.
-
-Standard values for CPU and OS can be found in [RFC-1010].
-
-HINFO records are used to acquire general information about a host.  The
-main use is for protocols such as FTP that can use special procedures
-when talking between machines or operating systems of the same type.
-
-3.3.3. MB RDATA format (EXPERIMENTAL)
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                   MADNAME                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-MADNAME         A <domain-name> which specifies a host which has the
-                specified mailbox.
-
-
-
-Mockapetris                                                    [Page 14]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-MB records cause additional section processing which looks up an A type
-RRs corresponding to MADNAME.
-
-3.3.4. MD RDATA format (Obsolete)
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                   MADNAME                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-MADNAME         A <domain-name> which specifies a host which has a mail
-                agent for the domain which should be able to deliver
-                mail for the domain.
-
-MD records cause additional section processing which looks up an A type
-record corresponding to MADNAME.
-
-MD is obsolete.  See the definition of MX and [RFC-974] for details of
-the new scheme.  The recommended policy for dealing with MD RRs found in
-a master file is to reject them, or to convert them to MX RRs with a
-preference of 0.
-
-3.3.5. MF RDATA format (Obsolete)
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                   MADNAME                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-MADNAME         A <domain-name> which specifies a host which has a mail
-                agent for the domain which will accept mail for
-                forwarding to the domain.
-
-MF records cause additional section processing which looks up an A type
-record corresponding to MADNAME.
-
-MF is obsolete.  See the definition of MX and [RFC-974] for details ofw
-the new scheme.  The recommended policy for dealing with MD RRs found in
-a master file is to reject them, or to convert them to MX RRs with a
-preference of 10.
-
-
-
-
-
-
-
-Mockapetris                                                    [Page 15]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-3.3.6. MG RDATA format (EXPERIMENTAL)
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                   MGMNAME                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-MGMNAME         A <domain-name> which specifies a mailbox which is a
-                member of the mail group specified by the domain name.
-
-MG records cause no additional section processing.
-
-3.3.7. MINFO RDATA format (EXPERIMENTAL)
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                    RMAILBX                    /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                    EMAILBX                    /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-RMAILBX         A <domain-name> which specifies a mailbox which is
-                responsible for the mailing list or mailbox.  If this
-                domain name names the root, the owner of the MINFO RR is
-                responsible for itself.  Note that many existing mailing
-                lists use a mailbox X-request for the RMAILBX field of
-                mailing list X, e.g., Msgroup-request for Msgroup.  This
-                field provides a more general mechanism.
-
-
-EMAILBX         A <domain-name> which specifies a mailbox which is to
-                receive error messages related to the mailing list or
-                mailbox specified by the owner of the MINFO RR (similar
-                to the ERRORS-TO: field which has been proposed).  If
-                this domain name names the root, errors should be
-                returned to the sender of the message.
-
-MINFO records cause no additional section processing.  Although these
-records can be associated with a simple mailbox, they are usually used
-with a mailing list.
-
-
-
-
-
-
-
-
-Mockapetris                                                    [Page 16]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-3.3.8. MR RDATA format (EXPERIMENTAL)
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                   NEWNAME                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-NEWNAME         A <domain-name> which specifies a mailbox which is the
-                proper rename of the specified mailbox.
-
-MR records cause no additional section processing.  The main use for MR
-is as a forwarding entry for a user who has moved to a different
-mailbox.
-
-3.3.9. MX RDATA format
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                  PREFERENCE                   |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                   EXCHANGE                    /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-PREFERENCE      A 16 bit integer which specifies the preference given to
-                this RR among others at the same owner.  Lower values
-                are preferred.
-
-EXCHANGE        A <domain-name> which specifies a host willing to act as
-                a mail exchange for the owner name.
-
-MX records cause type A additional section processing for the host
-specified by EXCHANGE.  The use of MX RRs is explained in detail in
-[RFC-974].
-
-3.3.10. NULL RDATA format (EXPERIMENTAL)
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                  <anything>                   /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-Anything at all may be in the RDATA field so long as it is 65535 octets
-or less.
-
-
-
-
-Mockapetris                                                    [Page 17]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-NULL records cause no additional section processing.  NULL RRs are not
-allowed in master files.  NULLs are used as placeholders in some
-experimental extensions of the DNS.
-
-3.3.11. NS RDATA format
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                   NSDNAME                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-NSDNAME         A <domain-name> which specifies a host which should be
-                authoritative for the specified class and domain.
-
-NS records cause both the usual additional section processing to locate
-a type A record, and, when used in a referral, a special search of the
-zone in which they reside for glue information.
-
-The NS RR states that the named host should be expected to have a zone
-starting at owner name of the specified class.  Note that the class may
-not indicate the protocol family which should be used to communicate
-with the host, although it is typically a strong hint.  For example,
-hosts which are name servers for either Internet (IN) or Hesiod (HS)
-class information are normally queried using IN class protocols.
-
-3.3.12. PTR RDATA format
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                   PTRDNAME                    /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-PTRDNAME        A <domain-name> which points to some location in the
-                domain name space.
-
-PTR records cause no additional section processing.  These RRs are used
-in special domains to point to some other location in the domain space.
-These records are simple data, and don't imply any special processing
-similar to that performed by CNAME, which identifies aliases.  See the
-description of the IN-ADDR.ARPA domain for an example.
-
-
-
-
-
-
-
-
-Mockapetris                                                    [Page 18]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-3.3.13. SOA RDATA format
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                     MNAME                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                     RNAME                     /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                    SERIAL                     |
-    |                                               |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                    REFRESH                    |
-    |                                               |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                     RETRY                     |
-    |                                               |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                    EXPIRE                     |
-    |                                               |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                    MINIMUM                    |
-    |                                               |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-MNAME           The <domain-name> of the name server that was the
-                original or primary source of data for this zone.
-
-RNAME           A <domain-name> which specifies the mailbox of the
-                person responsible for this zone.
-
-SERIAL          The unsigned 32 bit version number of the original copy
-                of the zone.  Zone transfers preserve this value.  This
-                value wraps and should be compared using sequence space
-                arithmetic.
-
-REFRESH         A 32 bit time interval before the zone should be
-                refreshed.
-
-RETRY           A 32 bit time interval that should elapse before a
-                failed refresh should be retried.
-
-EXPIRE          A 32 bit time value that specifies the upper limit on
-                the time interval that can elapse before the zone is no
-                longer authoritative.
-
-
-
-
-
-Mockapetris                                                    [Page 19]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-MINIMUM         The unsigned 32 bit minimum TTL field that should be
-                exported with any RR from this zone.
-
-SOA records cause no additional section processing.
-
-All times are in units of seconds.
-
-Most of these fields are pertinent only for name server maintenance
-operations.  However, MINIMUM is used in all query operations that
-retrieve RRs from a zone.  Whenever a RR is sent in a response to a
-query, the TTL field is set to the maximum of the TTL field from the RR
-and the MINIMUM field in the appropriate SOA.  Thus MINIMUM is a lower
-bound on the TTL field for all RRs in a zone.  Note that this use of
-MINIMUM should occur when the RRs are copied into the response and not
-when the zone is loaded from a master file or via a zone transfer.  The
-reason for this provison is to allow future dynamic update facilities to
-change the SOA RR with known semantics.
-
-
-3.3.14. TXT RDATA format
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    /                   TXT-DATA                    /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-TXT-DATA        One or more <character-string>s.
-
-TXT RRs are used to hold descriptive text.  The semantics of the text
-depends on the domain where it is found.
-
-3.4. Internet specific RRs
-
-3.4.1. A RDATA format
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                    ADDRESS                    |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-ADDRESS         A 32 bit Internet address.
-
-Hosts that have multiple Internet addresses will have multiple A
-records.
-
-
-
-
-
-Mockapetris                                                    [Page 20]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-A records cause no additional section processing.  The RDATA section of
-an A line in a master file is an Internet address expressed as four
-decimal numbers separated by dots without any imbedded spaces (e.g.,
-"10.2.0.52" or "192.0.5.6").
-
-3.4.2. WKS RDATA format
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                    ADDRESS                    |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |       PROTOCOL        |                       |
-    +--+--+--+--+--+--+--+--+                       |
-    |                                               |
-    /                   <BIT MAP>                   /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-ADDRESS         An 32 bit Internet address
-
-PROTOCOL        An 8 bit IP protocol number
-
-<BIT MAP>       A variable length bit map.  The bit map must be a
-                multiple of 8 bits long.
-
-The WKS record is used to describe the well known services supported by
-a particular protocol on a particular internet address.  The PROTOCOL
-field specifies an IP protocol number, and the bit map has one bit per
-port of the specified protocol.  The first bit corresponds to port 0,
-the second to port 1, etc.  If the bit map does not include a bit for a
-protocol of interest, that bit is assumed zero.  The appropriate values
-and mnemonics for ports and protocols are specified in [RFC-1010].
-
-For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port
-25 (SMTP).  If this bit is set, a SMTP server should be listening on TCP
-port 25; if zero, SMTP service is not supported on the specified
-address.
-
-The purpose of WKS RRs is to provide availability information for
-servers for TCP and UDP.  If a server supports both TCP and UDP, or has
-multiple Internet addresses, then multiple WKS RRs are used.
-
-WKS RRs cause no additional section processing.
-
-In master files, both ports and protocols are expressed using mnemonics
-or decimal numbers.
-
-
-
-
-Mockapetris                                                    [Page 21]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-3.5. IN-ADDR.ARPA domain
-
-The Internet uses a special domain to support gateway location and
-Internet address to host mapping.  Other classes may employ a similar
-strategy in other domains.  The intent of this domain is to provide a
-guaranteed method to perform host address to host name mapping, and to
-facilitate queries to locate all gateways on a particular network in the
-Internet.
-
-Note that both of these services are similar to functions that could be
-performed by inverse queries; the difference is that this part of the
-domain name space is structured according to address, and hence can
-guarantee that the appropriate data can be located without an exhaustive
-search of the domain space.
-
-The domain begins at IN-ADDR.ARPA and has a substructure which follows
-the Internet addressing structure.
-
-Domain names in the IN-ADDR.ARPA domain are defined to have up to four
-labels in addition to the IN-ADDR.ARPA suffix.  Each label represents
-one octet of an Internet address, and is expressed as a character string
-for a decimal value in the range 0-255 (with leading zeros omitted
-except in the case of a zero octet which is represented by a single
-zero).
-
-Host addresses are represented by domain names that have all four labels
-specified.  Thus data for Internet address 10.2.0.52 is located at
-domain name 52.0.2.10.IN-ADDR.ARPA.  The reversal, though awkward to
-read, allows zones to be delegated which are exactly one network of
-address space.  For example, 10.IN-ADDR.ARPA can be a zone containing
-data for the ARPANET, while 26.IN-ADDR.ARPA can be a separate zone for
-MILNET.  Address nodes are used to hold pointers to primary host names
-in the normal domain space.
-
-Network numbers correspond to some non-terminal nodes at various depths
-in the IN-ADDR.ARPA domain, since Internet network numbers are either 1,
-2, or 3 octets.  Network nodes are used to hold pointers to the primary
-host names of gateways attached to that network.  Since a gateway is, by
-definition, on more than one network, it will typically have two or more
-network nodes which point at it.  Gateways will also have host level
-pointers at their fully qualified addresses.
-
-Both the gateway pointers at network nodes and the normal host pointers
-at full address nodes use the PTR RR to point back to the primary domain
-names of the corresponding hosts.
-
-For example, the IN-ADDR.ARPA domain will contain information about the
-ISI gateway between net 10 and 26, an MIT gateway from net 10 to MIT's
-
-
-
-Mockapetris                                                    [Page 22]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-net 18, and hosts A.ISI.EDU and MULTICS.MIT.EDU.  Assuming that ISI
-gateway has addresses 10.2.0.22 and 26.0.0.103, and a name MILNET-
-GW.ISI.EDU, and the MIT gateway has addresses 10.0.0.77 and 18.10.0.4
-and a name GW.LCS.MIT.EDU, the domain database would contain:
-
-    10.IN-ADDR.ARPA.           PTR MILNET-GW.ISI.EDU.
-    10.IN-ADDR.ARPA.           PTR GW.LCS.MIT.EDU.
-    18.IN-ADDR.ARPA.           PTR GW.LCS.MIT.EDU.
-    26.IN-ADDR.ARPA.           PTR MILNET-GW.ISI.EDU.
-    22.0.2.10.IN-ADDR.ARPA.    PTR MILNET-GW.ISI.EDU.
-    103.0.0.26.IN-ADDR.ARPA.   PTR MILNET-GW.ISI.EDU.
-    77.0.0.10.IN-ADDR.ARPA.    PTR GW.LCS.MIT.EDU.
-    4.0.10.18.IN-ADDR.ARPA.    PTR GW.LCS.MIT.EDU.
-    103.0.3.26.IN-ADDR.ARPA.   PTR A.ISI.EDU.
-    6.0.0.10.IN-ADDR.ARPA.     PTR MULTICS.MIT.EDU.
-
-Thus a program which wanted to locate gateways on net 10 would originate
-a query of the form QTYPE=PTR, QCLASS=IN, QNAME=10.IN-ADDR.ARPA.  It
-would receive two RRs in response:
-
-    10.IN-ADDR.ARPA.           PTR MILNET-GW.ISI.EDU.
-    10.IN-ADDR.ARPA.           PTR GW.LCS.MIT.EDU.
-
-The program could then originate QTYPE=A, QCLASS=IN queries for MILNET-
-GW.ISI.EDU. and GW.LCS.MIT.EDU. to discover the Internet addresses of
-these gateways.
-
-A resolver which wanted to find the host name corresponding to Internet
-host address 10.0.0.6 would pursue a query of the form QTYPE=PTR,
-QCLASS=IN, QNAME=6.0.0.10.IN-ADDR.ARPA, and would receive:
-
-    6.0.0.10.IN-ADDR.ARPA.     PTR MULTICS.MIT.EDU.
-
-Several cautions apply to the use of these services:
-   - Since the IN-ADDR.ARPA special domain and the normal domain
-     for a particular host or gateway will be in different zones,
-     the possibility exists that that the data may be inconsistent.
-
-   - Gateways will often have two names in separate domains, only
-     one of which can be primary.
-
-   - Systems that use the domain database to initialize their
-     routing tables must start with enough gateway information to
-     guarantee that they can access the appropriate name server.
-
-   - The gateway data only reflects the existence of a gateway in a
-     manner equivalent to the current HOSTS.TXT file.  It doesn't
-     replace the dynamic availability information from GGP or EGP.
-
-
-
-Mockapetris                                                    [Page 23]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-3.6. Defining new types, classes, and special namespaces
-
-The previously defined types and classes are the ones in use as of the
-date of this memo.  New definitions should be expected.  This section
-makes some recommendations to designers considering additions to the
-existing facilities.  The mailing list NAMEDROPPERS@SRI-NIC.ARPA is the
-forum where general discussion of design issues takes place.
-
-In general, a new type is appropriate when new information is to be
-added to the database about an existing object, or we need new data
-formats for some totally new object.  Designers should attempt to define
-types and their RDATA formats that are generally applicable to all
-classes, and which avoid duplication of information.  New classes are
-appropriate when the DNS is to be used for a new protocol, etc which
-requires new class-specific data formats, or when a copy of the existing
-name space is desired, but a separate management domain is necessary.
-
-New types and classes need mnemonics for master files; the format of the
-master files requires that the mnemonics for type and class be disjoint.
-
-TYPE and CLASS values must be a proper subset of QTYPEs and QCLASSes
-respectively.
-
-The present system uses multiple RRs to represent multiple values of a
-type rather than storing multiple values in the RDATA section of a
-single RR.  This is less efficient for most applications, but does keep
-RRs shorter.  The multiple RRs assumption is incorporated in some
-experimental work on dynamic update methods.
-
-The present system attempts to minimize the duplication of data in the
-database in order to insure consistency.  Thus, in order to find the
-address of the host for a mail exchange, you map the mail domain name to
-a host name, then the host name to addresses, rather than a direct
-mapping to host address.  This approach is preferred because it avoids
-the opportunity for inconsistency.
-
-In defining a new type of data, multiple RR types should not be used to
-create an ordering between entries or express different formats for
-equivalent bindings, instead this information should be carried in the
-body of the RR and a single type used.  This policy avoids problems with
-caching multiple types and defining QTYPEs to match multiple types.
-
-For example, the original form of mail exchange binding used two RR
-types one to represent a "closer" exchange (MD) and one to represent a
-"less close" exchange (MF).  The difficulty is that the presence of one
-RR type in a cache doesn't convey any information about the other
-because the query which acquired the cached information might have used
-a QTYPE of MF, MD, or MAILA (which matched both).  The redesigned
-
-
-
-Mockapetris                                                    [Page 24]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-service used a single type (MX) with a "preference" value in the RDATA
-section which can order different RRs.  However, if any MX RRs are found
-in the cache, then all should be there.
-
-4. MESSAGES
-
-4.1. Format
-
-All communications inside of the domain protocol are carried in a single
-format called a message.  The top level format of message is divided
-into 5 sections (some of which are empty in certain cases) shown below:
-
-    +---------------------+
-    |        Header       |
-    +---------------------+
-    |       Question      | the question for the name server
-    +---------------------+
-    |        Answer       | RRs answering the question
-    +---------------------+
-    |      Authority      | RRs pointing toward an authority
-    +---------------------+
-    |      Additional     | RRs holding additional information
-    +---------------------+
-
-The header section is always present.  The header includes fields that
-specify which of the remaining sections are present, and also specify
-whether the message is a query or a response, a standard query or some
-other opcode, etc.
-
-The names of the sections after the header are derived from their use in
-standard queries.  The question section contains fields that describe a
-question to a name server.  These fields are a query type (QTYPE), a
-query class (QCLASS), and a query domain name (QNAME).  The last three
-sections have the same format: a possibly empty list of concatenated
-resource records (RRs).  The answer section contains RRs that answer the
-question; the authority section contains RRs that point toward an
-authoritative name server; the additional records section contains RRs
-which relate to the query, but are not strictly answers for the
-question.
-
-
-
-
-
-
-
-
-
-
-
-
-Mockapetris                                                    [Page 25]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-4.1.1. Header section format
-
-The header contains the following fields:
-
-                                    1  1  1  1  1  1
-      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                      ID                       |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                    QDCOUNT                    |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                    ANCOUNT                    |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                    NSCOUNT                    |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                    ARCOUNT                    |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-ID              A 16 bit identifier assigned by the program that
-                generates any kind of query.  This identifier is copied
-                the corresponding reply and can be used by the requester
-                to match up replies to outstanding queries.
-
-QR              A one bit field that specifies whether this message is a
-                query (0), or a response (1).
-
-OPCODE          A four bit field that specifies kind of query in this
-                message.  This value is set by the originator of a query
-                and copied into the response.  The values are:
-
-                0               a standard query (QUERY)
-
-                1               an inverse query (IQUERY)
-
-                2               a server status request (STATUS)
-
-                3-15            reserved for future use
-
-AA              Authoritative Answer - this bit is valid in responses,
-                and specifies that the responding name server is an
-                authority for the domain name in question section.
-
-                Note that the contents of the answer section may have
-                multiple owner names because of aliases.  The AA bit
-
-
-
-Mockapetris                                                    [Page 26]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-                corresponds to the name which matches the query name, or
-                the first owner name in the answer section.
-
-TC              TrunCation - specifies that this message was truncated
-                due to length greater than that permitted on the
-                transmission channel.
-
-RD              Recursion Desired - this bit may be set in a query and
-                is copied into the response.  If RD is set, it directs
-                the name server to pursue the query recursively.
-                Recursive query support is optional.
-
-RA              Recursion Available - this be is set or cleared in a
-                response, and denotes whether recursive query support is
-                available in the name server.
-
-Z               Reserved for future use.  Must be zero in all queries
-                and responses.
-
-RCODE           Response code - this 4 bit field is set as part of
-                responses.  The values have the following
-                interpretation:
-
-                0               No error condition
-
-                1               Format error - The name server was
-                                unable to interpret the query.
-
-                2               Server failure - The name server was
-                                unable to process this query due to a
-                                problem with the name server.
-
-                3               Name Error - Meaningful only for
-                                responses from an authoritative name
-                                server, this code signifies that the
-                                domain name referenced in the query does
-                                not exist.
-
-                4               Not Implemented - The name server does
-                                not support the requested kind of query.
-
-                5               Refused - The name server refuses to
-                                perform the specified operation for
-                                policy reasons.  For example, a name
-                                server may not wish to provide the
-                                information to the particular requester,
-                                or a name server may not wish to perform
-                                a particular operation (e.g., zone
-
-
-
-Mockapetris                                                    [Page 27]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-                                transfer) for particular data.
-
-                6-15            Reserved for future use.
-
-QDCOUNT         an unsigned 16 bit integer specifying the number of
-                entries in the question section.
-
-ANCOUNT         an unsigned 16 bit integer specifying the number of
-                resource records in the answer section.
-
-NSCOUNT         an unsigned 16 bit integer specifying the number of name
-                server resource records in the authority records
-                section.
-
-ARCOUNT         an unsigned 16 bit integer specifying the number of
-                resource records in the additional records section.
-
-4.1.2. Question section format
-
-The question section is used to carry the "question" in most queries,
-i.e., the parameters that define what is being asked.  The section
-contains QDCOUNT (usually 1) entries, each of the following format:
-
-                                    1  1  1  1  1  1
-      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                                               |
-    /                     QNAME                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                     QTYPE                     |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                     QCLASS                    |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-QNAME           a domain name represented as a sequence of labels, where
-                each label consists of a length octet followed by that
-                number of octets.  The domain name terminates with the
-                zero length octet for the null label of the root.  Note
-                that this field may be an odd number of octets; no
-                padding is used.
-
-QTYPE           a two octet code which specifies the type of the query.
-                The values for this field include all codes valid for a
-                TYPE field, together with some more general codes which
-                can match more than one type of RR.
-
-
-
-Mockapetris                                                    [Page 28]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-QCLASS          a two octet code that specifies the class of the query.
-                For example, the QCLASS field is IN for the Internet.
-
-4.1.3. Resource record format
-
-The answer, authority, and additional sections all share the same
-format: a variable number of resource records, where the number of
-records is specified in the corresponding count field in the header.
-Each resource record has the following format:
-                                    1  1  1  1  1  1
-      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                                               |
-    /                                               /
-    /                      NAME                     /
-    |                                               |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                      TYPE                     |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                     CLASS                     |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                      TTL                      |
-    |                                               |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    |                   RDLENGTH                    |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
-    /                     RDATA                     /
-    /                                               /
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-NAME            a domain name to which this resource record pertains.
-
-TYPE            two octets containing one of the RR type codes.  This
-                field specifies the meaning of the data in the RDATA
-                field.
-
-CLASS           two octets which specify the class of the data in the
-                RDATA field.
-
-TTL             a 32 bit unsigned integer that specifies the time
-                interval (in seconds) that the resource record may be
-                cached before it should be discarded.  Zero values are
-                interpreted to mean that the RR can only be used for the
-                transaction in progress, and should not be cached.
-
-
-
-
-
-Mockapetris                                                    [Page 29]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-RDLENGTH        an unsigned 16 bit integer that specifies the length in
-                octets of the RDATA field.
-
-RDATA           a variable length string of octets that describes the
-                resource.  The format of this information varies
-                according to the TYPE and CLASS of the resource record.
-                For example, the if the TYPE is A and the CLASS is IN,
-                the RDATA field is a 4 octet ARPA Internet address.
-
-4.1.4. Message compression
-
-In order to reduce the size of messages, the domain system utilizes a
-compression scheme which eliminates the repetition of domain names in a
-message.  In this scheme, an entire domain name or a list of labels at
-the end of a domain name is replaced with a pointer to a prior occurance
-of the same name.
-
-The pointer takes the form of a two octet sequence:
-
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    | 1  1|                OFFSET                   |
-    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-The first two bits are ones.  This allows a pointer to be distinguished
-from a label, since the label must begin with two zero bits because
-labels are restricted to 63 octets or less.  (The 10 and 01 combinations
-are reserved for future use.)  The OFFSET field specifies an offset from
-the start of the message (i.e., the first octet of the ID field in the
-domain header).  A zero offset specifies the first byte of the ID field,
-etc.
-
-The compression scheme allows a domain name in a message to be
-represented as either:
-
-   - a sequence of labels ending in a zero octet
-
-   - a pointer
-
-   - a sequence of labels ending with a pointer
-
-Pointers can only be used for occurances of a domain name where the
-format is not class specific.  If this were not the case, a name server
-or resolver would be required to know the format of all RRs it handled.
-As yet, there are no such cases, but they may occur in future RDATA
-formats.
-
-If a domain name is contained in a part of the message subject to a
-length field (such as the RDATA section of an RR), and compression is
-
-
-
-Mockapetris                                                    [Page 30]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-used, the length of the compressed name is used in the length
-calculation, rather than the length of the expanded name.
-
-Programs are free to avoid using pointers in messages they generate,
-although this will reduce datagram capacity, and may cause truncation.
-However all programs are required to understand arriving messages that
-contain pointers.
-
-For example, a datagram might need to use the domain names F.ISI.ARPA,
-FOO.F.ISI.ARPA, ARPA, and the root.  Ignoring the other fields of the
-message, these domain names might be represented as:
-
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    20 |           1           |           F           |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    22 |           3           |           I           |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    24 |           S           |           I           |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    26 |           4           |           A           |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    28 |           R           |           P           |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    30 |           A           |           0           |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    40 |           3           |           F           |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    42 |           O           |           O           |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    44 | 1  1|                20                       |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    64 | 1  1|                26                       |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-    92 |           0           |                       |
-       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-The domain name for F.ISI.ARPA is shown at offset 20.  The domain name
-FOO.F.ISI.ARPA is shown at offset 40; this definition uses a pointer to
-concatenate a label for FOO to the previously defined F.ISI.ARPA.  The
-domain name ARPA is defined at offset 64 using a pointer to the ARPA
-component of the name F.ISI.ARPA at 20; note that this pointer relies on
-ARPA being the last label in the string at 20.  The root domain name is
-
-
-
-Mockapetris                                                    [Page 31]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-defined by a single octet of zeros at 92; the root domain name has no
-labels.
-
-4.2. Transport
-
-The DNS assumes that messages will be transmitted as datagrams or in a
-byte stream carried by a virtual circuit.  While virtual circuits can be
-used for any DNS activity, datagrams are preferred for queries due to
-their lower overhead and better performance.  Zone refresh activities
-must use virtual circuits because of the need for reliable transfer.
-
-The Internet supports name server access using TCP [RFC-793] on server
-port 53 (decimal) as well as datagram access using UDP [RFC-768] on UDP
-port 53 (decimal).
-
-4.2.1. UDP usage
-
-Messages sent using UDP user server port 53 (decimal).
-
-Messages carried by UDP are restricted to 512 bytes (not counting the IP
-or UDP headers).  Longer messages are truncated and the TC bit is set in
-the header.
-
-UDP is not acceptable for zone transfers, but is the recommended method
-for standard queries in the Internet.  Queries sent using UDP may be
-lost, and hence a retransmission strategy is required.  Queries or their
-responses may be reordered by the network, or by processing in name
-servers, so resolvers should not depend on them being returned in order.
-
-The optimal UDP retransmission policy will vary with performance of the
-Internet and the needs of the client, but the following are recommended:
-
-   - The client should try other servers and server addresses
-     before repeating a query to a specific address of a server.
-
-   - The retransmission interval should be based on prior
-     statistics if possible.  Too aggressive retransmission can
-     easily slow responses for the community at large.  Depending
-     on how well connected the client is to its expected servers,
-     the minimum retransmission interval should be 2-5 seconds.
-
-More suggestions on server selection and retransmission policy can be
-found in the resolver section of this memo.
-
-4.2.2. TCP usage
-
-Messages sent over TCP connections use server port 53 (decimal).  The
-message is prefixed with a two byte length field which gives the message
-
-
-
-Mockapetris                                                    [Page 32]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-length, excluding the two byte length field.  This length field allows
-the low-level processing to assemble a complete message before beginning
-to parse it.
-
-Several connection management policies are recommended:
-
-   - The server should not block other activities waiting for TCP
-     data.
-
-   - The server should support multiple connections.
-
-   - The server should assume that the client will initiate
-     connection closing, and should delay closing its end of the
-     connection until all outstanding client requests have been
-     satisfied.
-
-   - If the server needs to close a dormant connection to reclaim
-     resources, it should wait until the connection has been idle
-     for a period on the order of two minutes.  In particular, the
-     server should allow the SOA and AXFR request sequence (which
-     begins a refresh operation) to be made on a single connection.
-     Since the server would be unable to answer queries anyway, a
-     unilateral close or reset may be used instead of a graceful
-     close.
-
-5. MASTER FILES
-
-Master files are text files that contain RRs in text form.  Since the
-contents of a zone can be expressed in the form of a list of RRs a
-master file is most often used to define a zone, though it can be used
-to list a cache's contents.  Hence, this section first discusses the
-format of RRs in a master file, and then the special considerations when
-a master file is used to create a zone in some name server.
-
-5.1. Format
-
-The format of these files is a sequence of entries.  Entries are
-predominantly line-oriented, though parentheses can be used to continue
-a list of items across a line boundary, and text literals can contain
-CRLF within the text.  Any combination of tabs and spaces act as a
-delimiter between the separate items that make up an entry.  The end of
-any line in the master file can end with a comment.  The comment starts
-with a ";" (semicolon).
-
-The following entries are defined:
-
-    <blank>[<comment>]
-
-
-
-
-Mockapetris                                                    [Page 33]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-    $ORIGIN <domain-name> [<comment>]
-
-    $INCLUDE <file-name> [<domain-name>] [<comment>]
-
-    <domain-name><rr> [<comment>]
-
-    <blank><rr> [<comment>]
-
-Blank lines, with or without comments, are allowed anywhere in the file.
-
-Two control entries are defined: $ORIGIN and $INCLUDE.  $ORIGIN is
-followed by a domain name, and resets the current origin for relative
-domain names to the stated name.  $INCLUDE inserts the named file into
-the current file, and may optionally specify a domain name that sets the
-relative domain name origin for the included file.  $INCLUDE may also
-have a comment.  Note that a $INCLUDE entry never changes the relative
-origin of the parent file, regardless of changes to the relative origin
-made within the included file.
-
-The last two forms represent RRs.  If an entry for an RR begins with a
-blank, then the RR is assumed to be owned by the last stated owner.  If
-an RR entry begins with a <domain-name>, then the owner name is reset.
-
-<rr> contents take one of the following forms:
-
-    [<TTL>] [<class>] <type> <RDATA>
-
-    [<class>] [<TTL>] <type> <RDATA>
-
-The RR begins with optional TTL and class fields, followed by a type and
-RDATA field appropriate to the type and class.  Class and type use the
-standard mnemonics, TTL is a decimal integer.  Omitted class and TTL
-values are default to the last explicitly stated values.  Since type and
-class mnemonics are disjoint, the parse is unique.  (Note that this
-order is different from the order used in examples and the order used in
-the actual RRs; the given order allows easier parsing and defaulting.)
-
-<domain-name>s make up a large share of the data in the master file.
-The labels in the domain name are expressed as character strings and
-separated by dots.  Quoting conventions allow arbitrary characters to be
-stored in domain names.  Domain names that end in a dot are called
-absolute, and are taken as complete.  Domain names which do not end in a
-dot are called relative; the actual domain name is the concatenation of
-the relative part with an origin specified in a $ORIGIN, $INCLUDE, or as
-an argument to the master file loading routine.  A relative name is an
-error when no origin is available.
-
-
-
-
-
-Mockapetris                                                    [Page 34]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-<character-string> is expressed in one or two ways: as a contiguous set
-of characters without interior spaces, or as a string beginning with a "
-and ending with a ".  Inside a " delimited string any character can
-occur, except for a " itself, which must be quoted using \ (back slash).
-
-Because these files are text files several special encodings are
-necessary to allow arbitrary data to be loaded.  In particular:
-
-                of the root.
-
-@               A free standing @ is used to denote the current origin.
-
-\X              where X is any character other than a digit (0-9), is
-                used to quote that character so that its special meaning
-                does not apply.  For example, "\." can be used to place
-                a dot character in a label.
-
-\DDD            where each D is a digit is the octet corresponding to
-                the decimal number described by DDD.  The resulting
-                octet is assumed to be text and is not checked for
-                special meaning.
-
-( )             Parentheses are used to group data that crosses a line
-                boundary.  In effect, line terminations are not
-                recognized within parentheses.
-
-;               Semicolon is used to start a comment; the remainder of
-                the line is ignored.
-
-5.2. Use of master files to define zones
-
-When a master file is used to load a zone, the operation should be
-suppressed if any errors are encountered in the master file.  The
-rationale for this is that a single error can have widespread
-consequences.  For example, suppose that the RRs defining a delegation
-have syntax errors; then the server will return authoritative name
-errors for all names in the subzone (except in the case where the
-subzone is also present on the server).
-
-Several other validity checks that should be performed in addition to
-insuring that the file is syntactically correct:
-
-   1. All RRs in the file should have the same class.
-
-   2. Exactly one SOA RR should be present at the top of the zone.
-
-   3. If delegations are present and glue information is required,
-      it should be present.
-
-
-
-Mockapetris                                                    [Page 35]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-   4. Information present outside of the authoritative nodes in the
-      zone should be glue information, rather than the result of an
-      origin or similar error.
-
-5.3. Master file example
-
-The following is an example file which might be used to define the
-ISI.EDU zone.and is loaded with an origin of ISI.EDU:
-
-@   IN  SOA     VENERA      Action\.domains (
-                                 20     ; SERIAL
-                                 7200   ; REFRESH
-                                 600    ; RETRY
-                                 3600000; EXPIRE
-                                 60)    ; MINIMUM
-
-        NS      A.ISI.EDU.
-        NS      VENERA
-        NS      VAXA
-        MX      10      VENERA
-        MX      20      VAXA
-
-A       A       26.3.0.103
-
-VENERA  A       10.1.0.52
-        A       128.9.0.32
-
-VAXA    A       10.2.0.27
-        A       128.9.0.33
-
-
-$INCLUDE <SUBSYS>ISI-MAILBOXES.TXT
-
-Where the file <SUBSYS>ISI-MAILBOXES.TXT is:
-
-    MOE     MB      A.ISI.EDU.
-    LARRY   MB      A.ISI.EDU.
-    CURLEY  MB      A.ISI.EDU.
-    STOOGES MG      MOE
-            MG      LARRY
-            MG      CURLEY
-
-Note the use of the \ character in the SOA RR to specify the responsible
-person mailbox "Action.domains@E.ISI.EDU".
-
-
-
-
-
-
-
-Mockapetris                                                    [Page 36]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-6. NAME SERVER IMPLEMENTATION
-
-6.1. Architecture
-
-The optimal structure for the name server will depend on the host
-operating system and whether the name server is integrated with resolver
-operations, either by supporting recursive service, or by sharing its
-database with a resolver.  This section discusses implementation
-considerations for a name server which shares a database with a
-resolver, but most of these concerns are present in any name server.
-
-6.1.1. Control
-
-A name server must employ multiple concurrent activities, whether they
-are implemented as separate tasks in the host's OS or multiplexing
-inside a single name server program.  It is simply not acceptable for a
-name server to block the service of UDP requests while it waits for TCP
-data for refreshing or query activities.  Similarly, a name server
-should not attempt to provide recursive service without processing such
-requests in parallel, though it may choose to serialize requests from a
-single client, or to regard identical requests from the same client as
-duplicates.  A name server should not substantially delay requests while
-it reloads a zone from master files or while it incorporates a newly
-refreshed zone into its database.
-
-6.1.2. Database
-
-While name server implementations are free to use any internal data
-structures they choose, the suggested structure consists of three major
-parts:
-
-   - A "catalog" data structure which lists the zones available to
-     this server, and a "pointer" to the zone data structure.  The
-     main purpose of this structure is to find the nearest ancestor
-     zone, if any, for arriving standard queries.
-
-   - Separate data structures for each of the zones held by the
-     name server.
-
-   - A data structure for cached data. (or perhaps separate caches
-     for different classes)
-
-All of these data structures can be implemented an identical tree
-structure format, with different data chained off the nodes in different
-parts: in the catalog the data is pointers to zones, while in the zone
-and cache data structures, the data will be RRs.  In designing the tree
-framework the designer should recognize that query processing will need
-to traverse the tree using case-insensitive label comparisons; and that
-
-
-
-Mockapetris                                                    [Page 37]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-in real data, a few nodes have a very high branching factor (100-1000 or
-more), but the vast majority have a very low branching factor (0-1).
-
-One way to solve the case problem is to store the labels for each node
-in two pieces: a standardized-case representation of the label where all
-ASCII characters are in a single case, together with a bit mask that
-denotes which characters are actually of a different case.  The
-branching factor diversity can be handled using a simple linked list for
-a node until the branching factor exceeds some threshold, and
-transitioning to a hash structure after the threshold is exceeded.  In
-any case, hash structures used to store tree sections must insure that
-hash functions and procedures preserve the casing conventions of the
-DNS.
-
-The use of separate structures for the different parts of the database
-is motivated by several factors:
-
-   - The catalog structure can be an almost static structure that
-     need change only when the system administrator changes the
-     zones supported by the server.  This structure can also be
-     used to store parameters used to control refreshing
-     activities.
-
-   - The individual data structures for zones allow a zone to be
-     replaced simply by changing a pointer in the catalog.  Zone
-     refresh operations can build a new structure and, when
-     complete, splice it into the database via a simple pointer
-     replacement.  It is very important that when a zone is
-     refreshed, queries should not use old and new data
-     simultaneously.
-
-   - With the proper search procedures, authoritative data in zones
-     will always "hide", and hence take precedence over, cached
-     data.
-
-   - Errors in zone definitions that cause overlapping zones, etc.,
-     may cause erroneous responses to queries, but problem
-     determination is simplified, and the contents of one "bad"
-     zone can't corrupt another.
-
-   - Since the cache is most frequently updated, it is most
-     vulnerable to corruption during system restarts.  It can also
-     become full of expired RR data.  In either case, it can easily
-     be discarded without disturbing zone data.
-
-A major aspect of database design is selecting a structure which allows
-the name server to deal with crashes of the name server's host.  State
-information which a name server should save across system crashes
-
-
-
-Mockapetris                                                    [Page 38]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-includes the catalog structure (including the state of refreshing for
-each zone) and the zone data itself.
-
-6.1.3. Time
-
-Both the TTL data for RRs and the timing data for refreshing activities
-depends on 32 bit timers in units of seconds.  Inside the database,
-refresh timers and TTLs for cached data conceptually "count down", while
-data in the zone stays with constant TTLs.
-
-A recommended implementation strategy is to store time in two ways:  as
-a relative increment and as an absolute time.  One way to do this is to
-use positive 32 bit numbers for one type and negative numbers for the
-other.  The RRs in zones use relative times; the refresh timers and
-cache data use absolute times.  Absolute numbers are taken with respect
-to some known origin and converted to relative values when placed in the
-response to a query.  When an absolute TTL is negative after conversion
-to relative, then the data is expired and should be ignored.
-
-6.2. Standard query processing
-
-The major algorithm for standard query processing is presented in
-[RFC-1034].
-
-When processing queries with QCLASS=*, or some other QCLASS which
-matches multiple classes, the response should never be authoritative
-unless the server can guarantee that the response covers all classes.
-
-When composing a response, RRs which are to be inserted in the
-additional section, but duplicate RRs in the answer or authority
-sections, may be omitted from the additional section.
-
-When a response is so long that truncation is required, the truncation
-should start at the end of the response and work forward in the
-datagram.  Thus if there is any data for the authority section, the
-answer section is guaranteed to be unique.
-
-The MINIMUM value in the SOA should be used to set a floor on the TTL of
-data distributed from a zone.  This floor function should be done when
-the data is copied into a response.  This will allow future dynamic
-update protocols to change the SOA MINIMUM field without ambiguous
-semantics.
-
-6.3. Zone refresh and reload processing
-
-In spite of a server's best efforts, it may be unable to load zone data
-from a master file due to syntax errors, etc., or be unable to refresh a
-zone within the its expiration parameter.  In this case, the name server
-
-
-
-Mockapetris                                                    [Page 39]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-should answer queries as if it were not supposed to possess the zone.
-
-If a master is sending a zone out via AXFR, and a new version is created
-during the transfer, the master should continue to send the old version
-if possible.  In any case, it should never send part of one version and
-part of another.  If completion is not possible, the master should reset
-the connection on which the zone transfer is taking place.
-
-6.4. Inverse queries (Optional)
-
-Inverse queries are an optional part of the DNS.  Name servers are not
-required to support any form of inverse queries.  If a name server
-receives an inverse query that it does not support, it returns an error
-response with the "Not Implemented" error set in the header.  While
-inverse query support is optional, all name servers must be at least
-able to return the error response.
-
-6.4.1. The contents of inverse queries and responses          Inverse
-queries reverse the mappings performed by standard query operations;
-while a standard query maps a domain name to a resource, an inverse
-query maps a resource to a domain name.  For example, a standard query
-might bind a domain name to a host address; the corresponding inverse
-query binds the host address to a domain name.
-
-Inverse queries take the form of a single RR in the answer section of
-the message, with an empty question section.  The owner name of the
-query RR and its TTL are not significant.  The response carries
-questions in the question section which identify all names possessing
-the query RR WHICH THE NAME SERVER KNOWS.  Since no name server knows
-about all of the domain name space, the response can never be assumed to
-be complete.  Thus inverse queries are primarily useful for database
-management and debugging activities.  Inverse queries are NOT an
-acceptable method of mapping host addresses to host names; use the IN-
-ADDR.ARPA domain instead.
-
-Where possible, name servers should provide case-insensitive comparisons
-for inverse queries.  Thus an inverse query asking for an MX RR of
-"Venera.isi.edu" should get the same response as a query for
-"VENERA.ISI.EDU"; an inverse query for HINFO RR "IBM-PC UNIX" should
-produce the same result as an inverse query for "IBM-pc unix".  However,
-this cannot be guaranteed because name servers may possess RRs that
-contain character strings but the name server does not know that the
-data is character.
-
-When a name server processes an inverse query, it either returns:
-
-   1. zero, one, or multiple domain names for the specified
-      resource as QNAMEs in the question section
-
-
-
-Mockapetris                                                    [Page 40]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-   2. an error code indicating that the name server doesn't support
-      inverse mapping of the specified resource type.
-
-When the response to an inverse query contains one or more QNAMEs, the
-owner name and TTL of the RR in the answer section which defines the
-inverse query is modified to exactly match an RR found at the first
-QNAME.
-
-RRs returned in the inverse queries cannot be cached using the same
-mechanism as is used for the replies to standard queries.  One reason
-for this is that a name might have multiple RRs of the same type, and
-only one would appear.  For example, an inverse query for a single
-address of a multiply homed host might create the impression that only
-one address existed.
-
-6.4.2. Inverse query and response example          The overall structure
-of an inverse query for retrieving the domain name that corresponds to
-Internet address 10.1.0.52 is shown below:
-
-                         +-----------------------------------------+
-           Header        |          OPCODE=IQUERY, ID=997          |
-                         +-----------------------------------------+
-          Question       |                 <empty>                 |
-                         +-----------------------------------------+
-           Answer        |        <anyname> A IN 10.1.0.52         |
-                         +-----------------------------------------+
-          Authority      |                 <empty>                 |
-                         +-----------------------------------------+
-         Additional      |                 <empty>                 |
-                         +-----------------------------------------+
-
-This query asks for a question whose answer is the Internet style
-address 10.1.0.52.  Since the owner name is not known, any domain name
-can be used as a placeholder (and is ignored).  A single octet of zero,
-signifying the root, is usually used because it minimizes the length of
-the message.  The TTL of the RR is not significant.  The response to
-this query might be:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Mockapetris                                                    [Page 41]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-                         +-----------------------------------------+
-           Header        |         OPCODE=RESPONSE, ID=997         |
-                         +-----------------------------------------+
-          Question       |QTYPE=A, QCLASS=IN, QNAME=VENERA.ISI.EDU |
-                         +-----------------------------------------+
-           Answer        |  VENERA.ISI.EDU  A IN 10.1.0.52         |
-                         +-----------------------------------------+
-          Authority      |                 <empty>                 |
-                         +-----------------------------------------+
-         Additional      |                 <empty>                 |
-                         +-----------------------------------------+
-
-Note that the QTYPE in a response to an inverse query is the same as the
-TYPE field in the answer section of the inverse query.  Responses to
-inverse queries may contain multiple questions when the inverse is not
-unique.  If the question section in the response is not empty, then the
-RR in the answer section is modified to correspond to be an exact copy
-of an RR at the first QNAME.
-
-6.4.3. Inverse query processing
-
-Name servers that support inverse queries can support these operations
-through exhaustive searches of their databases, but this becomes
-impractical as the size of the database increases.  An alternative
-approach is to invert the database according to the search key.
-
-For name servers that support multiple zones and a large amount of data,
-the recommended approach is separate inversions for each zone.  When a
-particular zone is changed during a refresh, only its inversions need to
-be redone.
-
-Support for transfer of this type of inversion may be included in future
-versions of the domain system, but is not supported in this version.
-
-6.5. Completion queries and responses
-
-The optional completion services described in RFC-882 and RFC-883 have
-been deleted.  Redesigned services may become available in the future.
-
-
-
-
-
-
-
-
-
-
-
-
-
-Mockapetris                                                    [Page 42]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-7. RESOLVER IMPLEMENTATION
-
-The top levels of the recommended resolver algorithm are discussed in
-[RFC-1034].  This section discusses implementation details assuming the
-database structure suggested in the name server implementation section
-of this memo.
-
-7.1. Transforming a user request into a query
-
-The first step a resolver takes is to transform the client's request,
-stated in a format suitable to the local OS, into a search specification
-for RRs at a specific name which match a specific QTYPE and QCLASS.
-Where possible, the QTYPE and QCLASS should correspond to a single type
-and a single class, because this makes the use of cached data much
-simpler.  The reason for this is that the presence of data of one type
-in a cache doesn't confirm the existence or non-existence of data of
-other types, hence the only way to be sure is to consult an
-authoritative source.  If QCLASS=* is used, then authoritative answers
-won't be available.
-
-Since a resolver must be able to multiplex multiple requests if it is to
-perform its function efficiently, each pending request is usually
-represented in some block of state information.  This state block will
-typically contain:
-
-   - A timestamp indicating the time the request began.
-     The timestamp is used to decide whether RRs in the database
-     can be used or are out of date.  This timestamp uses the
-     absolute time format previously discussed for RR storage in
-     zones and caches.  Note that when an RRs TTL indicates a
-     relative time, the RR must be timely, since it is part of a
-     zone.  When the RR has an absolute time, it is part of a
-     cache, and the TTL of the RR is compared against the timestamp
-     for the start of the request.
-
-     Note that using the timestamp is superior to using a current
-     time, since it allows RRs with TTLs of zero to be entered in
-     the cache in the usual manner, but still used by the current
-     request, even after intervals of many seconds due to system
-     load, query retransmission timeouts, etc.
-
-   - Some sort of parameters to limit the amount of work which will
-     be performed for this request.
-
-     The amount of work which a resolver will do in response to a
-     client request must be limited to guard against errors in the
-     database, such as circular CNAME references, and operational
-     problems, such as network partition which prevents the
-
-
-
-Mockapetris                                                    [Page 43]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-     resolver from accessing the name servers it needs.  While
-     local limits on the number of times a resolver will retransmit
-     a particular query to a particular name server address are
-     essential, the resolver should have a global per-request
-     counter to limit work on a single request.  The counter should
-     be set to some initial value and decremented whenever the
-     resolver performs any action (retransmission timeout,
-     retransmission, etc.)  If the counter passes zero, the request
-     is terminated with a temporary error.
-
-     Note that if the resolver structure allows one request to
-     start others in parallel, such as when the need to access a
-     name server for one request causes a parallel resolve for the
-     name server's addresses, the spawned request should be started
-     with a lower counter.  This prevents circular references in
-     the database from starting a chain reaction of resolver
-     activity.
-
-   - The SLIST data structure discussed in [RFC-1034].
-
-     This structure keeps track of the state of a request if it
-     must wait for answers from foreign name servers.
-
-7.2. Sending the queries
-
-As described in [RFC-1034], the basic task of the resolver is to
-formulate a query which will answer the client's request and direct that
-query to name servers which can provide the information.  The resolver
-will usually only have very strong hints about which servers to ask, in
-the form of NS RRs, and may have to revise the query, in response to
-CNAMEs, or revise the set of name servers the resolver is asking, in
-response to delegation responses which point the resolver to name
-servers closer to the desired information.  In addition to the
-information requested by the client, the resolver may have to call upon
-its own services to determine the address of name servers it wishes to
-contact.
-
-In any case, the model used in this memo assumes that the resolver is
-multiplexing attention between multiple requests, some from the client,
-and some internally generated.  Each request is represented by some
-state information, and the desired behavior is that the resolver
-transmit queries to name servers in a way that maximizes the probability
-that the request is answered, minimizes the time that the request takes,
-and avoids excessive transmissions.  The key algorithm uses the state
-information of the request to select the next name server address to
-query, and also computes a timeout which will cause the next action
-should a response not arrive.  The next action will usually be a
-transmission to some other server, but may be a temporary error to the
-
-
-
-Mockapetris                                                    [Page 44]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-client.
-
-The resolver always starts with a list of server names to query (SLIST).
-This list will be all NS RRs which correspond to the nearest ancestor
-zone that the resolver knows about.  To avoid startup problems, the
-resolver should have a set of default servers which it will ask should
-it have no current NS RRs which are appropriate.  The resolver then adds
-to SLIST all of the known addresses for the name servers, and may start
-parallel requests to acquire the addresses of the servers when the
-resolver has the name, but no addresses, for the name servers.
-
-To complete initialization of SLIST, the resolver attaches whatever
-history information it has to the each address in SLIST.  This will
-usually consist of some sort of weighted averages for the response time
-of the address, and the batting average of the address (i.e., how often
-the address responded at all to the request).  Note that this
-information should be kept on a per address basis, rather than on a per
-name server basis, because the response time and batting average of a
-particular server may vary considerably from address to address.  Note
-also that this information is actually specific to a resolver address /
-server address pair, so a resolver with multiple addresses may wish to
-keep separate histories for each of its addresses.  Part of this step
-must deal with addresses which have no such history; in this case an
-expected round trip time of 5-10 seconds should be the worst case, with
-lower estimates for the same local network, etc.
-
-Note that whenever a delegation is followed, the resolver algorithm
-reinitializes SLIST.
-
-The information establishes a partial ranking of the available name
-server addresses.  Each time an address is chosen and the state should
-be altered to prevent its selection again until all other addresses have
-been tried.  The timeout for each transmission should be 50-100% greater
-than the average predicted value to allow for variance in response.
-
-Some fine points:
-
-   - The resolver may encounter a situation where no addresses are
-     available for any of the name servers named in SLIST, and
-     where the servers in the list are precisely those which would
-     normally be used to look up their own addresses.  This
-     situation typically occurs when the glue address RRs have a
-     smaller TTL than the NS RRs marking delegation, or when the
-     resolver caches the result of a NS search.  The resolver
-     should detect this condition and restart the search at the
-     next ancestor zone, or alternatively at the root.
-
-
-
-
-
-Mockapetris                                                    [Page 45]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-   - If a resolver gets a server error or other bizarre response
-     from a name server, it should remove it from SLIST, and may
-     wish to schedule an immediate transmission to the next
-     candidate server address.
-
-7.3. Processing responses
-
-The first step in processing arriving response datagrams is to parse the
-response.  This procedure should include:
-
-   - Check the header for reasonableness.  Discard datagrams which
-     are queries when responses are expected.
-
-   - Parse the sections of the message, and insure that all RRs are
-     correctly formatted.
-
-   - As an optional step, check the TTLs of arriving data looking
-     for RRs with excessively long TTLs.  If a RR has an
-     excessively long TTL, say greater than 1 week, either discard
-     the whole response, or limit all TTLs in the response to 1
-     week.
-
-The next step is to match the response to a current resolver request.
-The recommended strategy is to do a preliminary matching using the ID
-field in the domain header, and then to verify that the question section
-corresponds to the information currently desired.  This requires that
-the transmission algorithm devote several bits of the domain ID field to
-a request identifier of some sort.  This step has several fine points:
-
-   - Some name servers send their responses from different
-     addresses than the one used to receive the query.  That is, a
-     resolver cannot rely that a response will come from the same
-     address which it sent the corresponding query to.  This name
-     server bug is typically encountered in UNIX systems.
-
-   - If the resolver retransmits a particular request to a name
-     server it should be able to use a response from any of the
-     transmissions.  However, if it is using the response to sample
-     the round trip time to access the name server, it must be able
-     to determine which transmission matches the response (and keep
-     transmission times for each outgoing message), or only
-     calculate round trip times based on initial transmissions.
-
-   - A name server will occasionally not have a current copy of a
-     zone which it should have according to some NS RRs.  The
-     resolver should simply remove the name server from the current
-     SLIST, and continue.
-
-
-
-
-Mockapetris                                                    [Page 46]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-7.4. Using the cache
-
-In general, we expect a resolver to cache all data which it receives in
-responses since it may be useful in answering future client requests.
-However, there are several types of data which should not be cached:
-
-   - When several RRs of the same type are available for a
-     particular owner name, the resolver should either cache them
-     all or none at all.  When a response is truncated, and a
-     resolver doesn't know whether it has a complete set, it should
-     not cache a possibly partial set of RRs.
-
-   - Cached data should never be used in preference to
-     authoritative data, so if caching would cause this to happen
-     the data should not be cached.
-
-   - The results of an inverse query should not be cached.
-
-   - The results of standard queries where the QNAME contains "*"
-     labels if the data might be used to construct wildcards.  The
-     reason is that the cache does not necessarily contain existing
-     RRs or zone boundary information which is necessary to
-     restrict the application of the wildcard RRs.
-
-   - RR data in responses of dubious reliability.  When a resolver
-     receives unsolicited responses or RR data other than that
-     requested, it should discard it without caching it.  The basic
-     implication is that all sanity checks on a packet should be
-     performed before any of it is cached.
-
-In a similar vein, when a resolver has a set of RRs for some name in a
-response, and wants to cache the RRs, it should check its cache for
-already existing RRs.  Depending on the circumstances, either the data
-in the response or the cache is preferred, but the two should never be
-combined.  If the data in the response is from authoritative data in the
-answer section, it is always preferred.
-
-8. MAIL SUPPORT
-
-The domain system defines a standard for mapping mailboxes into domain
-names, and two methods for using the mailbox information to derive mail
-routing information.  The first method is called mail exchange binding
-and the other method is mailbox binding.  The mailbox encoding standard
-and mail exchange binding are part of the DNS official protocol, and are
-the recommended method for mail routing in the Internet.  Mailbox
-binding is an experimental feature which is still under development and
-subject to change.
-
-
-
-
-Mockapetris                                                    [Page 47]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-The mailbox encoding standard assumes a mailbox name of the form
-"<local-part>@<mail-domain>".  While the syntax allowed in each of these
-sections varies substantially between the various mail internets, the
-preferred syntax for the ARPA Internet is given in [RFC-822].
-
-The DNS encodes the <local-part> as a single label, and encodes the
-<mail-domain> as a domain name.  The single label from the <local-part>
-is prefaced to the domain name from <mail-domain> to form the domain
-name corresponding to the mailbox.  Thus the mailbox HOSTMASTER@SRI-
-NIC.ARPA is mapped into the domain name HOSTMASTER.SRI-NIC.ARPA.  If the
-<local-part> contains dots or other special characters, its
-representation in a master file will require the use of backslash
-quoting to ensure that the domain name is properly encoded.  For
-example, the mailbox Action.domains@ISI.EDU would be represented as
-Action\.domains.ISI.EDU.
-
-8.1. Mail exchange binding
-
-Mail exchange binding uses the <mail-domain> part of a mailbox
-specification to determine where mail should be sent.  The <local-part>
-is not even consulted.  [RFC-974] specifies this method in detail, and
-should be consulted before attempting to use mail exchange support.
-
-One of the advantages of this method is that it decouples mail
-destination naming from the hosts used to support mail service, at the
-cost of another layer of indirection in the lookup function.  However,
-the addition layer should eliminate the need for complicated "%", "!",
-etc encodings in <local-part>.
-
-The essence of the method is that the <mail-domain> is used as a domain
-name to locate type MX RRs which list hosts willing to accept mail for
-<mail-domain>, together with preference values which rank the hosts
-according to an order specified by the administrators for <mail-domain>.
-
-In this memo, the <mail-domain> ISI.EDU is used in examples, together
-with the hosts VENERA.ISI.EDU and VAXA.ISI.EDU as mail exchanges for
-ISI.EDU.  If a mailer had a message for Mockapetris@ISI.EDU, it would
-route it by looking up MX RRs for ISI.EDU.  The MX RRs at ISI.EDU name
-VENERA.ISI.EDU and VAXA.ISI.EDU, and type A queries can find the host
-addresses.
-
-8.2. Mailbox binding (Experimental)
-
-In mailbox binding, the mailer uses the entire mail destination
-specification to construct a domain name.  The encoded domain name for
-the mailbox is used as the QNAME field in a QTYPE=MAILB query.
-
-Several outcomes are possible for this query:
-
-
-
-Mockapetris                                                    [Page 48]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-   1. The query can return a name error indicating that the mailbox
-      does not exist as a domain name.
-
-      In the long term, this would indicate that the specified
-      mailbox doesn't exist.  However, until the use of mailbox
-      binding is universal, this error condition should be
-      interpreted to mean that the organization identified by the
-      global part does not support mailbox binding.  The
-      appropriate procedure is to revert to exchange binding at
-      this point.
-
-   2. The query can return a Mail Rename (MR) RR.
-
-      The MR RR carries new mailbox specification in its RDATA
-      field.  The mailer should replace the old mailbox with the
-      new one and retry the operation.
-
-   3. The query can return a MB RR.
-
-      The MB RR carries a domain name for a host in its RDATA
-      field.  The mailer should deliver the message to that host
-      via whatever protocol is applicable, e.g., b,SMTP.
-
-   4. The query can return one or more Mail Group (MG) RRs.
-
-      This condition means that the mailbox was actually a mailing
-      list or mail group, rather than a single mailbox.  Each MG RR
-      has a RDATA field that identifies a mailbox that is a member
-      of the group.  The mailer should deliver a copy of the
-      message to each member.
-
-   5. The query can return a MB RR as well as one or more MG RRs.
-
-      This condition means the the mailbox was actually a mailing
-      list.  The mailer can either deliver the message to the host
-      specified by the MB RR, which will in turn do the delivery to
-      all members, or the mailer can use the MG RRs to do the
-      expansion itself.
-
-In any of these cases, the response may include a Mail Information
-(MINFO) RR.  This RR is usually associated with a mail group, but is
-legal with a MB.  The MINFO RR identifies two mailboxes.  One of these
-identifies a responsible person for the original mailbox name.  This
-mailbox should be used for requests to be added to a mail group, etc.
-The second mailbox name in the MINFO RR identifies a mailbox that should
-receive error messages for mail failures.  This is particularly
-appropriate for mailing lists when errors in member names should be
-reported to a person other than the one who sends a message to the list.
-
-
-
-Mockapetris                                                    [Page 49]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-New fields may be added to this RR in the future.
-
-
-9. REFERENCES and BIBLIOGRAPHY
-
-[Dyer 87]       S. Dyer, F. Hsu, "Hesiod", Project Athena
-                Technical Plan - Name Service, April 1987, version 1.9.
-
-                Describes the fundamentals of the Hesiod name service.
-
-[IEN-116]       J. Postel, "Internet Name Server", IEN-116,
-                USC/Information Sciences Institute, August 1979.
-
-                A name service obsoleted by the Domain Name System, but
-                still in use.
-
-[Quarterman 86] J. Quarterman, and J. Hoskins, "Notable Computer Networks",
-                Communications of the ACM, October 1986, volume 29, number
-                10.
-
-[RFC-742]       K. Harrenstien, "NAME/FINGER", RFC-742, Network
-                Information Center, SRI International, December 1977.
-
-[RFC-768]       J. Postel, "User Datagram Protocol", RFC-768,
-                USC/Information Sciences Institute, August 1980.
-
-[RFC-793]       J. Postel, "Transmission Control Protocol", RFC-793,
-                USC/Information Sciences Institute, September 1981.
-
-[RFC-799]       D. Mills, "Internet Name Domains", RFC-799, COMSAT,
-                September 1981.
-
-                Suggests introduction of a hierarchy in place of a flat
-                name space for the Internet.
-
-[RFC-805]       J. Postel, "Computer Mail Meeting Notes", RFC-805,
-                USC/Information Sciences Institute, February 1982.
-
-[RFC-810]       E. Feinler, K. Harrenstien, Z. Su, and V. White, "DOD
-                Internet Host Table Specification", RFC-810, Network
-                Information Center, SRI International, March 1982.
-
-                Obsolete.  See RFC-952.
-
-[RFC-811]       K. Harrenstien, V. White, and E. Feinler, "Hostnames
-                Server", RFC-811, Network Information Center, SRI
-                International, March 1982.
-
-
-
-
-Mockapetris                                                    [Page 50]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-                Obsolete.  See RFC-953.
-
-[RFC-812]       K. Harrenstien, and V. White, "NICNAME/WHOIS", RFC-812,
-                Network Information Center, SRI International, March
-                1982.
-
-[RFC-819]       Z. Su, and J. Postel, "The Domain Naming Convention for
-                Internet User Applications", RFC-819, Network
-                Information Center, SRI International, August 1982.
-
-                Early thoughts on the design of the domain system.
-                Current implementation is completely different.
-
-[RFC-821]       J. Postel, "Simple Mail Transfer Protocol", RFC-821,
-                USC/Information Sciences Institute, August 1980.
-
-[RFC-830]       Z. Su, "A Distributed System for Internet Name Service",
-                RFC-830, Network Information Center, SRI International,
-                October 1982.
-
-                Early thoughts on the design of the domain system.
-                Current implementation is completely different.
-
-[RFC-882]       P. Mockapetris, "Domain names - Concepts and
-                Facilities," RFC-882, USC/Information Sciences
-                Institute, November 1983.
-
-                Superceeded by this memo.
-
-[RFC-883]       P. Mockapetris, "Domain names - Implementation and
-                Specification," RFC-883, USC/Information Sciences
-                Institute, November 1983.
-
-                Superceeded by this memo.
-
-[RFC-920]       J. Postel and J. Reynolds, "Domain Requirements",
-                RFC-920, USC/Information Sciences Institute,
-                October 1984.
-
-                Explains the naming scheme for top level domains.
-
-[RFC-952]       K. Harrenstien, M. Stahl, E. Feinler, "DoD Internet Host
-                Table Specification", RFC-952, SRI, October 1985.
-
-                Specifies the format of HOSTS.TXT, the host/address
-                table replaced by the DNS.
-
-
-
-
-
-Mockapetris                                                    [Page 51]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-[RFC-953]       K. Harrenstien, M. Stahl, E. Feinler, "HOSTNAME Server",
-                RFC-953, SRI, October 1985.
-
-                This RFC contains the official specification of the
-                hostname server protocol, which is obsoleted by the DNS.
-                This TCP based protocol accesses information stored in
-                the RFC-952 format, and is used to obtain copies of the
-                host table.
-
-[RFC-973]       P. Mockapetris, "Domain System Changes and
-                Observations", RFC-973, USC/Information Sciences
-                Institute, January 1986.
-
-                Describes changes to RFC-882 and RFC-883 and reasons for
-                them.
-
-[RFC-974]       C. Partridge, "Mail routing and the domain system",
-                RFC-974, CSNET CIC BBN Labs, January 1986.
-
-                Describes the transition from HOSTS.TXT based mail
-                addressing to the more powerful MX system used with the
-                domain system.
-
-[RFC-1001]      NetBIOS Working Group, "Protocol standard for a NetBIOS
-                service on a TCP/UDP transport: Concepts and Methods",
-                RFC-1001, March 1987.
-
-                This RFC and RFC-1002 are a preliminary design for
-                NETBIOS on top of TCP/IP which proposes to base NetBIOS
-                name service on top of the DNS.
-
-[RFC-1002]      NetBIOS Working Group, "Protocol standard for a NetBIOS
-                service on a TCP/UDP transport: Detailed
-                Specifications", RFC-1002, March 1987.
-
-[RFC-1010]      J. Reynolds, and J. Postel, "Assigned Numbers", RFC-1010,
-                USC/Information Sciences Institute, May 1987.
-
-                Contains socket numbers and mnemonics for host names,
-                operating systems, etc.
-
-[RFC-1031]      W. Lazear, "MILNET Name Domain Transition", RFC-1031,
-                November 1987.
-
-                Describes a plan for converting the MILNET to the DNS.
-
-[RFC-1032]      M. Stahl, "Establishing a Domain - Guidelines for
-                Administrators", RFC-1032, November 1987.
-
-
-
-Mockapetris                                                    [Page 52]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-                Describes the registration policies used by the NIC to
-                administer the top level domains and delegate subzones.
-
-[RFC-1033]      M. Lottor, "Domain Administrators Operations Guide",
-                RFC-1033, November 1987.
-
-                A cookbook for domain administrators.
-
-[Solomon 82]    M. Solomon, L. Landweber, and D. Neuhengen, "The CSNET
-                Name Server", Computer Networks, vol 6, nr 3, July 1982.
-
-                Describes a name service for CSNET which is independent
-                from the DNS and DNS use in the CSNET.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Mockapetris                                                    [Page 53]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-Index
-
-          *   13
-
-          ;   33, 35
-
-          <character-string>   35
-          <domain-name>   34
-
-          @   35
-
-          \   35
-
-          A   12
-
-          Byte order   8
-
-          CH   13
-          Character case   9
-          CLASS   11
-          CNAME   12
-          Completion   42
-          CS   13
-
-          Hesiod   13
-          HINFO   12
-          HS   13
-
-          IN   13
-          IN-ADDR.ARPA domain   22
-          Inverse queries   40
-
-          Mailbox names   47
-          MB   12
-          MD   12
-          MF   12
-          MG   12
-          MINFO   12
-          MINIMUM   20
-          MR   12
-          MX   12
-
-          NS   12
-          NULL   12
-
-          Port numbers   32
-          Primary server   5
-          PTR   12, 18
-
-
-
-Mockapetris                                                    [Page 54]
-\f
-RFC 1035        Domain Implementation and Specification    November 1987
-
-
-          QCLASS   13
-          QTYPE   12
-
-          RDATA   12
-          RDLENGTH  11
-
-          Secondary server   5
-          SOA   12
-          Stub resolvers   7
-
-          TCP   32
-          TXT   12
-          TYPE   11
-
-          UDP   32
-
-          WKS   12
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Mockapetris                                                    [Page 55]
-\f
diff --git a/docs/rfc/rfc1413.txt b/docs/rfc/rfc1413.txt
deleted file mode 100644 (file)
index 17ede58..0000000
+++ /dev/null
@@ -1,451 +0,0 @@
-
-
-
-
-
-
-Network Working Group                                       M. St. Johns
-Request for Comments: 1413                      US Department of Defense
-Obsoletes: 931                                             February 1993
-
-
-                        Identification Protocol
-
-Status of this Memo
-
-   This RFC specifies an IAB standards track protocol for the Internet
-   community, and requests discussion and suggestions for improvements.
-   Please refer to the current edition of the "IAB Official Protocol
-   Standards" for the standardization state and status of this protocol.
-   Distribution of this memo is unlimited.
-
-1.  INTRODUCTION
-
-   The Identification Protocol (a.k.a., "ident", a.k.a., "the Ident
-   Protocol") provides a means to determine the identity of a user of a
-   particular TCP connection.  Given a TCP port number pair, it returns
-   a character string which identifies the owner of that connection on
-   the server's system.
-
-   The Identification Protocol was formerly called the Authentication
-   Server Protocol.  It has been renamed to better reflect its function.
-   This document is a product of the TCP Client Identity Protocol
-   Working Group of the Internet Engineering Task Force (IETF).
-
-2.  OVERVIEW
-
-   This is a connection based application on TCP.  A server listens for
-   TCP connections on TCP port 113 (decimal).  Once a connection is
-   established, the server reads a line of data which specifies the
-   connection of interest.  If it exists, the system dependent user
-   identifier of the connection of interest is sent as the reply.  The
-   server may then either shut the connection down or it may continue to
-   read/respond to multiple queries.
-
-   The server should close the connection down after a configurable
-   amount of time with no queries - a 60-180 second idle timeout is
-   recommended.  The client may close the connection down at any time;
-   however to allow for network delays the client should wait at least
-   30 seconds (or longer) after a query before abandoning the query and
-   closing the connection.
-
-
-
-
-
-
-
-St. Johns                                                       [Page 1]
-\f
-RFC 1413                Identification Protocol            February 1993
-
-
-3.  RESTRICTIONS
-
-   Queries are permitted only for fully specified connections.  The
-   query contains the local/foreign port pair -- the local/foreign
-   address pair used to fully specify the connection is taken from the
-   local and foreign address of query connection.  This means a user on
-   address A may only query the server on address B about connections
-   between A and B.
-
-4.  QUERY/RESPONSE FORMAT
-
-   The server accepts simple text query requests of the form:
-
-            <port-on-server> , <port-on-client>
-
-   where <port-on-server> is the TCP port (decimal) on the target (where
-   the "ident" server is running) system, and <port-on-client> is the
-   TCP port (decimal) on the source (client) system.
-
-   N.B - If a client on host A wants to ask a server on host B about a
-   connection specified locally (on the client's machine) as 23, 6191
-   (an inbound TELNET connection), the client must actually ask about
-   6191, 23 - which is how the connection would be specified on host B.
-
-      For example:
-
-                 6191, 23
-
-   The response is of the form
-
-   <port-on-server> , <port-on-client> : <resp-type> : <add-info>
-
-   where <port-on-server>,<port-on-client> are the same pair as the
-   query, <resp-type> is a keyword identifying the type of response, and
-   <add-info> is context dependent.
-
-   The information returned is that associated with the fully specified
-   TCP connection identified by <server-address>, <client-address>,
-   <port-on-server>, <port-on-client>, where <server-address> and
-   <client-address> are the local and foreign IP addresses of the
-   querying connection -- i.e., the TCP connection to the Identification
-   Protocol Server.  (<port-on-server> and <port-on-client> are taken
-   from the query.)
-
-      For example:
-
-         6193, 23 : USERID : UNIX : stjohns
-         6195, 23 : ERROR : NO-USER
-
-
-
-St. Johns                                                       [Page 2]
-\f
-RFC 1413                Identification Protocol            February 1993
-
-
-5.  RESPONSE TYPES
-
-A response can be one of two types:
-
-USERID
-
-     In this case, <add-info> is a string consisting of an
-     operating system name (with an optional character set
-     identifier), followed by ":", followed by an
-     identification string.
-
-     The character set (if present) is separated from the
-     operating system name by ",".  The character set
-     identifier is used to indicate the character set of the
-     identification string.  The character set identifier,
-     if omitted, defaults to "US-ASCII" (see below).
-
-     Permitted operating system names and character set
-     names are specified in RFC 1340, "Assigned Numbers" or
-     its successors.
-
-     In addition to those operating system and character set
-     names specified in "Assigned Numbers" there is one
-     special case operating system identifier - "OTHER".
-
-     Unless "OTHER" is specified as the operating system
-     type, the server is expected to return the "normal"
-     user identification of the owner of this connection.
-     "Normal" in this context may be taken to mean a string
-     of characters which uniquely identifies the connection
-     owner such as a user identifier assigned by the system
-     administrator and used by such user as a mail
-     identifier, or as the "user" part of a user/password
-     pair used to gain access to system resources.  When an
-     operating system is specified (e.g., anything but
-     "OTHER"), the user identifier is expected to be in a
-     more or less immediately useful form - e.g., something
-     that could be used as an argument to "finger" or as a
-     mail address.
-
-     "OTHER" indicates the identifier is an unformatted
-     character string consisting of printable characters in
-     the specified character set.  "OTHER" should be
-     specified if the user identifier does not meet the
-     constraints of the previous paragraph.  Sending an
-     encrypted audit token, or returning other non-userid
-     information about a user (such as the real name and
-     phone number of a user from a UNIX passwd file) are
-
-
-
-St. Johns                                                       [Page 3]
-\f
-RFC 1413                Identification Protocol            February 1993
-
-
-     both examples of when "OTHER" should be used.
-
-     Returned user identifiers are expected to be printable
-     in the character set indicated.
-
-     The identifier is an unformatted octet string - - all
-     octets are permissible EXCEPT octal 000 (NUL), 012 (LF)
-     and 015 (CR).  N.B. - space characters (040) following the
-     colon separator ARE part of the identifier string and
-     may not be ignored. A response string is still
-     terminated normally by a CR/LF.  N.B. A string may be
-     printable, but is not *necessarily* printable.
-
-ERROR
-
-   For some reason the port owner could not be determined, <add-info>
-   tells why.  The following are the permitted values of <add-info> and
-   their meanings:
-
-          INVALID-PORT
-
-          Either the local or foreign port was improperly
-          specified.  This should be returned if either or
-          both of the port ids were out of range (TCP port
-          numbers are from 1-65535), negative integers, reals or
-          in any fashion not recognized as a non-negative
-          integer.
-
-          NO-USER
-
-          The connection specified by the port pair is not
-          currently in use or currently not owned by an
-          identifiable entity.
-
-          HIDDEN-USER
-
-          The server was able to identify the user of this
-          port, but the information was not returned at the
-          request of the user.
-
-          UNKNOWN-ERROR
-
-          Can't determine connection owner; reason unknown.
-          Any error not covered above should return this
-          error code value.  Optionally, this code MAY be
-          returned in lieu of any other specific error code
-          if, for example, the server desires to hide
-          information implied by the return of that error
-
-
-
-St. Johns                                                       [Page 4]
-\f
-RFC 1413                Identification Protocol            February 1993
-
-
-          code, or for any other reason.  If a server
-          implements such a feature, it MUST be configurable
-          and it MUST default to returning the proper error
-          message.
-
-   Other values may eventually be specified and defined in future
-   revisions to this document.  If an implementer has a need to specify
-   a non-standard error code, that code must begin with "X".
-
-   In addition, the server is allowed to drop the query connection
-   without responding.  Any premature close (i.e., one where the client
-   does not receive the EOL, whether graceful or an abort should be
-   considered to have the same meaning as "ERROR : UNKNOWN-ERROR".
-
-FORMAL SYNTAX
-
-   <request> ::= <port-pair> <EOL>
-
-   <port-pair> ::= <integer> "," <integer>
-
-   <reply> ::= <reply-text> <EOL>
-
-   <EOL> ::= "015 012"  ; CR-LF End of Line Indicator
-
-   <reply-text> ::= <error-reply> | <ident-reply>
-
-   <error-reply> ::= <port-pair> ":" "ERROR" ":" <error-type>
-
-   <ident-reply> ::= <port-pair> ":" "USERID" ":" <opsys-field>
-                     ":" <user-id>
-
-   <error-type> ::= "INVALID-PORT" | "NO-USER" | "UNKNOWN-ERROR"
-                    | "HIDDEN-USER" |  <error-token>
-
-   <opsys-field> ::= <opsys> [ "," <charset>]
-
-   <opsys> ::= "OTHER" | "UNIX" | <token> ...etc.
-               ;  (See "Assigned Numbers")
-
-   <charset> ::= "US-ASCII" | ...etc.
-                 ;  (See "Assigned Numbers")
-
-   <user-id> ::= <octet-string>
-
-   <token> ::= 1*64<token-characters> ; 1-64 characters
-
-   <error-token> ::= "X"1*63<token-characters>
-                     ; 2-64 chars beginning w/X
-
-
-
-St. Johns                                                       [Page 5]
-\f
-RFC 1413                Identification Protocol            February 1993
-
-
-   <integer> ::= 1*5<digit> ; 1-5 digits.
-
-   <digit> ::= "0" | "1" ... "8" | "9" ; 0-9
-
-   <token-characters> ::=
-                  <Any of these ASCII characters: a-z, A-Z,
-                   - (dash), .!@#$%^&*()_=+.,<>/?"'~`{}[]; >
-                               ; upper and lowercase a-z plus
-                               ; printables minus the colon ":"
-                               ; character.
-
-   <octet-string> ::= 1*512<octet-characters>
-
-   <octet-characters> ::=
-                  <any octet from  00 to 377 (octal) except for
-                   ASCII NUL (000), CR (015) and LF (012)>
-
-Notes on Syntax:
-
-   1)   To promote interoperability among variant
-        implementations, with respect to white space the above
-        syntax is understood to embody the "be conservative in
-        what you send and be liberal in what you accept"
-        philosophy.  Clients and servers should not generate
-        unnecessary white space (space and tab characters) but
-        should accept white space anywhere except within a
-        token.  In parsing responses, white space may occur
-        anywhere, except within a token.  Specifically, any
-        amount of white space is permitted at the beginning or
-        end of a line both for queries and responses.  This
-        does not apply for responses that contain a user ID
-        because everything after the colon after the operating
-        system type until the terminating CR/LF is taken as
-        part of the user ID.  The terminating CR/LF is NOT
-        considered part of the user ID.
-
-   2)   The above notwithstanding, servers should restrict the
-        amount of inter-token white space they send to the
-        smallest amount reasonable or useful.  Clients should
-        feel free to abort a connection if they receive 1000
-        characters without receiving an <EOL>.
-
-   3)   The 512 character limit on user IDs and the 64
-        character limit on tokens should be understood to mean
-        as follows: a) No new token (i.e., OPSYS or ERROR-TYPE)
-        token will be defined that has a length greater than 64
-        and b) a server SHOULD NOT send more than 512 octets of
-        user ID and a client MUST accept at least 512 octets of
-
-
-
-St. Johns                                                       [Page 6]
-\f
-RFC 1413                Identification Protocol            February 1993
-
-
-        user ID.  Because of this limitation, a server MUST
-        return the most significant portion of the user ID in
-        the first 512 octets.
-
-   4)   The character sets and character set identifiers should
-        map directly to those defined in or referenced by RFC 1340,
-        "Assigned Numbers" or its successors.  Character set
-        identifiers only apply to the user identification field
-        - all other fields will be defined in and must be sent
-        as US-ASCII.
-
-   5)   Although <user-id> is defined as an <octet-string>
-        above, it must follow the format and character set
-        constraints implied by the <opsys-field>; see the
-        discussion above.
-
-   6)   The character set provides context for the client to
-        print or store the returned user identification string.
-        If the client does not recognize or implement the
-        returned character set, it should handle the returned
-        identification string as OCTET, but should in addition
-        store or report the character set.  An OCTET string
-        should be printed, stored or handled in hex notation
-        (0-9a-f) in addition to any other representation the
-        client implements - this provides a standard
-        representation among differing implementations.
-
-6.  Security Considerations
-
-   The information returned by this protocol is at most as trustworthy
-   as the host providing it OR the organization operating the host.  For
-   example, a PC in an open lab has few if any controls on it to prevent
-   a user from having this protocol return any identifier the user
-   wants.  Likewise, if the host has been compromised the information
-   returned may be completely erroneous and misleading.
-
-   The Identification Protocol is not intended as an authorization or
-   access control protocol.  At best, it provides some additional
-   auditing information with respect to TCP connections.  At worst, it
-   can provide misleading, incorrect, or maliciously incorrect
-   information.
-
-   The use of the information returned by this protocol for other than
-   auditing is strongly discouraged.  Specifically, using Identification
-   Protocol information to make access control decisions - either as the
-   primary method (i.e., no other checks) or as an adjunct to other
-   methods may result in a weakening of normal host security.
-
-
-
-
-St. Johns                                                       [Page 7]
-\f
-RFC 1413                Identification Protocol            February 1993
-
-
-   An Identification server may reveal information about users,
-   entities, objects or processes which might normally be considered
-   private.  An Identification server provides service which is a rough
-   analog of the CallerID services provided by some phone companies and
-   many of the same privacy considerations and arguments that apply to
-   the CallerID service apply to Identification.  If you wouldn't run a
-   "finger" server due to privacy considerations you may not want to run
-   this protocol.
-
-7.  ACKNOWLEDGEMENTS
-
-   Acknowledgement is given to Dan Bernstein who is primarily
-   responsible for renewing interest in this protocol and for pointing
-   out some annoying errors in RFC 931.
-
-References
-
-   [1] St. Johns, M., "Authentication Server", RFC 931, TPSC, January
-       1985.
-
-   [2] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC 1340,
-       USC/Information Sciences Institute, July 1992.
-
-Author's Address
-
-       Michael C. St. Johns
-       DARPA/CSTO
-       3701 N. Fairfax Dr
-       Arlington, VA 22203
-
-       Phone: (703) 696-2271
-       EMail: stjohns@DARPA.MIL
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-St. Johns                                                       [Page 8]
-\f
\ No newline at end of file
diff --git a/docs/rfc/rfc1459.txt b/docs/rfc/rfc1459.txt
deleted file mode 100644 (file)
index 09fbf34..0000000
+++ /dev/null
@@ -1,3643 +0,0 @@
-
-
-
-
-
-
-Network Working Group                                      J. Oikarinen
-Request for Comments: 1459                                      D. Reed
-                                                               May 1993
-
-
-                      Internet Relay Chat Protocol
-
-Status of This Memo
-
-   This memo defines an Experimental Protocol for the Internet
-   community.  Discussion and suggestions for improvement are requested.
-   Please refer to the current edition of the "IAB Official Protocol
-   Standards" for the standardization state and status of this protocol.
-   Distribution of this memo is unlimited.
-
-Abstract
-
-   The IRC protocol was developed over the last 4 years since it was
-   first implemented as a means for users on a BBS to chat amongst
-   themselves.  Now it supports a world-wide network of servers and
-   clients, and is stringing to cope with growth. Over the past 2 years,
-   the average number of users connected to the main IRC network has
-   grown by a factor of 10.
-
-   The IRC protocol is a text-based protocol, with the simplest client
-   being any socket program capable of connecting to the server.
-
-Table of Contents
-
-   1.  INTRODUCTION ...............................................    4
-      1.1  Servers ................................................    4
-      1.2  Clients ................................................    5
-         1.2.1 Operators ..........................................    5
-      1.3 Channels ................................................    5
-      1.3.1  Channel Operators ....................................    6
-   2. THE IRC SPECIFICATION .......................................    7
-      2.1 Overview ................................................    7
-      2.2 Character codes .........................................    7
-      2.3 Messages ................................................    7
-         2.3.1  Message format in 'pseudo' BNF ....................    8
-      2.4 Numeric replies .........................................   10
-   3. IRC Concepts ................................................   10
-      3.1 One-to-one communication ................................   10
-      3.2 One-to-many .............................................   11
-         3.2.1 To a list ..........................................   11
-         3.2.2 To a group (channel) ...............................   11
-         3.2.3 To a host/server mask ..............................   12
-      3.3 One to all ..............................................   12
-
-
-
-Oikarinen & Reed                                                [Page 1]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-         3.3.1 Client to Client ...................................   12
-         3.3.2 Clients to Server ..................................   12
-         3.3.3 Server to Server ...................................   12
-   4. MESSAGE DETAILS .............................................   13
-      4.1 Connection Registration .................................   13
-         4.1.1 Password message ...................................   14
-         4.1.2 Nickname message ...................................   14
-         4.1.3 User message .......................................   15
-         4.1.4 Server message .....................................   16
-         4.1.5 Operator message ...................................   17
-         4.1.6 Quit message .......................................   17
-         4.1.7 Server Quit message ................................   18
-      4.2 Channel operations ......................................   19
-         4.2.1 Join message .......................................   19
-         4.2.2 Part message .......................................   20
-         4.2.3 Mode message .......................................   21
-            4.2.3.1 Channel modes .................................   21
-            4.2.3.2 User modes ....................................   22
-         4.2.4 Topic message ......................................   23
-         4.2.5 Names message ......................................   24
-         4.2.6 List message .......................................   24
-         4.2.7 Invite message .....................................   25
-         4.2.8 Kick message .......................................   25
-      4.3 Server queries and commands .............................   26
-         4.3.1 Version message ....................................   26
-         4.3.2 Stats message ......................................   27
-         4.3.3 Links message ......................................   28
-         4.3.4 Time message .......................................   29
-         4.3.5 Connect message ....................................   29
-         4.3.6 Trace message ......................................   30
-         4.3.7 Admin message ......................................   31
-         4.3.8 Info message .......................................   31
-      4.4 Sending messages ........................................   32
-         4.4.1 Private messages ...................................   32
-         4.4.2 Notice messages ....................................   33
-      4.5 User-based queries ......................................   33
-         4.5.1 Who query ..........................................   33
-         4.5.2 Whois query ........................................   34
-         4.5.3 Whowas message .....................................   35
-      4.6 Miscellaneous messages ..................................   35
-         4.6.1 Kill message .......................................   36
-         4.6.2 Ping message .......................................   37
-         4.6.3 Pong message .......................................   37
-         4.6.4 Error message ......................................   38
-   5. OPTIONAL MESSAGES ...........................................   38
-      5.1 Away message ............................................   38
-      5.2 Rehash command ..........................................   39
-      5.3 Restart command .........................................   39
-
-
-
-Oikarinen & Reed                                                [Page 2]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-      5.4 Summon message ..........................................   40
-      5.5 Users message ...........................................   40
-      5.6 Operwall command ........................................   41
-      5.7 Userhost message ........................................   42
-      5.8 Ison message ............................................   42
-   6. REPLIES .....................................................   43
-      6.1 Error Replies ...........................................   43
-      6.2 Command responses .......................................   48
-      6.3 Reserved numerics .......................................   56
-   7. Client and server authentication ............................   56
-   8. Current Implementations Details .............................   56
-      8.1 Network protocol: TCP ...................................   57
-         8.1.1 Support of Unix sockets ............................   57
-      8.2 Command Parsing .........................................   57
-      8.3 Message delivery ........................................   57
-      8.4 Connection 'Liveness' ...................................   58
-      8.5 Establishing a server-client connection .................   58
-      8.6 Establishing a server-server connection .................   58
-         8.6.1 State information exchange when connecting .........   59
-      8.7 Terminating server-client connections ...................   59
-      8.8 Terminating server-server connections ...................   59
-      8.9 Tracking nickname changes ...............................   60
-      8.10 Flood control of clients ...............................   60
-      8.11 Non-blocking lookups ...................................   61
-         8.11.1 Hostname (DNS) lookups ............................   61
-         8.11.2 Username (Ident) lookups ..........................   61
-      8.12 Configuration file .....................................   61
-         8.12.1 Allowing clients to connect .......................   62
-         8.12.2 Operators .........................................   62
-         8.12.3 Allowing servers to connect .......................   62
-         8.12.4 Administrivia .....................................   63
-      8.13 Channel membership .....................................   63
-   9. Current problems ............................................   63
-      9.1 Scalability .............................................   63
-      9.2 Labels ..................................................   63
-         9.2.1 Nicknames ..........................................   63
-         9.2.2 Channels ...........................................   64
-         9.2.3 Servers ............................................   64
-      9.3 Algorithms ..............................................   64
-   10. Support and availability ...................................   64
-   11. Security Considerations ....................................   65
-   12. Authors' Addresses .........................................   65
-
-
-
-
-
-
-
-
-
-Oikarinen & Reed                                                [Page 3]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-1.  INTRODUCTION
-
-   The IRC (Internet Relay Chat) protocol has been designed over a
-   number of years for use with text based conferencing.  This document
-   describes the current IRC protocol.
-
-   The IRC protocol has been developed on systems using the TCP/IP
-   network protocol, although there is no requirement that this remain
-   the only sphere in which it operates.
-
-   IRC itself is a teleconferencing system, which (through the use of
-   the client-server model) is well-suited to running on many machines
-   in a distributed fashion.  A typical setup involves a single process
-   (the server) forming a central point for clients (or other servers)
-   to connect to, performing the required message delivery/multiplexing
-   and other functions.
-
-1.1 Servers
-
-   The server forms the backbone of IRC, providing a point to which
-   clients may connect to to talk to each other, and a point for other
-   servers to connect to, forming an IRC network.  The only network
-   configuration allowed for IRC servers is that of a spanning tree [see
-   Fig. 1] where each server acts as a central node for the rest of the
-   net it sees.
-
-
-                           [ Server 15 ]  [ Server 13 ] [ Server 14]
-                                 /                \         /
-                                /                  \       /
-        [ Server 11 ] ------ [ Server 1 ]       [ Server 12]
-                              /        \          /
-                             /          \        /
-                  [ Server 2 ]          [ Server 3 ]
-                    /       \                      \
-                   /         \                      \
-           [ Server 4 ]    [ Server 5 ]         [ Server 6 ]
-            /    |    \                           /
-           /     |     \                         /
-          /      |      \____                   /
-         /       |           \                 /
- [ Server 7 ] [ Server 8 ] [ Server 9 ]   [ Server 10 ]
-
-                                  :
-                               [ etc. ]
-                                  :
-
-                 [ Fig. 1. Format of IRC server network ]
-
-
-
-Oikarinen & Reed                                                [Page 4]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-1.2 Clients
-
-   A client is anything connecting to a server that is not another
-   server.  Each client is distinguished from other clients by a unique
-   nickname having a maximum length of nine (9) characters.  See the
-   protocol grammar rules for what may and may not be used in a
-   nickname.  In addition to the nickname, all servers must have the
-   following information about all clients: the real name of the host
-   that the client is running on, the username of the client on that
-   host, and the server to which the client is connected.
-
-1.2.1 Operators
-
-   To allow a reasonable amount of order to be kept within the IRC
-   network, a special class of clients (operators) is allowed to perform
-   general maintenance functions on the network.  Although the powers
-   granted to an operator can be considered as 'dangerous', they are
-   nonetheless required.  Operators should be able to perform basic
-   network tasks such as disconnecting and reconnecting servers as
-   needed to prevent long-term use of bad network routing.  In
-   recognition of this need, the protocol discussed herein provides for
-   operators only to be able to perform such functions.  See sections
-   4.1.7 (SQUIT) and 4.3.5 (CONNECT).
-
-   A more controversial power of operators is the ability  to  remove  a
-   user  from  the connected network by 'force', i.e. operators are able
-   to close the connection between any client and server.   The
-   justification for  this  is delicate since its abuse is both
-   destructive and annoying.  For further details on this type of
-   action, see section 4.6.1 (KILL).
-
-1.3 Channels
-
-   A channel is a named group of one or more clients which will all
-   receive messages addressed to that channel.  The channel is created
-   implicitly when the first client joins it, and the channel ceases to
-   exist when the last client leaves it.  While channel exists, any
-   client can reference the channel using the name of the channel.
-
-   Channels names are strings (beginning with a '&' or '#' character) of
-   length up to 200 characters.  Apart from the the requirement that the
-   first character being either '&' or '#'; the only restriction on a
-   channel name is that it may not contain any spaces (' '), a control G
-   (^G or ASCII 7), or a comma (',' which is used as a list item
-   separator by the protocol).
-
-   There are two types of channels allowed by this protocol.  One is a
-   distributed channel which is known to all the servers that are
-
-
-
-Oikarinen & Reed                                                [Page 5]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   connected to the network. These channels are marked by the first
-   character being a only clients on the server where it exists may join
-   it.  These are distinguished by a leading '&' character.  On top of
-   these two types, there are the various channel modes available to
-   alter the characteristics of individual channels.  See section 4.2.3
-   (MODE command) for more details on this.
-
-   To create a new channel or become part of an existing channel, a user
-   is required to JOIN the channel.  If the channel doesn't exist prior
-   to joining, the channel is created and the creating user becomes a
-   channel operator.  If the channel already exists, whether or not your
-   request to JOIN that channel is honoured depends on the current modes
-   of the channel. For example, if the channel is invite-only, (+i),
-   then you may only join if invited.  As part of the protocol, a user
-   may be a part of several channels at once, but a limit of ten (10)
-   channels is recommended as being ample for both experienced and
-   novice users.  See section 8.13 for more information on this.
-
-   If the IRC network becomes disjoint because of a split between two
-   servers, the channel on each side is only composed of those clients
-   which are connected to servers on the respective sides of the split,
-   possibly ceasing to exist on one side of the split.  When the split
-   is healed, the connecting servers announce to each other who they
-   think is in each channel and the mode of that channel.  If the
-   channel exists on both sides, the JOINs and MODEs are interpreted in
-   an inclusive manner so that both sides of the new connection will
-   agree about which clients are in the channel and what modes the
-   channel has.
-
-1.3.1 Channel Operators
-
-   The channel operator (also referred to as a "chop" or "chanop") on a
-   given channel is considered to 'own' that channel.  In recognition of
-   this status, channel operators are endowed with certain powers which
-   enable them to keep control and some sort of sanity in their channel.
-   As an owner of a channel, a channel operator is not required to have
-   reasons for their actions, although if their actions are generally
-   antisocial or otherwise abusive, it might be reasonable to ask an IRC
-   operator to intervene, or for the usersjust leave and go elsewhere
-   and form their own channel.
-
-   The commands which may only be used by channel operators are:
-
-        KICK    - Eject a client from the channel
-        MODE    - Change the channel's mode
-        INVITE  - Invite a client to an invite-only channel (mode +i)
-        TOPIC   - Change the channel topic in a mode +t channel
-
-
-
-
-Oikarinen & Reed                                                [Page 6]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   A channel operator is identified by the '@' symbol next to their
-   nickname whenever it is associated with a channel (ie replies to the
-   NAMES, WHO and WHOIS commands).
-
-2. The IRC Specification
-
-2.1 Overview
-
-   The protocol as described herein is for use both with server to
-   server and client to server connections.  There are, however, more
-   restrictions on client connections (which are considered to be
-   untrustworthy) than on server connections.
-
-2.2 Character codes
-
-   No specific character set is specified. The protocol is based on a a
-   set of codes which are composed of eight (8) bits, making up an
-   octet.  Each message may be composed of any number of these octets;
-   however, some octet values are used for control codes which act as
-   message delimiters.
-
-   Regardless of being an 8-bit protocol, the delimiters and keywords
-   are such that protocol is mostly usable from USASCII terminal and a
-   telnet connection.
-
-   Because of IRC's scandanavian origin, the characters {}| are
-   considered to be the lower case equivalents of the characters []\,
-   respectively. This is a critical issue when determining the
-   equivalence of two nicknames.
-
-2.3 Messages
-
-   Servers and clients send eachother messages which may or may not
-   generate a reply.  If the message contains a valid command, as
-   described in later sections, the client should expect a reply as
-   specified but it is not advised to wait forever for the reply; client
-   to server and server to server communication is essentially
-   asynchronous in nature.
-
-   Each IRC message may consist of up to three main parts: the prefix
-   (optional), the command, and the command parameters (of which there
-   may be up to 15).  The prefix, command, and all parameters are
-   separated by one (or more) ASCII space character(s) (0x20).
-
-   The presence of a prefix is indicated with a single leading ASCII
-   colon character (':', 0x3b), which must be the first character of the
-   message itself.  There must be no gap (whitespace) between the colon
-   and the prefix.  The prefix is used by servers to indicate the true
-
-
-
-Oikarinen & Reed                                                [Page 7]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   origin of the message.  If the prefix is missing from the message, it
-   is assumed to have originated from the connection from which it was
-   received.  Clients should not use prefix when sending a message from
-   themselves; if they use a prefix, the only valid prefix is the
-   registered nickname associated with the client.  If the source
-   identified by the prefix cannot be found from the server's internal
-   database, or if the source is registered from a different link than
-   from which the message arrived, the server must ignore the message
-   silently.
-
-   The command must either be a valid IRC command or a three (3) digit
-   number represented in ASCII text.
-
-   IRC messages are always lines of characters terminated with a CR-LF
-   (Carriage Return - Line Feed) pair, and these messages shall not
-   exceed 512 characters in length, counting all characters including
-   the trailing CR-LF. Thus, there are 510 characters maximum allowed
-   for the command and its parameters.  There is no provision for
-   continuation message lines.  See section 7 for more details about
-   current implementations.
-
-2.3.1 Message format in 'pseudo' BNF
-
-   The protocol messages must be extracted from the contiguous stream of
-   octets.  The current solution is to designate two characters, CR and
-   LF, as message separators.   Empty  messages  are  silently  ignored,
-   which permits  use  of  the  sequence  CR-LF  between  messages
-   without extra problems.
-
-   The extracted message is parsed into the components <prefix>,
-   <command> and list of parameters matched either by <middle> or
-   <trailing> components.
-
-   The BNF representation for this is:
-
-
-<message>  ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
-<prefix>   ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
-<command>  ::= <letter> { <letter> } | <number> <number> <number>
-<SPACE>    ::= ' ' { ' ' }
-<params>   ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
-
-<middle>   ::= <Any *non-empty* sequence of octets not including SPACE
-               or NUL or CR or LF, the first of which may not be ':'>
-<trailing> ::= <Any, possibly *empty*, sequence of octets not including
-                 NUL or CR or LF>
-
-<crlf>     ::= CR LF
-
-
-
-Oikarinen & Reed                                                [Page 8]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-NOTES:
-
-  1)    <SPACE> is consists only of SPACE character(s) (0x20).
-        Specially notice that TABULATION, and all other control
-        characters are considered NON-WHITE-SPACE.
-
-  2)    After extracting the parameter list, all parameters are equal,
-        whether matched by <middle> or <trailing>. <Trailing> is just
-        a syntactic trick to allow SPACE within parameter.
-
-  3)    The fact that CR and LF cannot appear in parameter strings is
-        just artifact of the message framing. This might change later.
-
-  4)    The NUL character is not special in message framing, and
-        basically could end up inside a parameter, but as it would
-        cause extra complexities in normal C string handling. Therefore
-        NUL is not allowed within messages.
-
-  5)    The last parameter may be an empty string.
-
-  6)    Use of the extended prefix (['!' <user> ] ['@' <host> ]) must
-        not be used in server to server communications and is only
-        intended for server to client messages in order to provide
-        clients with more useful information about who a message is
-        from without the need for additional queries.
-
-   Most protocol messages specify additional semantics and syntax for
-   the extracted parameter strings dictated by their position in the
-   list.  For example, many server commands will assume that the first
-   parameter after the command is the list of targets, which can be
-   described with:
-
-   <target>     ::= <to> [ "," <target> ]
-   <to>         ::= <channel> | <user> '@' <servername> | <nick> | <mask>
-   <channel>    ::= ('#' | '&') <chstring>
-   <servername> ::= <host>
-   <host>       ::= see RFC 952 [DNS:4] for details on allowed hostnames
-   <nick>       ::= <letter> { <letter> | <number> | <special> }
-   <mask>       ::= ('#' | '$') <chstring>
-   <chstring>   ::= <any 8bit code except SPACE, BELL, NUL, CR, LF and
-                     comma (',')>
-
-   Other parameter syntaxes are:
-
-   <user>       ::= <nonwhite> { <nonwhite> }
-   <letter>     ::= 'a' ... 'z' | 'A' ... 'Z'
-   <number>     ::= '0' ... '9'
-   <special>    ::= '-' | '[' | ']' | '\' | '`' | '^' | '{' | '}'
-
-
-
-Oikarinen & Reed                                                [Page 9]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   <nonwhite>   ::= <any 8bit code except SPACE (0x20), NUL (0x0), CR
-                     (0xd), and LF (0xa)>
-
-2.4 Numeric replies
-
-   Most of the messages sent to the server generate a reply of some
-   sort.  The most common reply is the numeric reply, used for both
-   errors and normal replies.  The numeric reply must be sent as one
-   message consisting of the sender prefix, the three digit numeric, and
-   the target of the reply.  A numeric reply is not allowed to originate
-   from a client; any such messages received by a server are silently
-   dropped. In all other respects, a numeric reply is just like a normal
-   message, except that the keyword is made up of 3 numeric digits
-   rather than a string of letters.  A list of different replies is
-   supplied in section 6.
-
-3. IRC Concepts.
-
-   This section is devoted to describing the actual concepts behind  the
-   organization  of  the  IRC  protocol and how the current
-   implementations deliver different classes of messages.
-
-
-
-                          1--\
-                              A        D---4
-                          2--/ \      /
-                                B----C
-                               /      \
-                              3        E
-
-   Servers: A, B, C, D, E         Clients: 1, 2, 3, 4
-
-                    [ Fig. 2. Sample small IRC network ]
-
-3.1 One-to-one communication
-
-   Communication on a one-to-one basis is usually only performed by
-   clients, since most server-server traffic is not a result of servers
-   talking only to each other.  To provide a secure means for clients to
-   talk to each other, it is required that all servers be able to send a
-   message in exactly one direction along the spanning tree in order to
-   reach any client.  The path of a message being delivered is the
-   shortest path between any two points on the spanning tree.
-
-   The following examples all refer to Figure 2 above.
-
-
-
-
-
-Oikarinen & Reed                                               [Page 10]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-Example 1:
-     A message between clients 1 and 2 is only seen by server A, which
-     sends it straight to client 2.
-
-Example 2:
-     A message between clients 1 and 3 is seen by servers A & B, and
-     client 3.  No other clients or servers are allowed see the message.
-
-Example 3:
-     A message between clients 2 and 4 is seen by servers A, B, C & D
-     and client 4 only.
-
-3.2 One-to-many
-
-   The main goal of IRC is to provide a  forum  which  allows  easy  and
-   efficient  conferencing (one to many conversations).  IRC offers
-   several means to achieve this, each serving its own purpose.
-
-3.2.1 To a list
-
-   The least efficient style of one-to-many conversation is through
-   clients talking to a 'list' of users.  How this is done is almost
-   self explanatory: the client gives a list of destinations to which
-   the message is to be delivered and the server breaks it up and
-   dispatches a separate copy of the message to each given destination.
-   This isn't as efficient as using a group since the destination list
-   is broken up and the dispatch sent without checking to make sure
-   duplicates aren't sent down each path.
-
-3.2.2 To a group (channel)
-
-   In IRC the channel has a role equivalent to that of the multicast
-   group; their existence is dynamic (coming and going as people join
-   and leave channels) and the actual conversation carried out on a
-   channel is only sent to servers which are supporting users on a given
-   channel.  If there are multiple users on a server in the same
-   channel, the message text is sent only once to that server and then
-   sent to each client on the channel.  This action is then repeated for
-   each client-server combination until the original message has fanned
-   out and reached each member of the channel.
-
-   The following examples all refer to Figure 2.
-
-Example 4:
-     Any channel with 1 client in it. Messages to the channel go to the
-     server and then nowhere else.
-
-
-
-
-
-Oikarinen & Reed                                               [Page 11]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-Example 5:
-     2 clients in a channel. All messages traverse a path as if they
-     were private messages between the two clients outside a channel.
-
-Example 6:
-     Clients 1, 2 and 3 in a channel.  All messages to the channel are
-     sent to all clients and only those servers which must be traversed
-     by the message if it were a private message to a single client.  If
-     client 1 sends a message, it goes back to client 2 and then via
-     server B to client 3.
-
-3.2.3 To a host/server mask
-
-   To provide IRC operators with some mechanism to send  messages  to  a
-   large body of related users, host and server mask messages are
-   provided.  These messages are sent to users whose host or server
-   information  match that  of  the mask.  The messages are only sent to
-   locations where users are, in a fashion similar to that of channels.
-
-3.3 One-to-all
-
-   The one-to-all type of message is better described as a broadcast
-   message, sent to all clients or servers or both.  On a large network
-   of users and servers, a single message can result in a lot of traffic
-   being sent over the network in an effort to reach all of the desired
-   destinations.
-
-   For some messages, there is no option but to broadcast it to all
-   servers so that the state information held by each server is
-   reasonably consistent between servers.
-
-3.3.1 Client-to-Client
-
-   There is no class of message which, from a single message, results in
-   a message being sent to every other client.
-
-3.3.2 Client-to-Server
-
-   Most of the commands which result in a change of state information
-   (such as channel membership, channel mode, user status, etc) must be
-   sent to all servers by default, and this distribution may not be
-   changed by the client.
-
-3.3.3 Server-to-Server.
-
-   While most messages between servers are distributed to all 'other'
-   servers, this is only required for any message that affects either a
-   user, channel or server.  Since these are the basic items found in
-
-
-
-Oikarinen & Reed                                               [Page 12]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   IRC, nearly all messages originating from a server are broadcast to
-   all other connected servers.
-
-4. Message details
-
-   On the following pages are descriptions of each message recognized by
-   the IRC server and client.  All commands described in this section
-   must be implemented by any server for this protocol.
-
-   Where the reply ERR_NOSUCHSERVER is listed, it means that the
-   <server> parameter could not be found.  The server must not send any
-   other replies after this for that command.
-
-   The server to which a client is connected is required to parse the
-   complete message, returning any appropriate errors.  If the server
-   encounters a fatal error while parsing a message, an error must be
-   sent back to the client and the parsing terminated.  A fatal error
-   may be considered to be incorrect command, a destination which is
-   otherwise unknown to the server (server, nick or channel names fit
-   this category), not enough parameters or incorrect privileges.
-
-   If a full set of parameters is presented, then each must be checked
-   for validity and appropriate responses sent back to the client.  In
-   the case of messages which use parameter lists using the comma as an
-   item separator, a reply must be sent for each item.
-
-   In the examples below, some messages appear using the full format:
-
-   :Name COMMAND parameter list
-
-   Such examples represent a message from "Name" in transit between
-   servers, where it is essential to include the name of the original
-   sender of the message so remote servers may send back a reply along
-   the correct path.
-
-4.1 Connection Registration
-
-   The commands described here are used to register a connection with an
-   IRC server as either a user or a server as well as correctly
-   disconnect.
-
-   A "PASS" command is not required for either client or server
-   connection to be registered, but it must precede the server message
-   or the latter of the NICK/USER combination.  It is strongly
-   recommended that all server connections have a password in order to
-   give some level of security to the actual connections.  The
-   recommended order for a client to register is as follows:
-
-
-
-
-Oikarinen & Reed                                               [Page 13]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-           1. Pass message
-           2. Nick message
-           3. User message
-
-4.1.1 Password message
-
-
-      Command: PASS
-   Parameters: <password>
-
-   The PASS command is used to set a 'connection password'.  The
-   password can and must be set before any attempt to register the
-   connection is made.  Currently this requires that clients send a PASS
-   command before sending the NICK/USER combination and servers *must*
-   send a PASS command before any SERVER command.  The password supplied
-   must match the one contained in the C/N lines (for servers) or I
-   lines (for clients).  It is possible to send multiple PASS commands
-   before registering but only the last one sent is used for
-   verification and it may not be changed once registered.  Numeric
-   Replies:
-
-           ERR_NEEDMOREPARAMS              ERR_ALREADYREGISTRED
-
-   Example:
-
-           PASS secretpasswordhere
-
-4.1.2 Nick message
-
-      Command: NICK
-   Parameters: <nickname> [ <hopcount> ]
-
-   NICK message is used to give user a nickname or change the previous
-   one.  The <hopcount> parameter is only used by servers to indicate
-   how far away a nick is from its home server.  A local connection has
-   a hopcount of 0.  If supplied by a client, it must be ignored.
-
-   If a NICK message arrives at a server which already knows about an
-   identical nickname for another client, a nickname collision occurs.
-   As a result of a nickname collision, all instances of the nickname
-   are removed from the server's database, and a KILL command is issued
-   to remove the nickname from all other server's database. If the NICK
-   message causing the collision was a nickname change, then the
-   original (old) nick must be removed as well.
-
-   If the server recieves an identical NICK from a client which is
-   directly connected, it may issue an ERR_NICKCOLLISION to the local
-   client, drop the NICK command, and not generate any kills.
-
-
-
-Oikarinen & Reed                                               [Page 14]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   Numeric Replies:
-
-           ERR_NONICKNAMEGIVEN             ERR_ERRONEUSNICKNAME
-           ERR_NICKNAMEINUSE               ERR_NICKCOLLISION
-
-   Example:
-
-   NICK Wiz                        ; Introducing new nick "Wiz".
-
-   :WiZ NICK Kilroy                ; WiZ changed his nickname to Kilroy.
-
-4.1.3 User message
-
-      Command: USER
-   Parameters: <username> <hostname> <servername> <realname>
-
-   The USER message is used at the beginning of connection to specify
-   the username, hostname, servername and realname of s new user.  It is
-   also used in communication between servers to indicate new user
-   arriving on IRC, since only after both USER and NICK have been
-   received from a client does a user become registered.
-
-   Between servers USER must to be prefixed with client's NICKname.
-   Note that hostname and servername are normally ignored by the IRC
-   server when the USER command comes from a directly connected client
-   (for security reasons), but they are used in server to server
-   communication.  This means that a NICK must always be sent to a
-   remote server when a new user is being introduced to the rest of the
-   network before the accompanying USER is sent.
-
-   It must be noted that realname parameter must be the last parameter,
-   because it may contain space characters and must be prefixed with a
-   colon (':') to make sure this is recognised as such.
-
-   Since it is easy for a client to lie about its username by relying
-   solely on the USER message, the use of an "Identity Server" is
-   recommended.  If the host which a user connects from has such a
-   server enabled the username is set to that as in the reply from the
-   "Identity Server".
-
-   Numeric Replies:
-
-           ERR_NEEDMOREPARAMS              ERR_ALREADYREGISTRED
-
-   Examples:
-
-
-   USER guest tolmoon tolsun :Ronnie Reagan
-
-
-
-Oikarinen & Reed                                               [Page 15]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                                   ; User registering themselves with a
-                                   username of "guest" and real name
-                                   "Ronnie Reagan".
-
-
-   :testnick USER guest tolmoon tolsun :Ronnie Reagan
-                                   ; message between servers with the
-                                   nickname for which the USER command
-                                   belongs to
-
-4.1.4 Server message
-
-      Command: SERVER
-   Parameters: <servername> <hopcount> <info>
-
-   The server message is used to tell a server that the other end of a
-   new connection is a server. This message is also used to pass server
-   data over whole net.  When a new server is connected to net,
-   information about it be broadcast to the whole network.  <hopcount>
-   is used to give all servers some internal information on how far away
-   all servers are.  With a full server list, it would be possible to
-   construct a map of the entire server tree, but hostmasks prevent this
-   from being done.
-
-   The SERVER message must only be accepted from either (a) a connection
-   which is yet to be registered and is attempting to register as a
-   server, or (b) an existing connection to another server, in  which
-   case the SERVER message is introducing a new server behind that
-   server.
-
-   Most errors that occur with the receipt of a SERVER command result in
-   the connection being terminated by the destination host (target
-   SERVER).  Error replies are usually sent using the "ERROR" command
-   rather than the numeric since the ERROR command has several useful
-   properties which make it useful here.
-
-   If a SERVER message is parsed and attempts to introduce a server
-   which is already known to the receiving server, the connection from
-   which that message must be closed (following the correct procedures),
-   since a duplicate route to a server has formed and the acyclic nature
-   of the IRC tree broken.
-
-   Numeric Replies:
-
-           ERR_ALREADYREGISTRED
-
-   Example:
-
-
-
-
-Oikarinen & Reed                                               [Page 16]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-SERVER test.oulu.fi 1 :[tolsun.oulu.fi] Experimental server
-                                ; New server test.oulu.fi introducing
-                                itself and attempting to register.  The
-                                name in []'s is the hostname for the
-                                host running test.oulu.fi.
-
-
-:tolsun.oulu.fi SERVER csd.bu.edu 5 :BU Central Server
-                                ; Server tolsun.oulu.fi is our uplink
-                                for csd.bu.edu which is 5 hops away.
-
-4.1.5 Oper
-
-      Command: OPER
-   Parameters: <user> <password>
-
-   OPER message is used by a normal user to obtain operator privileges.
-   The combination of <user> and <password> are required to gain
-   Operator privileges.
-
-   If the client sending the OPER command supplies the correct password
-   for the given user, the server then informs the rest of the network
-   of the new operator by issuing a "MODE +o" for the clients nickname.
-
-   The OPER message is client-server only.
-
-   Numeric Replies:
-
-           ERR_NEEDMOREPARAMS              RPL_YOUREOPER
-           ERR_NOOPERHOST                  ERR_PASSWDMISMATCH
-
-   Example:
-
-   OPER foo bar                    ; Attempt to register as an operator
-                                   using a username of "foo" and "bar" as
-                                   the password.
-
-4.1.6 Quit
-
-      Command: QUIT
-   Parameters: [<Quit message>]
-
-   A client session is ended with a quit message.  The server must close
-   the connection to a client which sends a QUIT message. If a "Quit
-   Message" is given, this will be sent instead of the default message,
-   the nickname.
-
-   When netsplits (disconnecting of two servers) occur, the quit message
-
-
-
-Oikarinen & Reed                                               [Page 17]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   is composed of the names of two servers involved, separated by a
-   space.  The first name is that of the server which is still connected
-   and the second name is that of the server that has become
-   disconnected.
-
-   If, for some other reason, a client connection is closed without  the
-   client  issuing  a  QUIT  command  (e.g.  client  dies and EOF occurs
-   on socket), the server is required to fill in the quit  message  with
-   some sort  of  message  reflecting the nature of the event which
-   caused it to happen.
-
-   Numeric Replies:
-
-           None.
-
-   Examples:
-
-   QUIT :Gone to have lunch        ; Preferred message format.
-
-4.1.7 Server quit message
-
-      Command: SQUIT
-   Parameters: <server> <comment>
-
-   The SQUIT message is needed to tell about quitting or dead servers.
-   If a server wishes to break the connection to another server it must
-   send a SQUIT message to the other server, using the the name of the
-   other server as the server parameter, which then closes its
-   connection to the quitting server.
-
-   This command is also available operators to help keep a network of
-   IRC servers connected in an orderly fashion.  Operators may also
-   issue an SQUIT message for a remote server connection.  In this case,
-   the SQUIT must be parsed by each server inbetween the operator and
-   the remote server, updating the view of the network held by each
-   server as explained below.
-
-   The <comment> should be supplied by all operators who execute a SQUIT
-   for a remote server (that is not connected to the server they are
-   currently on) so that other operators are aware for the reason of
-   this action.  The <comment> is also filled in by servers which may
-   place an error or similar message here.
-
-   Both of the servers which are on either side of the connection being
-   closed are required to to send out a SQUIT message (to all its other
-   server connections) for all other servers which are considered to be
-   behind that link.
-
-
-
-
-Oikarinen & Reed                                               [Page 18]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   Similarly, a QUIT message must be sent to the other connected servers
-   rest of the network on behalf of all clients behind that link.  In
-   addition to this, all channel members of a channel which lost a
-   member due to the split must be sent a QUIT message.
-
-   If a server connection is terminated prematurely (e.g. the server  on
-   the  other  end  of  the  link  died),  the  server  which  detects
-   this disconnection is required to inform the rest of  the  network
-   that  the connection  has  closed  and  fill  in  the comment field
-   with something appropriate.
-
-   Numeric replies:
-
-           ERR_NOPRIVILEGES                ERR_NOSUCHSERVER
-
-   Example:
-
-   SQUIT tolsun.oulu.fi :Bad Link ? ; the server link tolson.oulu.fi has
-                                   been terminated because of "Bad Link".
-
-   :Trillian SQUIT cm22.eng.umd.edu :Server out of control
-                                    ; message from Trillian to disconnect
-                                   "cm22.eng.umd.edu" from the net
-                                    because "Server out of control".
-
-4.2 Channel operations
-
-   This group of messages is concerned with manipulating channels, their
-   properties (channel modes), and their contents (typically clients).
-   In implementing these, a number of race conditions are inevitable
-   when clients at opposing ends of a network send commands which will
-   ultimately clash.  It is also required that servers keep a nickname
-   history to ensure that wherever a <nick> parameter is given, the
-   server check its history in case it has recently been changed.
-
-4.2.1 Join message
-
-      Command: JOIN
-   Parameters: <channel>{,<channel>} [<key>{,<key>}]
-
-   The JOIN command is used by client to start listening a specific
-   channel. Whether or not a client is allowed to join a channel is
-   checked only by the server the client is connected to; all other
-   servers automatically add the user to the channel when it is received
-   from other servers.  The conditions which affect this are as follows:
-
-           1.  the user must be invited if the channel is invite-only;
-
-
-
-
-Oikarinen & Reed                                               [Page 19]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-           2.  the user's nick/username/hostname must not match any
-               active bans;
-
-           3.  the correct key (password) must be given if it is set.
-
-   These are discussed in more detail under the MODE command (see
-   section 4.2.3 for more details).
-
-   Once a user has joined a channel, they receive notice about all
-   commands their server receives which affect the channel.  This
-   includes MODE, KICK, PART, QUIT and of course PRIVMSG/NOTICE.  The
-   JOIN command needs to be broadcast to all servers so that each server
-   knows where to find the users who are on the channel.  This allows
-   optimal delivery of PRIVMSG/NOTICE messages to the channel.
-
-   If a JOIN is successful, the user is then sent the channel's topic
-   (using RPL_TOPIC) and the list of users who are on the channel (using
-   RPL_NAMREPLY), which must include the user joining.
-
-   Numeric Replies:
-
-           ERR_NEEDMOREPARAMS              ERR_BANNEDFROMCHAN
-           ERR_INVITEONLYCHAN              ERR_BADCHANNELKEY
-           ERR_CHANNELISFULL               ERR_BADCHANMASK
-           ERR_NOSUCHCHANNEL               ERR_TOOMANYCHANNELS
-           RPL_TOPIC
-
-   Examples:
-
-   JOIN #foobar                    ; join channel #foobar.
-
-   JOIN &foo fubar                 ; join channel &foo using key "fubar".
-
-   JOIN #foo,&bar fubar            ; join channel #foo using key "fubar"
-                                   and &bar using no key.
-
-   JOIN #foo,#bar fubar,foobar     ; join channel #foo using key "fubar".
-                                   and channel #bar using key "foobar".
-
-   JOIN #foo,#bar                  ; join channels #foo and #bar.
-
-   :WiZ JOIN #Twilight_zone        ; JOIN message from WiZ
-
-4.2.2 Part message
-
-      Command: PART
-   Parameters: <channel>{,<channel>}
-
-
-
-
-Oikarinen & Reed                                               [Page 20]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   The PART message causes the client sending the message to be removed
-   from the list of active users for all given channels listed in the
-   parameter string.
-
-   Numeric Replies:
-
-           ERR_NEEDMOREPARAMS              ERR_NOSUCHCHANNEL
-           ERR_NOTONCHANNEL
-
-   Examples:
-
-   PART #twilight_zone             ; leave channel "#twilight_zone"
-
-   PART #oz-ops,&group5            ; leave both channels "&group5" and
-                                   "#oz-ops".
-
-4.2.3 Mode message
-
-      Command: MODE
-
-   The MODE command is a dual-purpose command in IRC.  It allows both
-   usernames and channels to have their mode changed.  The rationale for
-   this choice is that one day nicknames will be obsolete and the
-   equivalent property will be the channel.
-
-   When parsing MODE messages, it is recommended that the entire message
-   be parsed first and then the changes which resulted then passed on.
-
-4.2.3.1 Channel modes
-
-   Parameters: <channel> {[+|-]|o|p|s|i|t|n|b|v} [<limit>] [<user>]
-               [<ban mask>]
-
-   The MODE command is provided so that channel operators may change the
-   characteristics of `their' channel.  It is also required that servers
-   be able to change channel modes so that channel operators may be
-   created.
-
-   The various modes available for channels are as follows:
-
-           o - give/take channel operator privileges;
-           p - private channel flag;
-           s - secret channel flag;
-           i - invite-only channel flag;
-           t - topic settable by channel operator only flag;
-           n - no messages to channel from clients on the outside;
-           m - moderated channel;
-           l - set the user limit to channel;
-
-
-
-Oikarinen & Reed                                               [Page 21]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-           b - set a ban mask to keep users out;
-           v - give/take the ability to speak on a moderated channel;
-           k - set a channel key (password).
-
-   When using the 'o' and 'b' options, a restriction on a total of three
-   per mode command has been imposed.  That is, any combination of 'o'
-   and
-
-4.2.3.2 User modes
-
-   Parameters: <nickname> {[+|-]|i|w|s|o}
-
-   The user MODEs are typically changes which affect either how the
-   client is seen by others or what 'extra' messages the client is sent.
-   A user MODE command may only be accepted if both the sender of the
-   message and the nickname given as a parameter are both the same.
-
-   The available modes are as follows:
-
-           i - marks a users as invisible;
-           s - marks a user for receipt of server notices;
-           w - user receives wallops;
-           o - operator flag.
-
-   Additional modes may be available later on.
-
-   If a user attempts to make themselves an operator using the "+o"
-   flag, the attempt should be ignored.  There is no restriction,
-   however, on anyone `deopping' themselves (using "-o").  Numeric
-   Replies:
-
-           ERR_NEEDMOREPARAMS              RPL_CHANNELMODEIS
-           ERR_CHANOPRIVSNEEDED            ERR_NOSUCHNICK
-           ERR_NOTONCHANNEL                ERR_KEYSET
-           RPL_BANLIST                     RPL_ENDOFBANLIST
-           ERR_UNKNOWNMODE                 ERR_NOSUCHCHANNEL
-
-           ERR_USERSDONTMATCH              RPL_UMODEIS
-           ERR_UMODEUNKNOWNFLAG
-
-   Examples:
-
-           Use of Channel Modes:
-
-MODE #Finnish +im               ; Makes #Finnish channel moderated and
-                                'invite-only'.
-
-MODE #Finnish +o Kilroy         ; Gives 'chanop' privileges to Kilroy on
-
-
-
-Oikarinen & Reed                                               [Page 22]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                                channel #Finnish.
-
-MODE #Finnish +v Wiz            ; Allow WiZ to speak on #Finnish.
-
-MODE #Fins -s                   ; Removes 'secret' flag from channel
-                                #Fins.
-
-MODE #42 +k oulu                ; Set the channel key to "oulu".
-
-MODE #eu-opers +l 10            ; Set the limit for the number of users
-                                on channel to 10.
-
-MODE &oulu +b                   ; list ban masks set for channel.
-
-MODE &oulu +b *!*@*             ; prevent all users from joining.
-
-MODE &oulu +b *!*@*.edu         ; prevent any user from a hostname
-                                matching *.edu from joining.
-
-        Use of user Modes:
-
-:MODE WiZ -w                    ; turns reception of WALLOPS messages
-                                off for WiZ.
-
-:Angel MODE Angel +i            ; Message from Angel to make themselves
-                                invisible.
-
-MODE WiZ -o                     ; WiZ 'deopping' (removing operator
-                                status).  The plain reverse of this
-                                command ("MODE WiZ +o") must not be
-                                allowed from users since would bypass
-                                the OPER command.
-
-4.2.4 Topic message
-
-      Command: TOPIC
-   Parameters: <channel> [<topic>]
-
-   The TOPIC message is used to change or view the topic of a channel.
-   The topic for channel <channel> is returned if there is no <topic>
-   given.  If the <topic> parameter is present, the topic for that
-   channel will be changed, if the channel modes permit this action.
-
-   Numeric Replies:
-
-           ERR_NEEDMOREPARAMS              ERR_NOTONCHANNEL
-           RPL_NOTOPIC                     RPL_TOPIC
-           ERR_CHANOPRIVSNEEDED
-
-
-
-Oikarinen & Reed                                               [Page 23]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   Examples:
-
-   :Wiz TOPIC #test :New topic     ;User Wiz setting the topic.
-
-   TOPIC #test :another topic      ;set the topic on #test to "another
-                                   topic".
-
-   TOPIC #test                     ; check the topic for #test.
-
-4.2.5 Names message
-
-      Command: NAMES
-   Parameters: [<channel>{,<channel>}]
-
-   By using the NAMES command, a user can list all nicknames that are
-   visible to them on any channel that they can see.  Channel names
-   which they can see are those which aren't private (+p) or secret (+s)
-   or those which they are actually on.  The <channel> parameter
-   specifies which channel(s) to return information about if valid.
-   There is no error reply for bad channel names.
-
-   If no <channel> parameter is given, a list of all channels and their
-   occupants is returned.  At the end of this list, a list of users who
-   are visible but either not on any channel or not on a visible channel
-   are listed as being on `channel' "*".
-
-   Numerics:
-
-           RPL_NAMREPLY                    RPL_ENDOFNAMES
-
-   Examples:
-
-   NAMES #twilight_zone,#42        ; list visible users on #twilight_zone
-                                   and #42 if the channels are visible to
-                                   you.
-
-   NAMES                           ; list all visible channels and users
-
-4.2.6 List message
-
-      Command: LIST
-   Parameters: [<channel>{,<channel>} [<server>]]
-
-   The list message is used to list channels and their topics.  If  the
-   <channel>  parameter  is  used,  only  the  status  of  that  channel
-   is displayed.  Private  channels  are  listed  (without  their
-   topics)  as channel "Prv" unless the client generating the query is
-   actually on that channel.  Likewise, secret channels are not listed
-
-
-
-Oikarinen & Reed                                               [Page 24]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   at  all  unless  the client is a member of the channel in question.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER                RPL_LISTSTART
-           RPL_LIST                        RPL_LISTEND
-
-   Examples:
-
-   LIST                            ; List all channels.
-
-   LIST #twilight_zone,#42         ; List channels #twilight_zone and #42
-
-4.2.7 Invite message
-
-      Command: INVITE
-   Parameters: <nickname> <channel>
-
-   The INVITE message is used to invite users to a channel.  The
-   parameter <nickname> is the nickname of the person to be invited to
-   the target channel <channel>.  There is no requirement that the
-   channel the target user is being invited to must exist or be a valid
-   channel.  To invite a user to a channel which is invite only (MODE
-   +i), the client sending the invite must be recognised as being a
-   channel operator on the given channel.
-
-   Numeric Replies:
-
-           ERR_NEEDMOREPARAMS              ERR_NOSUCHNICK
-           ERR_NOTONCHANNEL                ERR_USERONCHANNEL
-           ERR_CHANOPRIVSNEEDED
-           RPL_INVITING                    RPL_AWAY
-
-   Examples:
-
-   :Angel INVITE Wiz #Dust         ; User Angel inviting WiZ to channel
-                                   #Dust
-
-   INVITE Wiz #Twilight_Zone       ; Command to invite WiZ to
-                                   #Twilight_zone
-
-4.2.8 Kick command
-
-      Command: KICK
-   Parameters: <channel> <user> [<comment>]
-
-   The KICK command can be  used  to  forcibly  remove  a  user  from  a
-   channel.   It  'kicks  them  out'  of the channel (forced PART).
-
-
-
-Oikarinen & Reed                                               [Page 25]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   Only a channel operator may kick another user out of a  channel.
-   Each  server that  receives  a KICK message checks that it is valid
-   (ie the sender is actually a  channel  operator)  before  removing
-   the  victim  from  the channel.
-
-   Numeric Replies:
-
-           ERR_NEEDMOREPARAMS              ERR_NOSUCHCHANNEL
-           ERR_BADCHANMASK                 ERR_CHANOPRIVSNEEDED
-           ERR_NOTONCHANNEL
-
-   Examples:
-
-KICK &Melbourne Matthew         ; Kick Matthew from &Melbourne
-
-KICK #Finnish John :Speaking English
-                                ; Kick John from #Finnish using
-                                "Speaking English" as the reason
-                                (comment).
-
-:WiZ KICK #Finnish John         ; KICK message from WiZ to remove John
-                                from channel #Finnish
-
-NOTE:
-     It is possible to extend the KICK command parameters to the
-following:
-
-<channel>{,<channel>} <user>{,<user>} [<comment>]
-
-4.3 Server queries and commands
-
-   The server query group of commands has been designed to return
-   information about any server which is connected to the network.  All
-   servers connected must respond to these queries and respond
-   correctly.  Any invalid response (or lack thereof) must be considered
-   a sign of a broken server and it must be disconnected/disabled as
-   soon as possible until the situation is remedied.
-
-   In these queries, where a parameter appears as "<server>", it will
-   usually mean it can be a nickname or a server or a wildcard name of
-   some sort.  For each parameter, however, only one query and set of
-   replies is to be generated.
-
-4.3.1 Version message
-
-      Command: VERSION
-   Parameters: [<server>]
-
-
-
-
-Oikarinen & Reed                                               [Page 26]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   The VERSION message is used  to  query  the  version  of  the  server
-   program.  An optional parameter <server> is used to query the version
-   of the server program which a client is not directly connected to.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER                RPL_VERSION
-
-   Examples:
-
-   :Wiz VERSION *.se               ; message from Wiz to check the version
-                                   of a server matching "*.se"
-
-   VERSION tolsun.oulu.fi          ; check the version of server
-                                   "tolsun.oulu.fi".
-
-4.3.2 Stats message
-
-      Command: STATS
-   Parameters: [<query> [<server>]]
-
-   The stats message is used to query statistics of certain server.  If
-   <server> parameter is omitted, only the end of stats reply is sent
-   back.  The implementation of this command is highly dependent on the
-   server which replies, although the server must be able to supply
-   information as described by the queries below (or similar).
-
-   A query may be given by any single letter which is only checked by
-   the destination server (if given as the <server> parameter) and is
-   otherwise passed on by intermediate servers, ignored and unaltered.
-   The following queries are those found in the current IRC
-   implementation and provide a large portion of the setup information
-   for that server.  Although these may not be supported in the same way
-   by other versions, all servers should be able to supply a valid reply
-   to a STATS query which is consistent with the reply formats currently
-   used and the purpose of the query.
-
-   The currently supported queries are:
-
-           c - returns a list of servers which the server may connect
-               to or allow connections from;
-           h - returns a list of servers which are either forced to be
-               treated as leaves or allowed to act as hubs;
-           i - returns a list of hosts which the server allows a client
-               to connect from;
-           k - returns a list of banned username/hostname combinations
-               for that server;
-           l - returns a list of the server's connections, showing how
-
-
-
-Oikarinen & Reed                                               [Page 27]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-               long each connection has been established and the traffic
-               over that connection in bytes and messages for each
-               direction;
-           m - returns a list of commands supported by the server and
-               the usage count for each if the usage count is non zero;
-           o - returns a list of hosts from which normal clients may
-               become operators;
-           y - show Y (Class) lines from server's configuration file;
-           u - returns a string showing how long the server has been up.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER
-           RPL_STATSCLINE                  RPL_STATSNLINE
-           RPL_STATSILINE                  RPL_STATSKLINE
-           RPL_STATSQLINE                  RPL_STATSLLINE
-           RPL_STATSLINKINFO               RPL_STATSUPTIME
-           RPL_STATSCOMMANDS               RPL_STATSOLINE
-           RPL_STATSHLINE                  RPL_ENDOFSTATS
-
-   Examples:
-
-STATS m                         ; check the command usage for the server
-                                you are connected to
-
-:Wiz STATS c eff.org            ; request by WiZ for C/N line
-                                information from server eff.org
-
-4.3.3 Links message
-
-      Command: LINKS
-   Parameters: [[<remote server>] <server mask>]
-
-   With LINKS, a user can list all servers which are known by the server
-   answering the query.  The returned list of servers must match the
-   mask, or if no mask is given, the full list is returned.
-
-   If <remote server> is given in addition to <server mask>, the LINKS
-   command is forwarded to the first server found that matches that name
-   (if any), and that server is then required to answer the query.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER
-           RPL_LINKS                       RPL_ENDOFLINKS
-
-   Examples:
-
-
-
-
-Oikarinen & Reed                                               [Page 28]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-LINKS *.au                      ; list all servers which have a name
-                                that matches *.au;
-
-:WiZ LINKS *.bu.edu *.edu       ; LINKS message from WiZ to the first
-                                server matching *.edu for a list of
-                                servers matching *.bu.edu.
-
-4.3.4 Time message
-
-      Command: TIME
-   Parameters: [<server>]
-
-   The time message is used to query local time from the specified
-   server. If the server parameter is not given, the server handling the
-   command must reply to the query.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER                RPL_TIME
-
-   Examples:
-
-   TIME tolsun.oulu.fi             ; check the time on the server
-                                   "tolson.oulu.fi"
-
-   Angel TIME *.au                 ; user angel checking the time on a
-                                   server matching "*.au"
-
-4.3.5 Connect message
-
-      Command: CONNECT
-   Parameters: <target server> [<port> [<remote server>]]
-
-   The CONNECT command can be used to force a server to try to establish
-   a new connection to another server immediately.  CONNECT is a
-   privileged command and is to be available only to IRC Operators.  If
-   a remote server is given then the CONNECT attempt is made by that
-   server to <target server> and <port>.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER                ERR_NOPRIVILEGES
-           ERR_NEEDMOREPARAMS
-
-   Examples:
-
-CONNECT tolsun.oulu.fi          ; Attempt to connect a server to
-                                tolsun.oulu.fi
-
-
-
-Oikarinen & Reed                                               [Page 29]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-:WiZ CONNECT eff.org 6667 csd.bu.edu
-                                ; CONNECT attempt by WiZ to get servers
-                                eff.org and csd.bu.edu connected on port
-                                6667.
-
-4.3.6 Trace message
-
-      Command: TRACE
-   Parameters: [<server>]
-
-   TRACE command is used to find the route to specific server.  Each
-   server that processes this message must tell the sender about it by
-   sending a reply indicating it is a pass-through link, forming a chain
-   of replies similar to that gained from using "traceroute".  After
-   sending this reply back, it must then send the TRACE message to the
-   next server until given server is reached.  If the <server> parameter
-   is omitted, it is recommended that TRACE command send a message to
-   the sender telling which servers the current server has direct
-   connection to.
-
-   If the destination given by "<server>" is an actual server, then the
-   destination server is required to report all servers and users which
-   are connected to it, although only operators are permitted to see
-   users present.  If the destination given by <server> is a nickname,
-   they only a reply for that nickname is given.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER
-
-   If the TRACE message is destined for another server, all intermediate
-   servers must return a RPL_TRACELINK reply to indicate that the TRACE
-   passed through it and where its going next.
-
-           RPL_TRACELINK
-   A TRACE reply may be composed of any number of the following numeric
-   replies.
-
-           RPL_TRACECONNECTING             RPL_TRACEHANDSHAKE
-           RPL_TRACEUNKNOWN                RPL_TRACEOPERATOR
-           RPL_TRACEUSER                   RPL_TRACESERVER
-           RPL_TRACESERVICE                RPL_TRACENEWTYPE
-           RPL_TRACECLASS
-
-   Examples:
-
-TRACE *.oulu.fi                 ; TRACE to a server matching *.oulu.fi
-
-
-
-
-Oikarinen & Reed                                               [Page 30]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-:WiZ TRACE AngelDust            ; TRACE issued by WiZ to nick AngelDust
-
-4.3.7 Admin command
-
-      Command: ADMIN
-   Parameters: [<server>]
-
-   The admin message is used to find the name of the administrator of
-   the given server, or current server if <server> parameter is omitted.
-   Each server must have the ability to forward ADMIN messages to other
-   servers.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER
-           RPL_ADMINME                     RPL_ADMINLOC1
-           RPL_ADMINLOC2                   RPL_ADMINEMAIL
-
-   Examples:
-
-   ADMIN tolsun.oulu.fi            ; request an ADMIN reply from
-                                   tolsun.oulu.fi
-
-   :WiZ ADMIN *.edu                ; ADMIN request from WiZ for first
-                                   server found to match *.edu.
-
-4.3.8 Info command
-
-      Command: INFO
-   Parameters: [<server>]
-
-   The INFO command is required to return information which describes
-   the server: its version, when it was compiled, the patchlevel, when
-   it was started, and any other miscellaneous information which may be
-   considered to be relevant.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER
-           RPL_INFO                        RPL_ENDOFINFO
-
-   Examples:
-
-   INFO csd.bu.edu                 ; request an INFO reply from
-   csd.bu.edu
-
-   :Avalon INFO *.fi               ; INFO request from Avalon for first
-                                   server found to match *.fi.
-
-
-
-Oikarinen & Reed                                               [Page 31]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   INFO Angel                      ; request info from the server that
-                                   Angel is connected to.
-
-4.4 Sending messages
-
-   The main purpose of the IRC protocol is to provide a base for clients
-   to communicate with each other.  PRIVMSG and NOTICE are the only
-   messages available which actually perform delivery of a text message
-   from one client to another - the rest just make it possible and try
-   to ensure it happens in a reliable and structured manner.
-
-4.4.1 Private messages
-
-      Command: PRIVMSG
-   Parameters: <receiver>{,<receiver>} <text to be sent>
-
-   PRIVMSG is used to send private messages between users.  <receiver>
-   is the nickname of the receiver of the message.  <receiver> can also
-   be a list of names or channels separated with commas.
-
-   The <receiver> parameter may also me a host mask  (#mask)  or  server
-   mask  ($mask).   In  both cases the server will only send the PRIVMSG
-   to those who have a server or host matching the mask.  The mask  must
-   have at  least  1  (one)  "."  in it and no wildcards following the
-   last ".".  This requirement exists to prevent people sending messages
-   to  "#*"  or "$*",  which  would  broadcast  to  all  users; from
-   experience, this is abused more than used responsibly and properly.
-   Wildcards are  the  '*' and  '?'   characters.   This  extension  to
-   the PRIVMSG command is only available to Operators.
-
-   Numeric Replies:
-
-           ERR_NORECIPIENT                 ERR_NOTEXTTOSEND
-           ERR_CANNOTSENDTOCHAN            ERR_NOTOPLEVEL
-           ERR_WILDTOPLEVEL                ERR_TOOMANYTARGETS
-           ERR_NOSUCHNICK
-           RPL_AWAY
-
-   Examples:
-
-:Angel PRIVMSG Wiz :Hello are you receiving this message ?
-                                ; Message from Angel to Wiz.
-
-PRIVMSG Angel :yes I'm receiving it !receiving it !'u>(768u+1n) .br ;
-                                Message to Angel.
-
-PRIVMSG jto@tolsun.oulu.fi :Hello !
-                                ; Message to a client on server
-
-
-
-Oikarinen & Reed                                               [Page 32]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                                tolsun.oulu.fi with username of "jto".
-
-PRIVMSG $*.fi :Server tolsun.oulu.fi rebooting.
-                                ; Message to everyone on a server which
-                                has a name matching *.fi.
-
-PRIVMSG #*.edu :NSFNet is undergoing work, expect interruptions
-                                ; Message to all users who come from a
-                                host which has a name matching *.edu.
-
-4.4.2 Notice
-
-      Command: NOTICE
-   Parameters: <nickname> <text>
-
-   The NOTICE message is used similarly to PRIVMSG.  The difference
-   between NOTICE and PRIVMSG is that automatic replies must never be
-   sent in response to a NOTICE message.  This rule applies to servers
-   too - they must not send any error reply back to the client on
-   receipt of a notice.  The object of this rule is to avoid loops
-   between a client automatically sending something in response to
-   something it received.  This is typically used by automatons (clients
-   with either an AI or other interactive program controlling their
-   actions) which are always seen to be replying lest they end up in a
-   loop with another automaton.
-
-   See PRIVMSG for more details on replies and examples.
-
-4.5 User based queries
-
-   User queries are a group of commands which are primarily concerned
-   with finding details on a particular user or group users.  When using
-   wildcards with any of these commands, if they match, they will only
-   return information on users who are 'visible' to you.  The visibility
-   of a user is determined as a combination of the user's mode and the
-   common set of channels you are both on.
-
-4.5.1 Who query
-
-      Command: WHO
-   Parameters: [<name> [<o>]]
-
-   The WHO message is used by a client to generate a query which returns
-   a list of information which 'matches' the <name> parameter given by
-   the client.  In the absence of the <name> parameter, all visible
-   (users who aren't invisible (user mode +i) and who don't have a
-   common channel with the requesting client) are listed.  The same
-   result can be achieved by using a <name> of "0" or any wildcard which
-
-
-
-Oikarinen & Reed                                               [Page 33]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   will end up matching every entry possible.
-
-   The <name> passed to WHO is matched against users' host, server, real
-   name and nickname if the channel <name> cannot be found.
-
-   If the "o" parameter is passed only operators are returned according
-   to the name mask supplied.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER
-           RPL_WHOREPLY                    RPL_ENDOFWHO
-
-   Examples:
-
-   WHO *.fi                        ; List all users who match against
-                                   "*.fi".
-
-   WHO jto* o                      ; List all users with a match against
-                                   "jto*" if they are an operator.
-
-4.5.2 Whois query
-
-      Command: WHOIS
-   Parameters: [<server>] <nickmask>[,<nickmask>[,...]]
-
-   This message is used to query information about particular user.  The
-   server will answer this message with several numeric messages
-   indicating different statuses of each user which matches the nickmask
-   (if you are entitled to see them).  If no wildcard is present in the
-   <nickmask>, any information about that nick which you are allowed to
-   see is presented.  A comma (',') separated list of nicknames may be
-   given.
-
-   The latter version sends the query to a specific server.  It is
-   useful if you want to know how long the user in question has been
-   idle as only local server (ie. the server the user is directly
-   connected to) knows that information, while everything else is
-   globally known.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER                ERR_NONICKNAMEGIVEN
-           RPL_WHOISUSER                   RPL_WHOISCHANNELS
-           RPL_WHOISCHANNELS               RPL_WHOISSERVER
-           RPL_AWAY                        RPL_WHOISOPERATOR
-           RPL_WHOISIDLE                   ERR_NOSUCHNICK
-           RPL_ENDOFWHOIS
-
-
-
-Oikarinen & Reed                                               [Page 34]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   Examples:
-
-   WHOIS wiz                       ; return available user information
-                                   about nick WiZ
-
-   WHOIS eff.org trillian          ; ask server eff.org for user
-                                   information about trillian
-
-4.5.3 Whowas
-
-      Command: WHOWAS
-   Parameters: <nickname> [<count> [<server>]]
-
-   Whowas asks for information about a nickname which no longer exists.
-   This may either be due to a nickname change or the user leaving IRC.
-   In response to this query, the server searches through its nickname
-   history, looking for any nicks which are lexically the same (no wild
-   card matching here).  The history is searched backward, returning the
-   most recent entry first.  If there are multiple entries, up to
-   <count> replies will be returned (or all of them if no <count>
-   parameter is given).  If a non-positive number is passed as being
-   <count>, then a full search is done.
-
-   Numeric Replies:
-
-           ERR_NONICKNAMEGIVEN             ERR_WASNOSUCHNICK
-           RPL_WHOWASUSER                  RPL_WHOISSERVER
-           RPL_ENDOFWHOWAS
-
-   Examples:
-
-   WHOWAS Wiz                      ; return all information in the nick
-                                   history about nick "WiZ";
-
-   WHOWAS Mermaid 9                ; return at most, the 9 most recent
-                                   entries in the nick history for
-                                   "Mermaid";
-
-   WHOWAS Trillian 1 *.edu         ; return the most recent history for
-                                   "Trillian" from the first server found
-                                   to match "*.edu".
-
-4.6 Miscellaneous messages
-
-   Messages in this category do not fit into any of the above categories
-   but are nonetheless still a part of and required by the protocol.
-
-
-
-
-
-Oikarinen & Reed                                               [Page 35]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-4.6.1 Kill message
-
-      Command: KILL
-   Parameters: <nickname> <comment>
-
-   The KILL message is used to cause a client-server connection to be
-   closed by the server which has the actual connection.  KILL is used
-   by servers when they encounter a duplicate entry in the list of valid
-   nicknames and is used to remove both entries.  It is also available
-   to operators.
-
-   Clients which have automatic reconnect algorithms effectively make
-   this command useless since the disconnection is only brief.  It does
-   however break the flow of data and can be used to stop large amounts
-   of being abused, any user may elect to receive KILL messages
-   generated for others to keep an 'eye' on would be trouble spots.
-
-   In an arena where nicknames are required to be globally unique at all
-   times, KILL messages are sent whenever 'duplicates' are detected
-   (that is an attempt to register two users with the same nickname) in
-   the hope that both of them will disappear and only 1 reappear.
-
-   The comment given must reflect the actual reason for the KILL.  For
-   server-generated KILLs it usually is made up of details concerning
-   the origins of the two conflicting nicknames.  For users it is left
-   up to them to provide an adequate reason to satisfy others who see
-   it.  To prevent/discourage fake KILLs from being generated to hide
-   the identify of the KILLer, the comment also shows a 'kill-path'
-   which is updated by each server it passes through, each prepending
-   its name to the path.
-
-   Numeric Replies:
-
-           ERR_NOPRIVILEGES                ERR_NEEDMOREPARAMS
-           ERR_NOSUCHNICK                  ERR_CANTKILLSERVER
-
-
-   KILL David (csd.bu.edu <- tolsun.oulu.fi)
-                                   ; Nickname collision between csd.bu.edu
-                                   and tolson.oulu.fi
-
-
-   NOTE:
-   It is recommended that only Operators be allowed to kill other users
-   with KILL message.  In an ideal world not even operators would need
-   to do this and it would be left to servers to deal with.
-
-
-
-
-
-Oikarinen & Reed                                               [Page 36]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-4.6.2 Ping message
-
-      Command: PING
-   Parameters: <server1> [<server2>]
-
-   The PING message is used to test the presence of an active client at
-   the other end of the connection.  A PING message is sent at regular
-   intervals if no other activity detected coming from a connection.  If
-   a connection fails to respond to a PING command within a set amount
-   of time, that connection is closed.
-
-   Any client which receives a PING message must respond to <server1>
-   (server which sent the PING message out) as quickly as possible with
-   an appropriate PONG message to indicate it is still there and alive.
-   Servers should not respond to PING commands but rely on PINGs from
-   the other end of the connection to indicate the connection is alive.
-   If the <server2> parameter is specified, the PING message gets
-   forwarded there.
-
-   Numeric Replies:
-
-           ERR_NOORIGIN                    ERR_NOSUCHSERVER
-
-   Examples:
-
-   PING tolsun.oulu.fi             ; server sending a PING message to
-                                   another server to indicate it is still
-                                   alive.
-
-   PING WiZ                        ; PING message being sent to nick WiZ
-
-4.6.3 Pong message
-
-      Command: PONG
-   Parameters: <daemon> [<daemon2>]
-
-   PONG message is a reply to ping message.  If parameter <daemon2> is
-   given this message must be forwarded to given daemon.  The <daemon>
-   parameter is the name of the daemon who has responded to PING message
-   and generated this message.
-
-   Numeric Replies:
-
-           ERR_NOORIGIN                    ERR_NOSUCHSERVER
-
-   Examples:
-
-   PONG csd.bu.edu tolsun.oulu.fi  ; PONG message from csd.bu.edu to
-
-
-
-Oikarinen & Reed                                               [Page 37]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                                   tolsun.oulu.fi
-
-4.6.4 Error
-
-      Command: ERROR
-   Parameters: <error message>
-
-   The ERROR command is for use by servers when reporting a serious or
-   fatal error to its operators.  It may also be sent from one server to
-   another but must not be accepted from any normal unknown clients.
-
-   An ERROR message is for use for reporting errors which occur with a
-   server-to-server link only.  An ERROR message is sent to the server
-   at the other end (which sends it to all of its connected operators)
-   and to all operators currently connected.  It is not to be passed
-   onto any other servers by a server if it is received from a server.
-
-   When a server sends a received ERROR message to its operators, the
-   message should be encapsulated inside a NOTICE message, indicating
-   that the client was not responsible for the error.
-
-   Numerics:
-
-           None.
-
-   Examples:
-
-   ERROR :Server *.fi already exists; ERROR message to the other server
-                                   which caused this error.
-
-   NOTICE WiZ :ERROR from csd.bu.edu -- Server *.fi already exists
-                                   ; Same ERROR message as above but sent
-                                   to user WiZ on the other server.
-
-5. OPTIONALS
-
-   This section describes OPTIONAL messages.  They are not required in a
-   working server implementation of the protocol described herein.  In
-   the absence of the option, an error reply message must be generated
-   or an unknown command error.  If the message is destined for another
-   server to answer then it must be passed on (elementary parsing
-   required) The allocated numerics for this are listed with the
-   messages below.
-
-5.1 Away
-
-      Command: AWAY
-   Parameters: [message]
-
-
-
-Oikarinen & Reed                                               [Page 38]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   With the AWAY message, clients can set an automatic reply string for
-   any PRIVMSG commands directed at them (not to a channel they are on).
-   The automatic reply is sent by the server to client sending the
-   PRIVMSG command.  The only replying server is the one to which the
-   sending client is connected to.
-
-   The AWAY message is used either with one parameter (to set an AWAY
-   message) or with no parameters (to remove the AWAY message).
-
-   Numeric Replies:
-
-           RPL_UNAWAY                      RPL_NOWAWAY
-
-   Examples:
-
-   AWAY :Gone to lunch.  Back in 5 ; set away message to "Gone to lunch.
-                                   Back in 5".
-
-   :WiZ AWAY                       ; unmark WiZ as being away.
-
-
-5.2 Rehash message
-
-      Command: REHASH
-   Parameters: None
-
-   The rehash message can be used by the operator to force the server to
-   re-read and process its configuration file.
-
-   Numeric Replies:
-
-        RPL_REHASHING                   ERR_NOPRIVILEGES
-
-Examples:
-
-REHASH                          ; message from client with operator
-                                status to server asking it to reread its
-                                configuration file.
-
-5.3 Restart message
-
-      Command: RESTART
-   Parameters: None
-
-   The restart message can only be used by an operator to force a server
-   restart itself.  This message is optional since it may be viewed as a
-   risk to allow arbitrary people to connect to a server as an operator
-   and execute this command, causing (at least) a disruption to service.
-
-
-
-Oikarinen & Reed                                               [Page 39]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   The RESTART command must always be fully processed by the server to
-   which the sending client is connected and not be passed onto other
-   connected servers.
-
-   Numeric Replies:
-
-           ERR_NOPRIVILEGES
-
-   Examples:
-
-   RESTART                         ; no parameters required.
-
-5.4 Summon message
-
-      Command: SUMMON
-   Parameters: <user> [<server>]
-
-   The SUMMON command can be used to give users who are on a host
-   running an IRC server a message asking them to please join IRC.  This
-   message is only sent if the target server (a) has SUMMON enabled, (b)
-   the user is logged in and (c) the server process can write to the
-   user's tty (or similar).
-
-   If no <server> parameter is given it tries to summon <user> from the
-   server the client is connected to is assumed as the target.
-
-   If summon is not enabled in a server, it must return the
-   ERR_SUMMONDISABLED numeric and pass the summon message onwards.
-
-   Numeric Replies:
-
-           ERR_NORECIPIENT                 ERR_FILEERROR
-           ERR_NOLOGIN                     ERR_NOSUCHSERVER
-           RPL_SUMMONING
-
-   Examples:
-
-   SUMMON jto                      ; summon user jto on the server's host
-
-   SUMMON jto tolsun.oulu.fi       ; summon user jto on the host which a
-                                   server named "tolsun.oulu.fi" is
-                                   running.
-
-
-5.5 Users
-
-      Command: USERS
-   Parameters: [<server>]
-
-
-
-Oikarinen & Reed                                               [Page 40]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   The USERS command returns a list of users logged into the server in a
-   similar  format  to  who(1),  rusers(1)  and finger(1).  Some people
-   may disable this command on their server for security related
-   reasons.   If disabled, the correct numeric must be returned to
-   indicate this.
-
-   Numeric Replies:
-
-           ERR_NOSUCHSERVER                ERR_FILEERROR
-           RPL_USERSSTART                  RPL_USERS
-           RPL_NOUSERS                     RPL_ENDOFUSERS
-           ERR_USERSDISABLED
-
-   Disabled Reply:
-
-           ERR_USERSDISABLED
-
-   Examples:
-
-USERS eff.org                   ; request a list of users logged in on
-                                server eff.org
-
-:John USERS tolsun.oulu.fi      ; request from John for a list of users
-                                logged in on server tolsun.oulu.fi
-
-5.6 Operwall message
-
-      Command: WALLOPS
-   Parameters: Text to be sent to all operators currently online
-
-   Sends  a  message  to  all   operators   currently   online.    After
-   implementing  WALLOPS  as  a user command it was found that it was
-   often and commonly abused as a means of sending a message to a lot
-   of  people (much  similar to WALL).  Due to this it is recommended
-   that the current implementation of  WALLOPS  be  used  as  an
-   example  by  allowing  and recognising only servers as the senders of
-   WALLOPS.
-
-   Numeric Replies:
-
-           ERR_NEEDMOREPARAMS
-
-   Examples:
-
-   :csd.bu.edu WALLOPS :Connect '*.uiuc.edu 6667' from Joshua; WALLOPS
-                                   message from csd.bu.edu announcing a
-                                   CONNECT message it received and acted
-                                   upon from Joshua.
-
-
-
-Oikarinen & Reed                                               [Page 41]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-5.7 Userhost message
-
-      Command: USERHOST
-   Parameters: <nickname>{<space><nickname>}
-
-   The USERHOST command takes a list of up to 5 nicknames, each
-   separated by a space character and returns a list of information
-   about each nickname that it found.  The returned list has each reply
-   separated by a space.
-
-   Numeric Replies:
-
-           RPL_USERHOST                    ERR_NEEDMOREPARAMS
-
-   Examples:
-
-   USERHOST Wiz Michael Marty p    ;USERHOST request for information on
-                                   nicks "Wiz", "Michael", "Marty" and "p"
-
-5.8 Ison message
-
-      Command: ISON
-   Parameters: <nickname>{<space><nickname>}
-
-   The ISON command was implemented to provide  a  quick  and  efficient
-   means  to get a response about whether a given nickname was currently
-   on IRC. ISON only takes one (1) parameter: a space-separated list of
-   nicks.  For  each  nickname in the list that is present, the server
-   adds that to its reply string.  Thus the reply string may return
-   empty (none  of  the given  nicks are present), an exact copy of the
-   parameter string (all of them present) or as any other subset of the
-   set of nicks  given  in  the parameter.  The only limit on the number
-   of nicks that may be checked is that the combined length must not be
-   too large as to cause the server to chop it off so it fits in 512
-   characters.
-
-   ISON is only be processed by the server local to the client sending
-   the command and thus not passed onto other servers for further
-   processing.
-
-   Numeric Replies:
-
-           RPL_ISON                ERR_NEEDMOREPARAMS
-
-   Examples:
-
-   ISON phone trillian WiZ jarlek Avalon Angel Monstah
-                                   ; Sample ISON request for 7 nicks.
-
-
-
-Oikarinen & Reed                                               [Page 42]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-6. REPLIES
-
-   The following is a list of numeric replies which are generated in
-   response to the commands given above.  Each numeric is given with its
-   number, name and reply string.
-
-6.1 Error Replies.
-
-        401     ERR_NOSUCHNICK
-                        "<nickname> :No such nick/channel"
-
-                - Used to indicate the nickname parameter supplied to a
-                  command is currently unused.
-
-        402     ERR_NOSUCHSERVER
-                        "<server name> :No such server"
-
-                - Used to indicate the server name given currently
-                  doesn't exist.
-
-        403     ERR_NOSUCHCHANNEL
-                        "<channel name> :No such channel"
-
-                - Used to indicate the given channel name is invalid.
-
-        404     ERR_CANNOTSENDTOCHAN
-                        "<channel name> :Cannot send to channel"
-
-                - Sent to a user who is either (a) not on a channel
-                  which is mode +n or (b) not a chanop (or mode +v) on
-                  a channel which has mode +m set and is trying to send
-                  a PRIVMSG message to that channel.
-
-        405     ERR_TOOMANYCHANNELS
-                        "<channel name> :You have joined too many \
-                         channels"
-                - Sent to a user when they have joined the maximum
-                  number of allowed channels and they try to join
-                  another channel.
-
-        406     ERR_WASNOSUCHNICK
-                        "<nickname> :There was no such nickname"
-
-                - Returned by WHOWAS to indicate there is no history
-                  information for that nickname.
-
-        407     ERR_TOOMANYTARGETS
-                        "<target> :Duplicate recipients. No message \
-
-
-
-Oikarinen & Reed                                               [Page 43]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                         delivered"
-
-                - Returned to a client which is attempting to send a
-                  PRIVMSG/NOTICE using the user@host destination format
-                  and for a user@host which has several occurrences.
-
-        409     ERR_NOORIGIN
-                        ":No origin specified"
-
-                - PING or PONG message missing the originator parameter
-                  which is required since these commands must work
-                  without valid prefixes.
-
-        411     ERR_NORECIPIENT
-                        ":No recipient given (<command>)"
-        412     ERR_NOTEXTTOSEND
-                        ":No text to send"
-        413     ERR_NOTOPLEVEL
-                        "<mask> :No toplevel domain specified"
-        414     ERR_WILDTOPLEVEL
-                        "<mask> :Wildcard in toplevel domain"
-
-                - 412 - 414 are returned by PRIVMSG to indicate that
-                  the message wasn't delivered for some reason.
-                  ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that
-                  are returned when an invalid use of
-                  "PRIVMSG $<server>" or "PRIVMSG #<host>" is attempted.
-
-        421     ERR_UNKNOWNCOMMAND
-                        "<command> :Unknown command"
-
-                - Returned to a registered client to indicate that the
-                  command sent is unknown by the server.
-
-        422     ERR_NOMOTD
-                        ":MOTD File is missing"
-
-                - Server's MOTD file could not be opened by the server.
-
-        423     ERR_NOADMININFO
-                        "<server> :No administrative info available"
-
-                - Returned by a server in response to an ADMIN message
-                  when there is an error in finding the appropriate
-                  information.
-
-        424     ERR_FILEERROR
-                ":File error doing <file op> on <file>"
-
-
-
-Oikarinen & Reed                                               [Page 44]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                - Generic error message used to report a failed file
-                  operation during the processing of a message.
-
-        431     ERR_NONICKNAMEGIVEN
-                        ":No nickname given"
-
-                - Returned when a nickname parameter expected for a
-                  command and isn't found.
-
-        432     ERR_ERRONEUSNICKNAME
-                        "<nick> :Erroneus nickname"
-
-                - Returned after receiving a NICK message which contains
-                  characters which do not fall in the defined set.  See
-                  section x.x.x for details on valid nicknames.
-
-        433     ERR_NICKNAMEINUSE
-                        "<nick> :Nickname is already in use"
-
-                - Returned when a NICK message is processed that results
-                  in an attempt to change to a currently existing
-                  nickname.
-
-        436     ERR_NICKCOLLISION
-                        "<nick> :Nickname collision KILL"
-
-                - Returned by a server to a client when it detects a
-                  nickname collision (registered of a NICK that
-                  already exists by another server).
-
-        441     ERR_USERNOTINCHANNEL
-                        "<nick> <channel> :They aren't on that channel"
-
-                - Returned by the server to indicate that the target
-                  user of the command is not on the given channel.
-
-        442     ERR_NOTONCHANNEL
-                        "<channel> :You're not on that channel"
-
-                - Returned by the server whenever a client tries to
-                  perform a channel effecting command for which the
-                  client isn't a member.
-
-        443     ERR_USERONCHANNEL
-                        "<user> <channel> :is already on channel"
-
-                - Returned when a client tries to invite a user to a
-                  channel they are already on.
-
-
-
-Oikarinen & Reed                                               [Page 45]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-        444     ERR_NOLOGIN
-                        "<user> :User not logged in"
-
-                - Returned by the summon after a SUMMON command for a
-                  user was unable to be performed since they were not
-                  logged in.
-
-        445     ERR_SUMMONDISABLED
-                        ":SUMMON has been disabled"
-
-                - Returned as a response to the SUMMON command.  Must be
-                  returned by any server which does not implement it.
-
-        446     ERR_USERSDISABLED
-                        ":USERS has been disabled"
-
-                - Returned as a response to the USERS command.  Must be
-                  returned by any server which does not implement it.
-
-        451     ERR_NOTREGISTERED
-                        ":You have not registered"
-
-                - Returned by the server to indicate that the client
-                  must be registered before the server will allow it
-                  to be parsed in detail.
-
-        461     ERR_NEEDMOREPARAMS
-                        "<command> :Not enough parameters"
-
-                - Returned by the server by numerous commands to
-                  indicate to the client that it didn't supply enough
-                  parameters.
-
-        462     ERR_ALREADYREGISTRED
-                        ":You may not reregister"
-
-                - Returned by the server to any link which tries to
-                  change part of the registered details (such as
-                  password or user details from second USER message).
-
-
-        463     ERR_NOPERMFORHOST
-                        ":Your host isn't among the privileged"
-
-                - Returned to a client which attempts to register with
-                  a server which does not been setup to allow
-                  connections from the host the attempted connection
-                  is tried.
-
-
-
-Oikarinen & Reed                                               [Page 46]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-        464     ERR_PASSWDMISMATCH
-                        ":Password incorrect"
-
-                - Returned to indicate a failed attempt at registering
-                  a connection for which a password was required and
-                  was either not given or incorrect.
-
-        465     ERR_YOUREBANNEDCREEP
-                        ":You are banned from this server"
-
-                - Returned after an attempt to connect and register
-                  yourself with a server which has been setup to
-                  explicitly deny connections to you.
-
-        467     ERR_KEYSET
-                        "<channel> :Channel key already set"
-        471     ERR_CHANNELISFULL
-                        "<channel> :Cannot join channel (+l)"
-        472     ERR_UNKNOWNMODE
-                        "<char> :is unknown mode char to me"
-        473     ERR_INVITEONLYCHAN
-                        "<channel> :Cannot join channel (+i)"
-        474     ERR_BANNEDFROMCHAN
-                        "<channel> :Cannot join channel (+b)"
-        475     ERR_BADCHANNELKEY
-                        "<channel> :Cannot join channel (+k)"
-        481     ERR_NOPRIVILEGES
-                        ":Permission Denied- You're not an IRC operator"
-
-                - Any command requiring operator privileges to operate
-                  must return this error to indicate the attempt was
-                  unsuccessful.
-
-        482     ERR_CHANOPRIVSNEEDED
-                        "<channel> :You're not channel operator"
-
-                - Any command requiring 'chanop' privileges (such as
-                  MODE messages) must return this error if the client
-                  making the attempt is not a chanop on the specified
-                  channel.
-
-        483     ERR_CANTKILLSERVER
-                        ":You cant kill a server!"
-
-                - Any attempts to use the KILL command on a server
-                  are to be refused and this error returned directly
-                  to the client.
-
-
-
-
-Oikarinen & Reed                                               [Page 47]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-        491     ERR_NOOPERHOST
-                        ":No O-lines for your host"
-
-                - If a client sends an OPER message and the server has
-                  not been configured to allow connections from the
-                  client's host as an operator, this error must be
-                  returned.
-
-        501     ERR_UMODEUNKNOWNFLAG
-                        ":Unknown MODE flag"
-
-                - Returned by the server to indicate that a MODE
-                  message was sent with a nickname parameter and that
-                  the a mode flag sent was not recognized.
-
-        502     ERR_USERSDONTMATCH
-                        ":Cant change mode for other users"
-
-                - Error sent to any user trying to view or change the
-                  user mode for a user other than themselves.
-
-6.2 Command responses.
-
-        300     RPL_NONE
-                        Dummy reply number. Not used.
-
-        302     RPL_USERHOST
-                        ":[<reply>{<space><reply>}]"
-
-                - Reply format used by USERHOST to list replies to
-                  the query list.  The reply string is composed as
-                  follows:
-
-                  <reply> ::= <nick>['*'] '=' <'+'|'-'><hostname>
-
-                  The '*' indicates whether the client has registered
-                  as an Operator.  The '-' or '+' characters represent
-                  whether the client has set an AWAY message or not
-                  respectively.
-
-        303     RPL_ISON
-                        ":[<nick> {<space><nick>}]"
-
-                - Reply format used by ISON to list replies to the
-                  query list.
-
-        301     RPL_AWAY
-                        "<nick> :<away message>"
-
-
-
-Oikarinen & Reed                                               [Page 48]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-        305     RPL_UNAWAY
-                        ":You are no longer marked as being away"
-        306     RPL_NOWAWAY
-                        ":You have been marked as being away"
-
-                - These replies are used with the AWAY command (if
-                  allowed).  RPL_AWAY is sent to any client sending a
-                  PRIVMSG to a client which is away.  RPL_AWAY is only
-                  sent by the server to which the client is connected.
-                  Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the
-                  client removes and sets an AWAY message.
-
-        311     RPL_WHOISUSER
-                        "<nick> <user> <host> * :<real name>"
-        312     RPL_WHOISSERVER
-                        "<nick> <server> :<server info>"
-        313     RPL_WHOISOPERATOR
-                        "<nick> :is an IRC operator"
-        317     RPL_WHOISIDLE
-                        "<nick> <integer> :seconds idle"
-        318     RPL_ENDOFWHOIS
-                        "<nick> :End of /WHOIS list"
-        319     RPL_WHOISCHANNELS
-                        "<nick> :{[@|+]<channel><space>}"
-
-                - Replies 311 - 313, 317 - 319 are all replies
-                  generated in response to a WHOIS message.  Given that
-                  there are enough parameters present, the answering
-                  server must either formulate a reply out of the above
-                  numerics (if the query nick is found) or return an
-                  error reply.  The '*' in RPL_WHOISUSER is there as
-                  the literal character and not as a wild card.  For
-                  each reply set, only RPL_WHOISCHANNELS may appear
-                  more than once (for long lists of channel names).
-                  The '@' and '+' characters next to the channel name
-                  indicate whether a client is a channel operator or
-                  has been granted permission to speak on a moderated
-                  channel.  The RPL_ENDOFWHOIS reply is used to mark
-                  the end of processing a WHOIS message.
-
-        314     RPL_WHOWASUSER
-                        "<nick> <user> <host> * :<real name>"
-        369     RPL_ENDOFWHOWAS
-                        "<nick> :End of WHOWAS"
-
-                - When replying to a WHOWAS message, a server must use
-                  the replies RPL_WHOWASUSER, RPL_WHOISSERVER or
-                  ERR_WASNOSUCHNICK for each nickname in the presented
-
-
-
-Oikarinen & Reed                                               [Page 49]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                  list.  At the end of all reply batches, there must
-                  be RPL_ENDOFWHOWAS (even if there was only one reply
-                  and it was an error).
-
-        321     RPL_LISTSTART
-                        "Channel :Users  Name"
-        322     RPL_LIST
-                        "<channel> <# visible> :<topic>"
-        323     RPL_LISTEND
-                        ":End of /LIST"
-
-                - Replies RPL_LISTSTART, RPL_LIST, RPL_LISTEND mark
-                  the start, actual replies with data and end of the
-                  server's response to a LIST command.  If there are
-                  no channels available to return, only the start
-                  and end reply must be sent.
-
-        324     RPL_CHANNELMODEIS
-                        "<channel> <mode> <mode params>"
-
-        331     RPL_NOTOPIC
-                        "<channel> :No topic is set"
-        332     RPL_TOPIC
-                        "<channel> :<topic>"
-
-                - When sending a TOPIC message to determine the
-                  channel topic, one of two replies is sent.  If
-                  the topic is set, RPL_TOPIC is sent back else
-                  RPL_NOTOPIC.
-
-        341     RPL_INVITING
-                        "<channel> <nick>"
-
-                - Returned by the server to indicate that the
-                  attempted INVITE message was successful and is
-                  being passed onto the end client.
-
-        342     RPL_SUMMONING
-                        "<user> :Summoning user to IRC"
-
-                - Returned by a server answering a SUMMON message to
-                  indicate that it is summoning that user.
-
-        351     RPL_VERSION
-                        "<version>.<debuglevel> <server> :<comments>"
-
-                - Reply by the server showing its version details.
-                  The <version> is the version of the software being
-
-
-
-Oikarinen & Reed                                               [Page 50]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                  used (including any patchlevel revisions) and the
-                  <debuglevel> is used to indicate if the server is
-                  running in "debug mode".
-
-                  The "comments" field may contain any comments about
-                  the version or further version details.
-
-        352     RPL_WHOREPLY
-                        "<channel> <user> <host> <server> <nick> \
-                         <H|G>[*][@|+] :<hopcount> <real name>"
-        315     RPL_ENDOFWHO
-                        "<name> :End of /WHO list"
-
-                - The RPL_WHOREPLY and RPL_ENDOFWHO pair are used
-                  to answer a WHO message.  The RPL_WHOREPLY is only
-                  sent if there is an appropriate match to the WHO
-                  query.  If there is a list of parameters supplied
-                  with a WHO message, a RPL_ENDOFWHO must be sent
-                  after processing each list item with <name> being
-                  the item.
-
-        353     RPL_NAMREPLY
-                        "<channel> :[[@|+]<nick> [[@|+]<nick> [...]]]"
-        366     RPL_ENDOFNAMES
-                        "<channel> :End of /NAMES list"
-
-                - To reply to a NAMES message, a reply pair consisting
-                  of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the
-                  server back to the client.  If there is no channel
-                  found as in the query, then only RPL_ENDOFNAMES is
-                  returned.  The exception to this is when a NAMES
-                  message is sent with no parameters and all visible
-                  channels and contents are sent back in a series of
-                  RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark
-                  the end.
-
-        364     RPL_LINKS
-                        "<mask> <server> :<hopcount> <server info>"
-        365     RPL_ENDOFLINKS
-                        "<mask> :End of /LINKS list"
-
-                - In replying to the LINKS message, a server must send
-                  replies back using the RPL_LINKS numeric and mark the
-                  end of the list using an RPL_ENDOFLINKS reply.
-
-        367     RPL_BANLIST
-                        "<channel> <banid>"
-        368     RPL_ENDOFBANLIST
-
-
-
-Oikarinen & Reed                                               [Page 51]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                        "<channel> :End of channel ban list"
-
-                - When listing the active 'bans' for a given channel,
-                  a server is required to send the list back using the
-                  RPL_BANLIST and RPL_ENDOFBANLIST messages.  A separate
-                  RPL_BANLIST is sent for each active banid.  After the
-                  banids have been listed (or if none present) a
-                  RPL_ENDOFBANLIST must be sent.
-
-        371     RPL_INFO
-                        ":<string>"
-        374     RPL_ENDOFINFO
-                        ":End of /INFO list"
-
-                - A server responding to an INFO message is required to
-                  send all its 'info' in a series of RPL_INFO messages
-                  with a RPL_ENDOFINFO reply to indicate the end of the
-                  replies.
-
-        375     RPL_MOTDSTART
-                        ":- <server> Message of the day - "
-        372     RPL_MOTD
-                        ":- <text>"
-        376     RPL_ENDOFMOTD
-                        ":End of /MOTD command"
-
-                - When responding to the MOTD message and the MOTD file
-                  is found, the file is displayed line by line, with
-                  each line no longer than 80 characters, using
-                  RPL_MOTD format replies.  These should be surrounded
-                  by a RPL_MOTDSTART (before the RPL_MOTDs) and an
-                  RPL_ENDOFMOTD (after).
-
-        381     RPL_YOUREOPER
-                        ":You are now an IRC operator"
-
-                - RPL_YOUREOPER is sent back to a client which has
-                  just successfully issued an OPER message and gained
-                  operator status.
-
-        382     RPL_REHASHING
-                        "<config file> :Rehashing"
-
-                - If the REHASH option is used and an operator sends
-                  a REHASH message, an RPL_REHASHING is sent back to
-                  the operator.
-
-        391     RPL_TIME
-
-
-
-Oikarinen & Reed                                               [Page 52]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                        "<server> :<string showing server's local time>"
-
-                - When replying to the TIME message, a server must send
-                  the reply using the RPL_TIME format above.  The string
-                  showing the time need only contain the correct day and
-                  time there.  There is no further requirement for the
-                  time string.
-
-        392     RPL_USERSSTART
-                        ":UserID   Terminal  Host"
-        393     RPL_USERS
-                        ":%-8s %-9s %-8s"
-        394     RPL_ENDOFUSERS
-                        ":End of users"
-        395     RPL_NOUSERS
-                        ":Nobody logged in"
-
-                - If the USERS message is handled by a server, the
-                  replies RPL_USERSTART, RPL_USERS, RPL_ENDOFUSERS and
-                  RPL_NOUSERS are used.  RPL_USERSSTART must be sent
-                  first, following by either a sequence of RPL_USERS
-                  or a single RPL_NOUSER.  Following this is
-                  RPL_ENDOFUSERS.
-
-        200     RPL_TRACELINK
-                        "Link <version & debug level> <destination> \
-                         <next server>"
-        201     RPL_TRACECONNECTING
-                        "Try. <class> <server>"
-        202     RPL_TRACEHANDSHAKE
-                        "H.S. <class> <server>"
-        203     RPL_TRACEUNKNOWN
-                        "???? <class> [<client IP address in dot form>]"
-        204     RPL_TRACEOPERATOR
-                        "Oper <class> <nick>"
-        205     RPL_TRACEUSER
-                        "User <class> <nick>"
-        206     RPL_TRACESERVER
-                        "Serv <class> <int>S <int>C <server> \
-                         <nick!user|*!*>@<host|server>"
-        208     RPL_TRACENEWTYPE
-                        "<newtype> 0 <client name>"
-        261     RPL_TRACELOG
-                        "File <logfile> <debug level>"
-
-                - The RPL_TRACE* are all returned by the server in
-                  response to the TRACE message.  How many are
-                  returned is dependent on the the TRACE message and
-
-
-
-Oikarinen & Reed                                               [Page 53]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                  whether it was sent by an operator or not.  There
-                  is no predefined order for which occurs first.
-                  Replies RPL_TRACEUNKNOWN, RPL_TRACECONNECTING and
-                  RPL_TRACEHANDSHAKE are all used for connections
-                  which have not been fully established and are either
-                  unknown, still attempting to connect or in the
-                  process of completing the 'server handshake'.
-                  RPL_TRACELINK is sent by any server which handles
-                  a TRACE message and has to pass it on to another
-                  server.  The list of RPL_TRACELINKs sent in
-                  response to a TRACE command traversing the IRC
-                  network should reflect the actual connectivity of
-                  the servers themselves along that path.
-                  RPL_TRACENEWTYPE is to be used for any connection
-                  which does not fit in the other categories but is
-                  being displayed anyway.
-
-        211     RPL_STATSLINKINFO
-                        "<linkname> <sendq> <sent messages> \
-                         <sent bytes> <received messages> \
-                         <received bytes> <time open>"
-        212     RPL_STATSCOMMANDS
-                        "<command> <count>"
-        213     RPL_STATSCLINE
-                        "C <host> * <name> <port> <class>"
-        214     RPL_STATSNLINE
-                        "N <host> * <name> <port> <class>"
-        215     RPL_STATSILINE
-                        "I <host> * <host> <port> <class>"
-        216     RPL_STATSKLINE
-                        "K <host> * <username> <port> <class>"
-        218     RPL_STATSYLINE
-                        "Y <class> <ping frequency> <connect \
-                         frequency> <max sendq>"
-        219     RPL_ENDOFSTATS
-                        "<stats letter> :End of /STATS report"
-        241     RPL_STATSLLINE
-                        "L <hostmask> * <servername> <maxdepth>"
-        242     RPL_STATSUPTIME
-                        ":Server Up %d days %d:%02d:%02d"
-        243     RPL_STATSOLINE
-                        "O <hostmask> * <name>"
-        244     RPL_STATSHLINE
-                        "H <hostmask> * <servername>"
-
-        221     RPL_UMODEIS
-                        "<user mode string>"
-
-
-
-
-Oikarinen & Reed                                               [Page 54]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-                        - To answer a query about a client's own mode,
-                          RPL_UMODEIS is sent back.
-
-        251     RPL_LUSERCLIENT
-                        ":There are <integer> users and <integer> \
-                         invisible on <integer> servers"
-        252     RPL_LUSEROP
-                        "<integer> :operator(s) online"
-        253     RPL_LUSERUNKNOWN
-                        "<integer> :unknown connection(s)"
-        254     RPL_LUSERCHANNELS
-                        "<integer> :channels formed"
-        255     RPL_LUSERME
-                        ":I have <integer> clients and <integer> \
-                          servers"
-
-                        - In processing an LUSERS message, the server
-                          sends a set of replies from RPL_LUSERCLIENT,
-                          RPL_LUSEROP, RPL_USERUNKNOWN,
-                          RPL_LUSERCHANNELS and RPL_LUSERME.  When
-                          replying, a server must send back
-                          RPL_LUSERCLIENT and RPL_LUSERME.  The other
-                          replies are only sent back if a non-zero count
-                          is found for them.
-
-        256     RPL_ADMINME
-                        "<server> :Administrative info"
-        257     RPL_ADMINLOC1
-                        ":<admin info>"
-        258     RPL_ADMINLOC2
-                        ":<admin info>"
-        259     RPL_ADMINEMAIL
-                        ":<admin info>"
-
-                        - When replying to an ADMIN message, a server
-                          is expected to use replies RLP_ADMINME
-                          through to RPL_ADMINEMAIL and provide a text
-                          message with each.  For RPL_ADMINLOC1 a
-                          description of what city, state and country
-                          the server is in is expected, followed by
-                          details of the university and department
-                          (RPL_ADMINLOC2) and finally the administrative
-                          contact for the server (an email address here
-                          is required) in RPL_ADMINEMAIL.
-
-
-
-
-
-
-
-Oikarinen & Reed                                               [Page 55]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-6.3 Reserved numerics.
-
-   These numerics are not described above since they fall into one of
-   the following categories:
-
-        1. no longer in use;
-
-        2. reserved for future planned use;
-
-        3. in current use but are part of a non-generic 'feature' of
-           the current IRC server.
-
-        209     RPL_TRACECLASS          217     RPL_STATSQLINE
-        231     RPL_SERVICEINFO         232     RPL_ENDOFSERVICES
-        233     RPL_SERVICE             234     RPL_SERVLIST
-        235     RPL_SERVLISTEND
-        316     RPL_WHOISCHANOP         361     RPL_KILLDONE
-        362     RPL_CLOSING             363     RPL_CLOSEEND
-        373     RPL_INFOSTART           384     RPL_MYPORTIS
-        466     ERR_YOUWILLBEBANNED     476     ERR_BADCHANMASK
-        492     ERR_NOSERVICEHOST
-
-7. Client and server authentication
-
-   Clients and servers are both subject to the same level of
-   authentication.  For both, an IP number to hostname lookup (and
-   reverse check on this) is performed for all connections made to the
-   server.  Both connections are then subject to a password check (if
-   there is a password set for that connection).  These checks are
-   possible on all connections although the password check is only
-   commonly used with servers.
-
-   An additional check that is becoming of more and more common is that
-   of the username responsible for making the connection.  Finding the
-   username of the other end of the connection typically involves
-   connecting to an authentication server such as IDENT as described in
-   RFC 1413.
-
-   Given that without passwords it is not easy to reliably determine who
-   is on the other end of a network connection, use of passwords is
-   strongly recommended on inter-server connections in addition to any
-   other measures such as using an ident server.
-
-8. Current implementations
-
-   The only current implementation of this protocol is the IRC server,
-   version 2.8. Earlier versions may implement some or all of the
-   commands described by this document with NOTICE messages replacing
-
-
-
-Oikarinen & Reed                                               [Page 56]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   many of the numeric replies.  Unfortunately, due to backward
-   compatibility requirements, the implementation of some parts of this
-   document varies with what is laid out.  On notable difference is:
-
-        * recognition that any LF or CR anywhere in a message marks the
-          end of that message (instead of requiring CR-LF);
-
-   The rest of this section deals with issues that are mostly of
-   importance to those who wish to implement a server but some parts
-   also apply directly to clients as well.
-
-8.1 Network protocol: TCP - why it is best used here.
-
-   IRC has been implemented on top of TCP since TCP supplies a reliable
-   network protocol which is well suited to this scale of conferencing.
-   The use of multicast IP is an alternative, but it is not widely
-   available or supported at the present time.
-
-8.1.1 Support of Unix sockets
-
-   Given that Unix domain sockets allow listen/connect operations, the
-   current implementation can be configured to listen and accept both
-   client and server connections on a Unix domain socket.  These are
-   recognized as sockets where the hostname starts with a '/'.
-
-   When providing any information about the connections on a Unix domain
-   socket, the server is required to supplant the actual hostname in
-   place of the pathname unless the actual socket name is being asked
-   for.
-
-8.2 Command Parsing
-
-   To provide useful 'non-buffered' network IO for clients and servers,
-   each connection is given its own private 'input buffer' in which the
-   results of the most recent read and parsing are kept.  A buffer size
-   of 512 bytes is used so as to hold 1 full message, although, this
-   will usually hold several commands.  The private buffer is parsed
-   after every read operation for valid messages.  When dealing with
-   multiple messages from one client in the buffer, care should be taken
-   in case one happens to cause the client to be 'removed'.
-
-8.3 Message delivery
-
-   It is common to find network links saturated or hosts to which you
-   are sending data unable to send data.  Although Unix typically
-   handles this through the TCP window and internal buffers, the server
-   often has large amounts of data to send (especially when a new
-   server-server link forms) and the small buffers provided in the
-
-
-
-Oikarinen & Reed                                               [Page 57]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   kernel are not enough for the outgoing queue.  To alleviate this
-   problem, a "send queue" is used as a FIFO queue for data to be sent.
-   A typical "send queue" may grow to 200 Kbytes on a large IRC network
-   with a slow network connection when a new server connects.
-
-   When polling its connections, a server will first read and parse all
-   incoming data, queuing any data to be sent out. When all available
-   input is processed, the queued data is sent. This reduces the number
-   of write() system calls and helps TCP make bigger packets.
-
-8.4 Connection 'Liveness'
-
-   To detect when a connection has died or become unresponsive, the
-   server must ping each of its connections that it doesn't get a
-   response from in a given amount of time.
-
-   If a connection doesn't respond in time, its connection is closed
-   using the appropriate procedures.  A connection is also dropped if
-   its sendq grows beyond the maximum allowed, because it is better to
-   close a slow connection than have a server process block.
-
-8.5 Establishing a server to client connection
-
-   Upon connecting to an IRC server, a client is sent the MOTD (if
-   present) as well as the current user/server count (as per the LUSER
-   command).  The server is also required to give an unambiguous message
-   to the client which states its name and version as well as any other
-   introductory messages which may be deemed appropriate.
-
-   After dealing with this, the server must then send out the new user's
-   nickname and other information as supplied by itself (USER command)
-   and as the server could discover (from DNS/authentication servers).
-   The server must send this information out with NICK first followed by
-   USER.
-
-8.6 Establishing a server-server connection.
-
-   The process of establishing of a server-to-server connection is
-   fraught with danger since there are many possible areas where
-   problems can occur - the least of which are race conditions.
-
-   After a server has received a connection following by a PASS/SERVER
-   pair which were recognised as being valid, the server should then
-   reply with its own PASS/SERVER information for that connection as
-   well as all of the other state information it knows about as
-   described below.
-
-   When the initiating server receives a PASS/SERVER pair, it too then
-
-
-
-Oikarinen & Reed                                               [Page 58]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   checks that the server responding is authenticated properly before
-   accepting the connection to be that server.
-
-8.6.1 Server exchange of state information when connecting
-
-   The order of state information being exchanged between servers is
-   essential.  The required order is as follows:
-
-        * all known other servers;
-
-        * all known user information;
-
-        * all known channel information.
-
-   Information regarding servers is sent via extra SERVER messages, user
-   information with NICK/USER/MODE/JOIN messages and channels with MODE
-   messages.
-
-   NOTE: channel topics are *NOT* exchanged here because the TOPIC
-   command overwrites any old topic information, so at best, the two
-   sides of the connection would exchange topics.
-
-   By passing the state information about servers first, any collisions
-   with servers that already exist occur before nickname collisions due
-   to a second server introducing a particular nickname.  Due to the IRC
-   network only being able to exist as an acyclic graph, it may be
-   possible that the network has already reconnected in another
-   location, the place where the collision occurs indicating where the
-   net needs to split.
-
-8.7 Terminating server-client connections
-
-   When a client connection closes, a QUIT message is generated on
-   behalf of the client by the server to which the client connected.  No
-   other message is to be generated or used.
-
-8.8 Terminating server-server connections
-
-   If a server-server connection is closed, either via a remotely
-   generated SQUIT or 'natural' causes, the rest of the connected IRC
-   network must have its information updated with by the server which
-   detected the closure.  The server then sends a list of SQUITs (one
-   for each server behind that connection) and a list of QUITs (again,
-   one for each client behind that connection).
-
-
-
-
-
-
-
-Oikarinen & Reed                                               [Page 59]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-8.9 Tracking nickname changes
-
-   All IRC servers are required to keep a history of recent nickname
-   changes.  This is required to allow the server to have a chance of
-   keeping in touch of things when nick-change race conditions occur
-   with commands which manipulate them.  Commands which must trace nick
-   changes are:
-
-        * KILL (the nick being killed)
-
-        * MODE (+/- o,v)
-
-        * KICK (the nick being kicked)
-
-   No other commands are to have nick changes checked for.
-
-   In the above cases, the server is required to first check for the
-   existence of the nickname, then check its history to see who that
-   nick currently belongs to (if anyone!).  This reduces the chances of
-   race conditions but they can still occur with the server ending up
-   affecting the wrong client.  When performing a change trace for an
-   above command it is recommended that a time range be given and
-   entries which are too old ignored.
-
-   For a reasonable history, a server should be able to keep previous
-   nickname for every client it knows about if they all decided to
-   change.  This size is limited by other factors (such as memory, etc).
-
-8.10 Flood control of clients
-
-   With a large network of interconnected IRC servers, it is quite easy
-   for any single client attached to the network to supply a continuous
-   stream of messages that result in not only flooding the network, but
-   also degrading the level of service provided to others.  Rather than
-   require every 'victim' to be provide their own protection, flood
-   protection was written into the server and is applied to all clients
-   except services.  The current algorithm is as follows:
-
-        * check to see if client's `message timer' is less than
-          current time (set to be equal if it is);
-
-        * read any data present from the client;
-
-        * while the timer is less than ten seconds ahead of the current
-          time, parse any present messages and penalize the client by
-          2 seconds for each message;
-
-   which in essence means that the client may send 1 message every 2
-
-
-
-Oikarinen & Reed                                               [Page 60]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   seconds without being adversely affected.
-
-8.11 Non-blocking lookups
-
-   In a real-time environment, it is essential that a server process do
-   as little waiting as possible so that all the clients are serviced
-   fairly.  Obviously this requires non-blocking IO on all network
-   read/write operations.  For normal server connections, this was not
-   difficult, but there are other support operations that may cause the
-   server to block (such as disk reads).  Where possible, such activity
-   should be performed with a short timeout.
-
-8.11.1 Hostname (DNS) lookups
-
-   Using the standard resolver libraries from Berkeley and others has
-   meant large delays in some cases where replies have timed out.  To
-   avoid this, a separate set of DNS routines were written which were
-   setup for non-blocking IO operations and then polled from within the
-   main server IO loop.
-
-8.11.2 Username (Ident) lookups
-
-   Although there are numerous ident libraries for use and inclusion
-   into other programs, these caused problems since they operated in a
-   synchronous manner and resulted in frequent delays.  Again the
-   solution was to write a set of routines which would cooperate with
-   the rest of the server and work using non-blocking IO.
-
-8.12 Configuration File
-
-   To provide a flexible way of setting up and running the server, it is
-   recommended that a configuration file be used which contains
-   instructions to the server on the following:
-
-        * which hosts to accept client connections from;
-
-        * which hosts to allow to connect as servers;
-
-        * which hosts to connect to (both actively and
-          passively);
-
-        * information about where the server is (university,
-          city/state, company are examples of this);
-
-        * who is responsible for the server and an email address
-          at which they can be contacted;
-
-        * hostnames and passwords for clients which wish to be given
-
-
-
-Oikarinen & Reed                                               [Page 61]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-          access to restricted operator commands.
-
-   In specifying hostnames, both domain names and use of the 'dot'
-   notation (127.0.0.1) should both be accepted.  It must be possible to
-   specify the password to be used/accepted for all outgoing and
-   incoming connections (although the only outgoing connections are
-   those to other servers).
-
-   The above list is the minimum requirement for any server which wishes
-   to make a connection with another server.  Other items which may be
-   of use are:
-
-        * specifying which servers other server may introduce;
-
-        * how deep a server branch is allowed to become;
-
-        * hours during which clients may connect.
-
-8.12.1 Allowing clients to connect
-
-   A server should use some sort of 'access control list' (either in the
-   configuration file or elsewhere) that is read at startup and used to
-   decide what hosts clients may use to connect to it.
-
-   Both 'deny' and 'allow' should be implemented to provide the required
-   flexibility for host access control.
-
-8.12.2 Operators
-
-   The granting of operator privileges to a disruptive person can have
-   dire consequences for the well-being of the IRC net in general due to
-   the powers given to them.  Thus, the acquisition of such powers
-   should not be very easy.  The current setup requires two 'passwords'
-   to be used although one of them is usually easy guessed.  Storage of
-   oper passwords in configuration files is preferable to hard coding
-   them in and should be stored in a crypted format (ie using crypt(3)
-   from Unix) to prevent easy theft.
-
-8.12.3 Allowing servers to connect
-
-   The interconnection of server is not a trivial matter: a bad
-   connection can have a large impact on the usefulness of IRC.  Thus,
-   each server should have a list of servers to which it may connect and
-   which servers may connect to it.  Under no circumstances should a
-   server allow an arbitrary host to connect as a server.  In addition
-   to which servers may and may not connect, the configuration file
-   should also store the password and other characteristics of that
-   link.
-
-
-
-Oikarinen & Reed                                               [Page 62]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-8.12.4 Administrivia
-
-   To provide accurate and valid replies to the ADMIN command (see
-   section 4.3.7), the server should find the relevant details in the
-   configuration.
-
-8.13 Channel membership
-
-   The current server allows any registered local user to join upto 10
-   different channels.  There is no limit imposed on non-local users so
-   that the server remains (reasonably) consistant with all others on a
-   channel membership basis
-
-9. Current problems
-
-   There are a number of recognized problems with this protocol, all  of
-   which  hope to be solved sometime in the near future during its
-   rewrite.  Currently, work is underway to find working solutions to
-   these problems.
-
-9.1 Scalability
-
-   It is widely recognized that this protocol does not scale
-   sufficiently well when used in a large arena.  The main problem comes
-   from the requirement that all servers know about all other servers
-   and users and that information regarding them be updated as soon as
-   it changes.  It is also desirable to keep the number of servers low
-   so that the path length between any two points is kept minimal and
-   the spanning tree as strongly branched as possible.
-
-9.2 Labels
-
-   The current IRC protocol has 3 types of labels: the nickname, the
-   channel name and the server name.  Each of the three types has its
-   own domain and no duplicates are allowed inside that domain.
-   Currently, it is possible for users to pick the label for any of the
-   three, resulting in collisions.  It is widely recognized that this
-   needs reworking, with a plan for unique names for channels and nicks
-   that don't collide being desirable as well as a solution allowing a
-   cyclic tree.
-
-9.2.1 Nicknames
-
-   The idea of the nickname on IRC is very convenient for users to use
-   when talking to each other outside of a channel, but there is only a
-   finite nickname space and being what they are, its not uncommon for
-   several people to want to use the same nick.  If a nickname is chosen
-   by two people using this protocol, either one will not succeed or
-
-
-
-Oikarinen & Reed                                               [Page 63]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-   both will removed by use of KILL (4.6.1).
-
-9.2.2 Channels
-
-   The current channel layout requires that all servers know about all
-   channels, their inhabitants and properties.  Besides not scaling
-   well, the issue of privacy is also a concern.  A collision of
-   channels is treated as an inclusive event (both people who create the
-   new channel are considered to be members of it) rather than an
-   exclusive one such as used to solve nickname collisions.
-
-9.2.3 Servers
-
-   Although the number of servers is usually small relative to the
-   number of users and channels, they two currently required to be known
-   globally, either each one separately or hidden behind a mask.
-
-9.3 Algorithms
-
-   In some places within the server code, it has not  been  possible  to
-   avoid  N^2  algorithms  such  as  checking  the channel list of a set
-   of clients.
-
-   In current server versions, there are no database consistency checks,
-   each server assumes that a neighbouring server is correct.  This
-   opens the door to large problems if a connecting server is buggy or
-   otherwise tries to introduce contradictions to the existing net.
-
-   Currently, because of the lack of unique internal and global labels,
-   there are a multitude of race conditions that exist.  These race
-   conditions generally arise from the problem of it taking time for
-   messages to traverse and effect the IRC network.  Even by changing to
-   unique labels, there are problems with channel-related commands being
-   disrupted.
-
-10. Current support and availability
-
-           Mailing lists for IRC related discussion:
-                Future protocol: ircd-three-request@eff.org
-                General discussion: operlist-request@eff.org
-
-           Software implemenations
-                cs.bu.edu:/irc
-                nic.funet.fi:/pub/irc
-                coombs.anu.edu.au:/pub/irc
-
-           Newsgroup: alt.irc
-
-
-
-
-Oikarinen & Reed                                               [Page 64]
-\f
-RFC 1459              Internet Relay Chat Protocol              May 1993
-
-
-Security Considerations
-
-   Security issues are discussed in sections 4.1, 4.1.1, 4.1.3, 5.5, and
-   7.
-
-12. Authors' Addresses
-
-   Jarkko Oikarinen
-   Tuirantie 17 as 9
-   90500 OULU
-   FINLAND
-
-   Email: jto@tolsun.oulu.fi
-
-
-   Darren Reed
-   4 Pateman Street
-   Watsonia, Victoria 3087
-   Australia
-
-   Email: avalon@coombs.anu.edu.au
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Oikarinen & Reed                                               [Page 65]
-\f
\ No newline at end of file
diff --git a/docs/sql/sqloper.mysql.sql b/docs/sql/sqloper.mysql.sql
new file mode 100644 (file)
index 0000000..a8a2b7e
--- /dev/null
@@ -0,0 +1,12 @@
+CREATE TABLE ircd_opers (
+  id bigint(20) NOT NULL auto_increment,
+  name text NOT NULL,
+  password text NOT NULL,
+  hash text,
+  host text NOT NULL,
+  type text NOT NULL,
+  fingerprint text,
+  autologin tinyint(1) NOT NULL DEFAULT 0,
+  active tinyint(1) NOT NULL DEFAULT 1,
+  PRIMARY KEY  (id)
+) ENGINE=MyISAM;
diff --git a/docs/sql/sqloper.pgsql.sql b/docs/sql/sqloper.pgsql.sql
new file mode 100644 (file)
index 0000000..954bc84
--- /dev/null
@@ -0,0 +1,13 @@
+CREATE TABLE ircd_opers (
+    "id" serial NOT NULL,
+    "name" text NOT NULL,
+    "password" text NOT NULL,
+    "hash" text,
+    "host" text NOT NULL,
+    "type" text NOT NULL,
+    "fingerprint" text,
+    "autologin" smallint NOT NULL DEFAULT 0,
+    "active" smallint NOT NULL DEFAULT 1
+);
+ALTER TABLE ONLY ircd_opers
+    ADD CONSTRAINT ircd_opers_pkey PRIMARY KEY (id);
diff --git a/docs/sql/sqloper.sqlite3.sql b/docs/sql/sqloper.sqlite3.sql
new file mode 100644 (file)
index 0000000..6aec5a1
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE TABLE ircd_opers (
+id integer primary key,
+name text NOT NULL,
+password text NOT NULL,
+hash text,
+host text NOT NULL,
+type text NOT NULL,
+fingerprint text,
+autologin integer NOT NULL DEFAULT 0,
+active integer NOT NULL DEFAULT 1);
diff --git a/extras/m_sqloper.mssql.sql b/extras/m_sqloper.mssql.sql
deleted file mode 100644 (file)
index 5056e12..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-CREATE TABLE [dbo].[ircd_opers] (\r
-  [id] int IDENTITY(1, 1) NOT NULL,\r
-  [username] varchar(255) NULL,\r
-  [password] varchar(255) NULL,\r
-  [hostname] varchar(255) NULL,\r
-  [type] varchar(255) NULL,\r
-  PRIMARY KEY CLUSTERED ([id])\r
-)\r
diff --git a/extras/m_sqloper.mysql.sql b/extras/m_sqloper.mysql.sql
deleted file mode 100644 (file)
index 293a2aa..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
--- 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,
-  PRIMARY KEY  (id)
-) TYPE=MyISAM;
-
---
--- Dumping data for table `ircd_opers`
---
-
-
diff --git a/extras/m_sqloper.postgresql.sql b/extras/m_sqloper.postgresql.sql
deleted file mode 100644 (file)
index fd64094..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
---
--- PostgreSQL database dump
---
-
-CREATE TABLE ircd_opers (
-    id serial NOT NULL,
-    username text,
-    "password" text,
-    hostname text,
-    "type" text
-);
-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
deleted file mode 100644 (file)
index 1bb2937..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-CREATE TABLE ircd_opers (
-id integer primary key,
-username text,
-password text,
-hostname text,
-type text);
-
diff --git a/include/aligned_storage.h b/include/aligned_storage.h
new file mode 100644 (file)
index 0000000..7bf0fe0
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 insp
+{
+       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->();
+       }
+};
index a7aac7f17c8ed0e72beef72f4562b74243a6ea76..e0c84ab5487ba0c69621df3b2802f2706a0ee768 100644 (file)
@@ -18,8 +18,7 @@
  */
 
 
-#ifndef BANCACHE_H
-#define BANCACHE_H
+#pragma once
 
 /** Stores a cached ban entry.
  * Each ban has one of these hashed in a hash_map to make for faster removal
@@ -37,68 +36,42 @@ class CoreExport BanCacheHit
        /** Reason, shown as quit message
         */
        std::string Reason;
-       /** IP to match against, no wildcards here (of course)
-        */
-       std::string IP;
        /** Time that the ban expires at
         */
        time_t Expiry;
 
-       BanCacheHit(const std::string &ip, const std::string &type, const std::string &reason)
-       {
-               this->Type = type;
-               this->Reason = reason;
-               this->IP = ip;
-               this->Expiry = ServerInstance->Time() + 86400; // a day. this might seem long, but entries will be removed as glines/etc expire.
-       }
+       BanCacheHit(const std::string& type, const std::string& reason, time_t seconds);
 
-       // overridden to allow custom time
-       BanCacheHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds)
-       {
-               this->Type = type;
-               this->Reason = reason;
-               this->IP = ip;
-               this->Expiry = ServerInstance->Time() + seconds;
-       }
+       bool IsPositive() const { return (!Reason.empty()); }
 };
 
-/* A container of ban cache items.
- * must be defined after class BanCacheHit.
- */
-typedef nspace::hash_map<std::string, BanCacheHit*, nspace::hash<std::string> > BanCacheHash;
-
 /** A manager for ban cache, which allocates and deallocates and checks cached bans.
  */
 class CoreExport BanCacheManager
 {
- private:
-       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:
 
        /** Creates and adds a Ban Cache item.
         * @param ip The IP the item is for.
         * @param type The type of ban cache item. std::string. .empty() means it's a negative match (user is allowed freely).
         * @param reason The reason for the ban. Left .empty() if it's a negative match.
+        * @param seconds Number of seconds before nuking the bancache entry, the default is a day. This might seem long, but entries will be removed as G-lines/etc expire.
         */
-       BanCacheHit *AddHit(const std::string &ip, const std::string &type, const std::string &reason);
-
-       // Overridden to allow an optional number of seconds before expiry
-       BanCacheHit *AddHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds);
+       BanCacheHit *AddHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds = 0);
        BanCacheHit *GetHit(const std::string &ip);
-       bool RemoveHit(BanCacheHit *b);
 
        /** Removes all entries of a given type, either positive or negative. Returns the number of hits removed.
         * @param type The type of bancache entries to remove (e.g. 'G')
         * @param positive Remove either positive (true) or negative (false) hits.
         */
-       unsigned int RemoveEntries(const std::string &type, bool positive);
+       void RemoveEntries(const std::string& type, bool positive);
 
-       BanCacheManager()
-       {
-               this->BanHash = new BanCacheHash();
-       }
        ~BanCacheManager();
-       void RehashCache();
 };
-
-#endif
index 19222a6f5d2ac4ac592ae84797c7044c31d7a1a1..d8781f7969a1f21f3b8d61b6eecff846cf2204e0 100644 (file)
@@ -20,8 +20,7 @@
  */
 
 
-#ifndef BASE_H
-#define BASE_H
+#pragma once
 
 #include <map>
 #include <deque>
@@ -180,21 +179,23 @@ class reference
  */
 class CoreExport CoreException : public std::exception
 {
- public:
+ protected:
        /** Holds the error message to be displayed
         */
        const std::string err;
        /** Source of the exception
         */
        const std::string source;
-       /** Default constructor, just uses the error mesage 'Core threw an exception'.
-        */
-       CoreException() : err("Core threw an exception"), source("The core") {}
+
+ public:
        /** This constructor can be used to specify an error message before throwing.
+        * @param message Human readable error message
         */
        CoreException(const std::string &message) : err(message), source("The core") {}
        /** This constructor can be used to specify an error message before throwing,
         * and to specify the source of the exception.
+        * @param message Human readable error message
+        * @param src Source of the exception
         */
        CoreException(const std::string &message, const std::string &src) : err(message), source(src) {}
        /** This destructor solves world hunger, cancels the world debt, and causes the world to end.
@@ -203,17 +204,14 @@ class CoreExport CoreException : public std::exception
         */
        virtual ~CoreException() throw() {}
        /** Returns the reason for the exception.
-        * The module should probably put something informative here as the user will see this upon failure.
+        * @return Human readable description of the error
         */
-       virtual const char* GetReason()
-       {
-               return err.c_str();
-       }
+       const std::string& GetReason() const { return err; }
 
-       virtual const char* GetSource()
-       {
-               return source.c_str();
-       }
+       /** Returns the source of the exception
+        * @return Source of the exception
+        */
+       const std::string& GetSource() const { return source; }
 };
 
 class Module;
@@ -237,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 */
@@ -250,10 +250,14 @@ class CoreExport ServiceProvider : public classbase
        const std::string name;
        /** Type of service (must match object type) */
        const ServiceType service;
-       ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type)
-               : creator(Creator), name(Name), service(Type) {}
+       ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type);
        virtual ~ServiceProvider();
-};
 
+       /** Register this service in the appropriate registrar
+        */
+       virtual void RegisterService();
 
-#endif
+       /** If called, this ServiceProvider won't be registered automatically
+        */
+       void DisableAutoRegister();
+};
diff --git a/include/caller.h b/include/caller.h
deleted file mode 100644 (file)
index 4057477..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 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/>.
- */
-
-
-#ifndef CALLER_H
-#define CALLER_H
-
-/** The templates below can be auto generated by tools/create_templates.pl.
- * They are used to represent a functor with a given number of parameters and
- * a specific return type. To prevent passing the wrong number of parameters
- * and have the compiler detect this error at build-time, each class is numbered
- * according to the number of parameters it takes, e.g. caller0, caller1, caller2.
- * These have been generated from zero parameters to eight.
- *
- * If you want to declare a functor which takes two parameters, a User and a Channel,
- * and returns bool, simply create it like this:
- *
- * caller2<bool, User*, Channel*> MyFunction;
- *
- * and initialize it correctly, when placed into a class you will be able to call it:
- *
- * bool n = someclass->MyFunction(someuser, somechan);
- *
- * These functor templates work this way so that you can simply and easily allow
- * for these class methods to be overridden from within a module, e.g. have a module
- * which completely replaces the code for IsNick, etc. For example, with the example
- * above:
- *
- * MyNewFunction replaceme(ServerInstance);
- *
- * someclass->MyFunction = \&replaceme;
- *
- * After this point, calls to someclass->MyFunction will call the new code in your
- * replacement functor.
- *
- * This is a very powerful feature which should be considered 'advanced' and not for
- * beginners. If you do not understand these templates, STAY AWAY from playing with
- * this until you do, as if you get this wrong, this can generate some pretty long
- * winded and confusing error messages at compile time.
- */
-template <typename ReturnType> class CoreExport HandlerBase0 : public classbase
-{
- public:
-       virtual ReturnType Call() = 0;
-       virtual ~HandlerBase0() { }
-};
-
-template <typename ReturnType, typename Param1> class CoreExport HandlerBase1 : public classbase
-{
- public:
-       virtual ReturnType Call(Param1) = 0;
-       virtual ~HandlerBase1() { }
-};
-
-template <typename ReturnType, typename Param1, typename Param2> class CoreExport HandlerBase2 : public classbase
-{
- public:
-       virtual ReturnType Call(Param1, Param2) = 0;
-       virtual ~HandlerBase2() { }
-};
-
-template <typename ReturnType, typename Param1, typename Param2, typename Param3> class CoreExport HandlerBase3 : public classbase
-{
- public:
-       virtual ReturnType Call(Param1, Param2, Param3) = 0;
-       virtual ~HandlerBase3() { }
-};
-
-template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4> class CoreExport HandlerBase4 : public classbase
-{
- public:
-       virtual ReturnType Call(Param1, Param2, Param3, Param4) = 0;
-       virtual ~HandlerBase4() { }
-};
-
-template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5> class CoreExport HandlerBase5 : public classbase
-{
- public:
-       virtual ReturnType Call(Param1, Param2, Param3, Param4, Param5) = 0;
-       virtual ~HandlerBase5() { }
-};
-
-template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6> class CoreExport HandlerBase6 : public classbase
-{
- public:
-       virtual ReturnType Call(Param1, Param2, Param3, Param4, Param5, Param6) = 0;
-       virtual ~HandlerBase6() { }
-};
-
-template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6, typename Param7> class CoreExport HandlerBase7 : public classbase
-{
- public:
-       virtual ReturnType Call(Param1, Param2, Param3, Param4, Param5, Param6, Param7) = 0;
-       virtual ~HandlerBase7() { }
-};
-
-template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6, typename Param7, typename Param8> class CoreExport HandlerBase8 : public classbase
-{
- public:
-       virtual ReturnType Call(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8) = 0;
-       virtual ~HandlerBase8() { }
-};
-
-template <typename HandlerType> class caller
-{
- public:
-       HandlerType* target;
-
-       caller(HandlerType* initial)
-       : target(initial)
-       { }
-
-       virtual ~caller() { }
-};
-
-template <typename ReturnType> class caller0 : public caller< HandlerBase0<ReturnType> >
-{
- public:
-       caller0(HandlerBase0<ReturnType>* initial)
-       : caller< HandlerBase0<ReturnType> >::caller(initial)
-       { }
-
-       ReturnType operator() ()
-       {
-               return this->target->Call();
-       }
-};
-
-template <typename ReturnType, typename Param1> class caller1 : public caller< HandlerBase1<ReturnType, Param1> >
-{
- public:
-       caller1(HandlerBase1<ReturnType, Param1>* initial)
-       : caller< HandlerBase1<ReturnType, Param1> >(initial)
-       { }
-
-       ReturnType operator() (Param1 param1)
-       {
-               return this->target->Call(param1);
-       }
-};
-
-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)
-       { }
-
-       ReturnType operator() (Param1 param1, Param2 param2)
-       {
-               return this->target->Call(param1, param2);
-       }
-};
-
-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)
-       { }
-
-       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 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)
-       { }
-
-       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 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)
-       { }
-
-       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 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)
-       { }
-
-       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 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)
-       { }
-
-       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 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)
-       { }
-
-       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);
-       }
-};
-
-/** These shorthand macros are used to define a functor class which only implements Call(). Most functors are like this.
- * If you want something more complex, define them by hand.
- *
- * The first parameter to each macro is the class name to define, the second parameter is the return value of Call().
- * The following parameters are the parameter types for Call(), and again, the macro is numbered to match the number of
- * parameters, to prevent mistakes.
- */
-#define DEFINE_HANDLER0(NAME, RETURN) \
-       class CoreExport NAME : public HandlerBase0<RETURN> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(); }
-
-#define DEFINE_HANDLER1(NAME, RETURN, V1) \
-       class CoreExport NAME : public HandlerBase1<RETURN, V1> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1); }
-
-#define DEFINE_HANDLER2(NAME, RETURN, V1, V2) \
-       class CoreExport NAME : public HandlerBase2<RETURN, V1, V2> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2); }
-
-#define DEFINE_HANDLER3(NAME, RETURN, V1, V2, V3) \
-       class CoreExport NAME : public HandlerBase3<RETURN, V1, V2, V3> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3); }
-
-#define DEFINE_HANDLER4(NAME, RETURN, V1, V2, V3, V4) \
-       class CoreExport NAME : public HandlerBase4<RETURN, V1, V2, V3, V4> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3, V4); }
-
-#define DEFINE_HANDLER5(NAME, RETURN, V1, V2, V3, V4, V5) \
-       class CoreExport NAME : public HandlerBase5<RETURN, V1, V2, V3, V4, V5> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5); }
-
-#define DEFINE_HANDLER6(NAME, RETURN, V1, V2, V3, V4, V5, V6) \
-       class CoreExport NAME : public HandlerBase6<RETURN, V1, V2, V3, V4, V5, V6> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5, V6); }
-
-#define DEFINE_HANDLER7(NAME, RETURN, V1, V2, V3, V4, V5, V6, V7) \
-       class CoreExport NAME : public HandlerBase7<RETURN, V1, V2, V3, V4, V5, V6, V7> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5, V6, V7); }
-
-#define DEFINE_HANDLER8(NAME, RETURN, V1, V2, V3, V4, V5, V6, V7, V8) \
-       class CoreExport NAME : public HandlerBase8<RETURN, V1, V2, V3, V4, V5, V6, V7, V8> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5, V6, V7, V8); }
-
-#endif
index dda53f69da0b0be756bbcd9d5de59d76aaca4c50..d346db8ef791271e6ad6e7f7eaca401207f78912 100644 (file)
  */
 
 
-#ifndef CHANNELS_H
-#define CHANNELS_H
+#pragma once
 
 #include "membership.h"
 #include "mode.h"
+#include "parammode.h"
 
 /** Holds an entry for a ban list, exemption list, or invite list.
  * This class contains a single element in a channel list, such as a banlist.
  */
-class HostItem
-{
- public:
-       /** Time the item was added
-        */
-       time_t set_time;
-       /** Who added the item
-        */
-       std::string set_by;
-       /** The actual item data
-        */
-       std::string data;
-
-       HostItem() { /* stub */ }
-       virtual ~HostItem() { /* stub */ }
-};
-
-/** A subclass of HostItem designed to hold channel bans (+b)
- */
-class BanItem : public HostItem
-{
-};
 
 /** Holds all relevent information for a channel.
  * 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
+class CoreExport Channel : public Extensible
 {
-       /** Connect a Channel to a User
+ public:
+       /** A map of Memberships on a channel keyed by User pointers
         */
-       static Channel* ForceChan(Channel* Ptr, User* user, const std::string &privs, bool bursting, bool created);
+       typedef std::map<User*, insp::aligned_storage<Membership> > MemberMap;
 
+ private:
        /** Set default modes for the channel on creation
         */
        void SetDefaultModes();
 
-       /** Maximum number of bans (cached)
-        */
-       int maxbans;
-
        /** Modes for the channel.
-        * This is not a null terminated string! It is a bitset where
-        * each item in it represents if a mode is set. For example
-        * for mode +A, index 0. Use modechar-65 to calculate which
-        * field to check.
+        * It is a bitset where each item in it represents if a mode is set.
+        * To see if a mode is set, inspect modes[mh->modeid]
         */
-       std::bitset<64> modes;
+       std::bitset<ModeParser::MODEID_MAX> modes;
 
-       /** Parameters for custom modes.
-        * One for each custom mode letter.
+       /** Remove the given membership from the channel's internal map of
+        * memberships and destroy the Membership object.
+        * 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 MemberMap iterator to remove, must be valid
         */
-       CustomModeList custom_mode_params;
+       void DelUser(const MemberMap::iterator& membiter);
 
  public:
        /** Creates a channel record and initialises it with default values
-        * @throw Nothing at present.
+        * @param name The name of the channel
+        * @param ts The creation time of the channel
+        * @throw CoreException if this channel name is in use
         */
        Channel(const std::string &name, time_t ts);
 
+       /** Checks whether the channel should be destroyed, and if yes, begins
+        * the teardown procedure.
+        *
+        * If there are users on the channel or a module vetoes the deletion
+        * (OnPreChannelDelete hook) then nothing else happens.
+        * Otherwise, first the OnChannelDelete event is fired, then the channel is
+        * removed from the channel list. All pending invites are destroyed and
+        * finally the channel is added to the cull list.
+        */
+       void CheckDestroy();
+
        /** The channel's name.
         */
        std::string name;
@@ -99,7 +90,7 @@ class CoreExport Channel : public Extensible, public InviteBase
 
        /** User list.
         */
-       UserMembList userlist;
+       MemberMap userlist;
 
        /** Channel topic.
         * If this is an empty string, no channel topic is set.
@@ -116,32 +107,19 @@ class CoreExport Channel : public Extensible, public InviteBase
         */
        std::string setby; /* 128 */
 
-       /** The list of all bans set on the channel.
-        */
-       BanList bans;
-
        /** Sets or unsets a custom mode in the channels info
         * @param mode The mode character to set or unset
         * @param value True if you want to set the mode or false if you want to remove it
         */
        void SetMode(ModeHandler* mode, bool value);
-       void SetMode(char mode,bool mode_on);
-
-       /** Sets or unsets a custom mode in the channels info
-        * @param mode The mode character to set or unset
-        * @param parameter The parameter string to associate with this mode character.
-        * If it is empty, the mode is unset; if it is nonempty, the mode is set.
-        */
-       void SetModeParam(ModeHandler* mode, const std::string& parameter);
-       void SetModeParam(char mode, const std::string& parameter);
 
        /** Returns true if a mode is set on a channel
          * @param mode The mode character you wish to query
          * @return True if the custom mode is set, false if otherwise
          */
-       inline bool IsModeSet(char mode) { return modes[mode-'A']; }
-       inline bool IsModeSet(ModeHandler* mode) { return modes[mode->GetModeChar()-'A']; }
-
+       bool IsModeSet(ModeHandler* mode) { return ((mode->GetId() != ModeParser::MODEID_MAX) && (modes[mode->GetId()])); }
+       bool IsModeSet(ModeHandler& mode) { return IsModeSet(&mode); }
+       bool IsModeSet(ChanModeReference& mode);
 
        /** Returns the parameter for a custom mode on a channel.
          * @param mode The mode character you wish to query
@@ -153,24 +131,25 @@ class CoreExport Channel : public Extensible, public InviteBase
          *
          * @return The parameter for this mode is returned, or an empty string
          */
-       std::string GetModeParameter(char mode);
        std::string GetModeParameter(ModeHandler* mode);
+       std::string GetModeParameter(ChanModeReference& mode);
+       std::string GetModeParameter(ParamModeBase* pm);
 
        /** Sets the channel topic.
-        * @param u The user setting the topic
-        * @param t The topic to set it to. Non-const, as it may be modified by a hook.
-        * @param forceset If set to true then all access checks will be bypassed.
+        * @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.
         */
-       int SetTopic(User *u, std::string &t, bool forceset = false);
+       void SetTopic(User* user, const std::string& topic, time_t topicts, const std::string* setter = NULL);
 
        /** Obtain the channel "user counter"
-        * This returns the channel reference counter, which is initialized
-        * to 0 when the channel is created and incremented/decremented
-        * upon joins, parts quits and kicks.
+        * This returns the number of users on this channel
         *
         * @return The number of users on this channel
         */
-       long GetUserCounter();
+       size_t GetUserCounter() const { return userlist.size(); }
 
        /** Add a user pointer to the internal reference list
         * @param user The user to add
@@ -196,7 +175,7 @@ class CoreExport Channel : public Extensible, public InviteBase
         *
         * @return This function returns pointer to a map of User pointers (CUList*).
         */
-       const UserMembList* GetUsers();
+       const MemberMap& GetUsers() const { return userlist; }
 
        /** Returns true if the user given is on the given channel.
         * @param user The user to look for
@@ -208,141 +187,72 @@ class CoreExport Channel : public Extensible, public InviteBase
 
        /** 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, User *user, const char* reason);
+       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
+        */
+       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 user to a channel. May be a channel that doesnt exist yet.
+       /** 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.
-        * @param cn The channel name to join to. Does not have to exist.
+        * @param channame The channel name to join to. Does not have to exist.
         * @param key The key of the channel, if given
         * @param override If true, override all join restrictions such as +bkil
         * @return A pointer to the Channel the user was joined to. A new Channel may have
         * been created if the channel did not exist before the user was joined to it.
-        * If the user could not be joined to a channel, the return value may be NULL.
-        */
-       static Channel* JoinUser(User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS = 0);
-
-       /** Write to a channel, from a user, using va_args for text
-        * @param user User whos details to prefix the line with
-        * @param text A printf-style format string which builds the output line without prefix
-        * @param ... Zero or more POD types
-        */
-       void WriteChannel(User* user, const char* text, ...) CUSTOM_PRINTF(3, 4);
-
-       /** Write to a channel, from a user, using std::string for text
-        * @param user User whos details to prefix the line with
-        * @param text A std::string containing the output line without prefix
-        */
-       void WriteChannel(User* user, const std::string &text);
-
-       /** Write to a channel, from a server, using va_args for text
-        * @param ServName Server name to prefix the line with
-        * @param text A printf-style format string which builds the output line without prefix
-        * @param ... Zero or more POD type
-        */
-       void WriteChannelWithServ(const std::string& ServName, const char* text, ...) CUSTOM_PRINTF(3, 4);
-
-       /** Write to a channel, from a server, using std::string for text
-        * @param ServName Server name to prefix the line with
-        * @param text A std::string containing the output line without prefix
-        */
-       void WriteChannelWithServ(const std::string& ServName, const std::string &text);
-
-       /** Write to all users on a channel except a specific user, using va_args for text.
-        * Internally, this calls WriteAllExcept().
-        * @param user User whos details to prefix the line with, and to omit from receipt of the message
-        * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise,
-        * use the nick!user\@host of the user.
-        * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone
-        * @param text A printf-style format string which builds the output line without prefix
-        * @param ... Zero or more POD type
+        * If the user could not be joined to a channel, the return value is NULL.
         */
-       void WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...) CUSTOM_PRINTF(5, 6);
+       static Channel* JoinUser(LocalUser* user, std::string channame, bool override = false, const std::string& key = "");
 
-       /** Write to all users on a channel except a list of users, using va_args for text
-        * @param user User whos details to prefix the line with, and to omit from receipt of the message
-        * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise,
-        * use the nick!user\@host of the user.
-        * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone
-        * @param except_list A list of users NOT to send the text to
-        * @param text A printf-style format string which builds the output line without prefix
-        * @param ... Zero or more POD type
+       /** Join a user to an existing channel, without doing any permission checks
+        * @param user The user to join to the 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 WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...) CUSTOM_PRINTF(6, 7);
+       Membership* ForceJoin(User* user, const std::string* privs = NULL, bool bursting = false, bool created_by_local = false);
 
-       /** Write to all users on a channel except a specific user, using std::string for text.
-        * Internally, this calls WriteAllExcept().
-        * @param user User whos details to prefix the line with, and to omit from receipt of the message
-        * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise,
-        * use the nick!user\@host of the user.
+       /** Write to all users on a channel except some users
+        * @param protoev Event to send, may contain any number of messages.
         * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone
-        * @param text A std::string containing the output line without prefix
+        * @param except_list List of users not to send to
         */
-       void WriteAllExceptSender(User* user, bool serversource, char status, const std::string& text);
+       void Write(ClientProtocol::Event& protoev, char status = 0, const CUList& except_list = CUList());
 
-       /** Write to all users on a channel except a list of users, using std::string for text
-        * @param user User whos details to prefix the line with, and to omit from receipt of the message
-        * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise,
-        * use the nick!user\@host of the user.
+       /** Write to all users on a channel except some users.
+        * @param protoevprov Protocol event provider for the message.
+        * @param msg Message to send.
         * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone
-        * @param except_list A list of users NOT to send the text to
-        * @param text A std::string containing the output line without prefix
-        */
-       void WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string& text);
-       /** Write a line of text that already includes the source */
-       void RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string& text);
-
-       /** Returns the maximum number of bans allowed to be set on this channel
-        * @return The maximum number of bans allowed
+        * @param except_list List of users not to send to
         */
-       long GetMaxBans();
+       void Write(ClientProtocol::EventProvider& protoevprov, ClientProtocol::Message& msg, char status = 0, const CUList& except_list = CUList());
 
        /** Return the channel's modes with parameters.
-        * @param showkey If this is set to true, the actual key is shown,
-        * otherwise it is replaced with '&lt;KEY&gt;'
+        * @param showsecret If this is set to true, the value of secret parameters
+        * are shown, otherwise they are replaced with '&lt;name&gt;'.
         * @return The channel mode string
         */
-       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
-        */
-       void UserList(User *user);
-
-       /** Get the number of invisible users on this channel
-        * @return Number of invisible users
-        */
-       int CountInvisible();
-
-       /** Get a users prefix on this channel in a string.
-        * @param user The user to look up
-        * @return A character array containing the prefix string.
-        * Unlike GetStatus and GetStatusFlags which will only return the
-        * core specified modes @, % and + (op, halfop and voice), GetPrefixChar
-        * will also return module-defined prefixes. If the user has to prefix,
-        * an empty but non-null string is returned. If the user has multiple
-        * prefixes, the highest is returned. If you do not recognise the prefix
-        * character you can get, you can deal with it in a 'proprtional' manner
-        * compared to known prefixes, using GetPrefixValue().
-        */
-       const char* GetPrefixChar(User *user);
-
-       /** Return all of a users mode prefixes into a char* string.
-        * @param user The user to look up
-        * @return A list of all prefix characters. The prefixes will always
-        * be in rank order, greatest first, as certain IRC clients require
-        * this when multiple prefixes are used names lists.
-        */
-       const char* GetAllPrefixChars(User* user);
+       const char* ChanModes(bool showsecret);
 
        /** Get the value of a users prefix on this channel.
         * @param user The user to look up
@@ -357,24 +267,6 @@ class CoreExport Channel : public Extensible, public InviteBase
         */
        unsigned int GetPrefixValue(User* user);
 
-       /** This method removes all prefix characters from a user.
-        * It will not inform the user or the channel of the removal of prefixes,
-        * and should be used when the user parts or quits.
-        * @param user The user to remove all prefixes from
-        */
-       void RemoveAllPrefixes(User* user);
-
-       /** Add a prefix character to a user.
-        * Only the core should call this method, usually  from
-        * within the mode parser or when the first user joins
-        * the channel (to grant ops to them)
-        * @param user The user to associate the privilage with
-        * @param prefix The prefix character to associate
-        * @param adding True if adding the prefix, false when removing
-        * @return True if a change was made
-        */
-       bool SetPrefix(User* user, char prefix, bool adding);
-
        /** Check if a user is banned on this channel
         * @param user A user to check against the banlist
         * @returns True if the user given is banned
@@ -389,9 +281,44 @@ class CoreExport Channel : public Extensible, public InviteBase
         */
        ModResult GetExtBanStatus(User *u, char type);
 
-       /** Clears the cached max bans value
+       /** Write a NOTICE to all local users on the channel
+        * @param text Text to send
         */
-       void ResetMaxBans();
+       void WriteNotice(const std::string& text);
 };
 
-#endif
+inline bool Channel::HasUser(User* user)
+{
+       return (userlist.find(user) != userlist.end());
+}
+
+inline std::string Channel::GetModeParameter(ChanModeReference& mode)
+{
+       if (!mode)
+               return "";
+       return GetModeParameter(*mode);
+}
+
+inline std::string Channel::GetModeParameter(ModeHandler* mh)
+{
+       std::string out;
+       ParamModeBase* pm = mh->IsParameterMode();
+       if (pm && this->IsModeSet(pm))
+               pm->GetParameter(this, out);
+       return out;
+}
+
+inline std::string Channel::GetModeParameter(ParamModeBase* pm)
+{
+       std::string out;
+       if (this->IsModeSet(pm))
+               pm->GetParameter(this, out);
+       return out;
+}
+
+inline bool Channel::IsModeSet(ChanModeReference& mode)
+{
+       if (!mode)
+               return false;
+       return IsModeSet(*mode);
+}
diff --git a/include/clientprotocol.h b/include/clientprotocol.h
new file mode 100644 (file)
index 0000000..44896a3
--- /dev/null
@@ -0,0 +1,726 @@
+/*
+ * 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 "event.h"
+
+namespace ClientProtocol
+{
+       class EventHook;
+       class MessageSource;
+       struct RFCEvents;
+       struct ParseOutput;
+       class TagSelection;
+}
+
+/** Contains a message parsed from wire format.
+ * Used by Serializer::Parse().
+ */
+struct ClientProtocol::ParseOutput
+{
+       /** Command name, must not be empty.
+        */
+       std::string cmd;
+
+       /** Parameter list, may be empty.
+        */
+       ClientProtocol::ParamList params;
+
+       /** Message tags, may be empty.
+        */
+       ClientProtocol::TagMap tags;
+};
+
+/** A selection of zero or more tags in a TagMap.
+ */
+class ClientProtocol::TagSelection
+{
+       std::bitset<64> selection;
+
+ public:
+       /** Check if a tag is selected.
+        * @param tags TagMap the tag is in. The TagMap must contain the same tags as it had when the tag
+        * was selected with Select(), otherwise the result is not meaningful.
+        * @param it Iterator to the tag to check.
+        * @return True if the tag is selected, false otherwise.
+        */
+       bool IsSelected(const TagMap& tags, TagMap::const_iterator it) const
+       {
+               const size_t index = std::distance(tags.begin(), it);
+               return ((index < selection.size()) && (selection[index]));
+       }
+
+       /** Select a tag.
+        * @param tags TagMap the tag is in. This parameter must be the same every time the method is called.
+        * The TagMap must not be altered otherwise the results of IsSelected() is not meaningful.
+        * @param it Iterator to the tag to mark as selected.
+        */
+       void Select(const TagMap& tags, TagMap::const_iterator it)
+       {
+               const size_t index = std::distance(tags.begin(), it);
+               if (index < selection.size())
+                       selection[index] = true;
+       }
+
+       /** Check if a TagSelection is equivalent to this object.
+        * @param other Other TagSelection object to compare this with.
+        * @return True if the objects are equivalent, false if they aren't.
+        */
+       bool operator==(const TagSelection& other) const
+       {
+               return (this->selection == other.selection);
+       }
+};
+
+class ClientProtocol::MessageSource
+{
+       User* sourceuser;
+       const std::string* sourcestr;
+
+ public:
+       /** Constructor, sets the source to be the full host of a user or sets it to be nothing.
+        * The actual source string when serializing will be obtained from User::GetFullHost() if the user is non-NULL.
+        * @param Sourceuser User to set as source of the message. If NULL, the message won't have a source when serialized.
+        * Optional, defaults to NULL.
+        */
+       MessageSource(User* Sourceuser = NULL)
+       {
+               SetSourceUser(Sourceuser);
+       }
+
+       /** Constructor, sets the source to the supplied string and optionally sets the source user.
+        * @param Sourcestr String to use as message source for serialization purposes. Must remain valid
+        * as long as this object is alive.
+        * @param Sourceuser User to set as source. Optional, defaults to NULL. It will not be used for serialization but
+        * if provided it may be used internally, for example to create message tags.
+        * Useful when the source string is synthesized but it is still related to a User.
+        */
+       MessageSource(const std::string& Sourcestr, User* Sourceuser = NULL)
+       {
+               SetSource(Sourcestr, Sourceuser);
+       }
+
+       /** Get the source of this message as a string.
+        * @return Pointer to the message source string or NULL if there is no source.
+        */
+       const std::string* GetSource() const
+       {
+               // Return string if there's one explicitly set
+               if (sourcestr)
+                       return sourcestr;
+               if (sourceuser)
+                       return &sourceuser->GetFullHost();
+               return NULL;
+       }
+
+       /** Get the source User.
+        * This shouldn't be used for serialization, use GetSource() for that.
+        * @return User pointer if the message has a source user, NULL otherwise.
+        */
+       User* GetSourceUser() const { return sourceuser; }
+
+       /** Set the source of this message to a User.
+        * See the one parameter constructor for a more detailed description.
+        * @param Sourceuser User to set as source.
+        */
+       void SetSourceUser(User* Sourceuser)
+       {
+               sourceuser = Sourceuser;
+               sourcestr = NULL;
+       }
+
+       /** Set the source string and optionally source user.
+        * See the two parameter constructor for a more detailed description.
+        * @param Sourcestr String source, to be used for serialization purposes. Must remain valid as long
+        * as this object is alive.
+        * @param Sourceuser Source user to set, optional.
+        */
+       void SetSource(const std::string& Sourcestr, User* Sourceuser = NULL)
+       {
+               sourcestr = &Sourcestr;
+               sourceuser = Sourceuser;
+       }
+
+       /** Copy the source from a MessageSource object.
+        * @param other MessageSource object to copy from.
+        */
+       void SetSource(const MessageSource& other)
+       {
+               sourcestr = other.sourcestr;
+               sourceuser = other.sourceuser;
+       }
+};
+
+/** Outgoing client protocol message.
+ * Represents a high level client protocol message which is turned into raw or wire format
+ * by a Serializer. Messages can be serialized into different format by different serializers.
+ *
+ * Messages are created on demand and are disposed of after they have been sent.
+ *
+ * All messages have a command name, a list of parameters and a map of tags, the last two can be empty.
+ * They also always have a source, see class MessageSource.
+ */
+class ClientProtocol::Message : public ClientProtocol::MessageSource
+{
+ public:
+       /** Contains information required to identify a specific version of a serialized message.
+        */
+       struct SerializedInfo
+       {
+               const Serializer* serializer;
+               TagSelection tagwl;
+
+               /** Constructor.
+                * @param Ser Serializer used to serialize the message.
+                * @param Tagwl Tag whitelist used to serialize the message.
+                */
+               SerializedInfo(const Serializer* Ser, const TagSelection& Tagwl)
+                       : serializer(Ser)
+                       , tagwl(Tagwl)
+               {
+               }
+
+               /** Check if a SerializedInfo object is equivalent to this object.
+                * @param other Other SerializedInfo object.
+                * @return True if other is equivalent to this object, false otherwise.
+                */
+               bool operator==(const SerializedInfo& other) const
+               {
+                       return ((serializer == other.serializer) && (tagwl == other.tagwl));
+               }
+       };
+
+       class Param
+       {
+               const std::string* ptr;
+               insp::aligned_storage<std::string> str;
+               bool owned;
+
+               void InitFrom(const Param& other)
+               {
+                       owned = other.owned;
+                       if (owned)
+                               new(str) std::string(*other.str);
+                       else
+                               ptr = other.ptr;
+               }
+
+        public:
+               operator const std::string&() const { return (owned ? *str : *ptr); }
+
+               Param()
+                       : ptr(NULL)
+                       , owned(false)
+               {
+               }
+
+               Param(const std::string& s)
+                       : ptr(&s)
+                       , owned(false)
+               {
+               }
+
+               Param(int, const char* s)
+                       : owned(true)
+               {
+                       new(str) std::string(s);
+               }
+
+               Param(int, const std::string& s)
+                       : owned(true)
+               {
+                        new(str) std::string(s);
+               }
+
+               Param(const Param& other)
+               {
+                       InitFrom(other);
+               }
+
+               ~Param()
+               {
+                       using std::string;
+                       if (owned)
+                               str->~string();
+               }
+
+               Param& operator=(const Param& other)
+               {
+                       if (&other == this)
+                               return *this;
+
+                       using std::string;
+                       if (owned)
+                               str->~string();
+                       InitFrom(other);
+                       return *this;
+               }
+
+               bool IsOwned() const { return owned; }
+       };
+
+       typedef std::vector<Param> ParamList;
+
+ private:
+       typedef std::vector<std::pair<SerializedInfo, SerializedMessage> > SerializedList;
+
+       ParamList params;
+       TagMap tags;
+       std::string command;
+       bool msginit_done;
+       mutable SerializedList serlist;
+       bool sideeffect;
+
+ protected:
+       /** Set command string.
+        * @param cmd Command string to set.
+        */
+       void SetCommand(const char* cmd)
+       {
+               command.clear();
+               if (cmd)
+                       command = cmd;
+       }
+
+ public:
+       /** Constructor.
+        * @param cmd Command name, e.g. "JOIN", "NICK". May be NULL. If NULL, the command must be set
+        * with SetCommand() before the message is serialized.
+        * @param Sourceuser See the one parameter constructor of MessageSource for description.
+        */
+       Message(const char* cmd, User* Sourceuser = NULL)
+               : ClientProtocol::MessageSource(Sourceuser)
+               , command(cmd ? cmd : std::string())
+               , msginit_done(false)
+               , sideeffect(false)
+       {
+               params.reserve(8);
+               serlist.reserve(8);
+       }
+
+       /** Constructor.
+        * @param cmd Command name, e.g. "JOIN", "NICK". May be NULL. If NULL, the command must be set
+        * with SetCommand() before the message is serialized.
+        * @param Sourcestr See the two parameter constructor of MessageSource for description.
+        * Must remain valid as long as this object is alive.
+        * @param Sourceuser See the two parameter constructor of MessageSource for description.
+        */
+       Message(const char* cmd, const std::string& Sourcestr, User* Sourceuser = NULL)
+               : ClientProtocol::MessageSource(Sourcestr, Sourceuser)
+               , command(cmd ? cmd : std::string())
+               , msginit_done(false)
+               , sideeffect(false)
+       {
+               params.reserve(8);
+               serlist.reserve(8);
+       }
+
+       /** Get the parameters of this message.
+        * @return List of parameters.
+        */
+       const ParamList& GetParams() const { return params; }
+
+       /** Get a map of tags attached to this message.
+        * The map contains the tag providers that attached the tag to the message.
+        * @return Map of tags.
+        */
+       const TagMap& GetTags() const { return tags; }
+
+       /** Get the command string.
+        * @return Command string, e.g. "NICK", "001".
+        */
+       const char* GetCommand() const { return command.c_str(); }
+
+       /** Add a parameter to the parameter list.
+        * @param str String to add, will be copied.
+        */
+       void PushParam(const char* str) { params.push_back(Param(0, str)); }
+
+       /** Add a parameter to the parameter list.
+        * @param str String to add, will be copied.
+        */
+       void PushParam(const std::string& str) { params.push_back(Param(0, str)); }
+
+       /** Add a parameter to the parameter list.
+        * @param str String to add.
+        * The string will NOT be copied, it must remain alive until ClearParams() is called or until the object is destroyed.
+        */
+       void PushParamRef(const std::string& str) { params.push_back(str); }
+
+       /** Add a placeholder parameter to the parameter list.
+        * Placeholder parameters must be filled in later with actual parameters using ReplaceParam() or ReplaceParamRef().
+        */
+       void PushParamPlaceholder() { params.push_back(Param()); }
+
+       /** Replace a parameter or a placeholder that is already in the parameter list.
+        * @param index Index of the parameter to replace. Must be less than GetParams().size().
+        * @param str String to replace the parameter or placeholder with, will be copied.
+        */
+       void ReplaceParam(unsigned int index, const char* str) { params[index] = Param(0, str); }
+
+       /** Replace a parameter or a placeholder that is already in the parameter list.
+        * @param index Index of the parameter to replace. Must be less than GetParams().size().
+        * @param str String to replace the parameter or placeholder with, will be copied.
+        */
+       void ReplaceParam(unsigned int index, const std::string& str) { params[index] = Param(0, str); }
+
+       /** Replace a parameter or a placeholder that is already in the parameter list.
+        * @param index Index of the parameter to replace. Must be less than GetParams().size().
+        * @param str String to replace the parameter or placeholder with.
+        * The string will NOT be copied, it must remain alive until ClearParams() is called or until the object is destroyed.
+        */
+       void ReplaceParamRef(unsigned int index, const std::string& str) { params[index] = Param(str); }
+
+       /** Add a tag.
+        * @param tagname Raw name of the tag to use in the protocol.
+        * @param tagprov Provider of the tag.
+        * @param val Tag value. If empty no value will be sent with the tag.
+        * @param tagdata Tag provider specific data, will be passed to MessageTagProvider::ShouldSendTag(). Optional, defaults to NULL.
+        */
+       void AddTag(const std::string& tagname, MessageTagProvider* tagprov, const std::string& val, void* tagdata = NULL)
+       {
+               tags.insert(std::make_pair(tagname, MessageTagData(tagprov, val, tagdata)));
+       }
+
+       /** Add all tags in a TagMap to the tags in this message. Existing tags will not be overwritten.
+        * @param newtags New tags to add.
+        */
+       void AddTags(const ClientProtocol::TagMap& newtags)
+       {
+               tags.insert(newtags.begin(), newtags.end());
+       }
+
+       /** Get the message in a serialized form.
+        * @param serializeinfo Information about which exact serialized form of the message is the caller asking for
+        * (which serializer to use and which tags to include).
+        * @return Serialized message according to serializeinfo. The returned reference remains valid until the
+        * next call to this method.
+        */
+       const SerializedMessage& GetSerialized(const SerializedInfo& serializeinfo) const;
+
+       /** Clear the parameter list and tags.
+        */
+       void ClearParams()
+       {
+               msginit_done = false;
+               params.clear();
+               tags.clear();
+               InvalidateCache();
+       }
+
+       /** Remove all serialized messages.
+        * If a parameter is changed after the message has been sent at least once, this method must be called before
+        * serializing the message again to ensure the cache won't contain stale data.
+        */
+       void InvalidateCache()
+       {
+               serlist.clear();
+       }
+
+       void CopyAll()
+       {
+               size_t j = 0;
+               for (ParamList::iterator i = params.begin(); i != params.end(); ++i, j++)
+               {
+                       Param& curr = *i;
+                       if (!curr.IsOwned())
+                               ReplaceParam(j, curr);
+               }
+       }
+
+       void SetSideEffect(bool Sideeffect) { sideeffect = Sideeffect; }
+       bool IsSideEffect() const { return sideeffect; }
+
+       friend class Serializer;
+};
+
+/** Client protocol event class.
+ * All messages sent to a user must be part of an event. A single event may result in more than one protocol message
+ * being sent, for example a join event may result in a JOIN and a MODE protocol message sent to members of the channel
+ * if the joining user has some prefix modes set.
+ *
+ * Event hooks attached to a specific event can alter the messages sent for that event.
+ */
+class ClientProtocol::Event
+{
+       EventProvider* event;
+       Message* initialmsg;
+       const MessageList* initialmsglist;
+       bool eventinit_done;
+
+ public:
+       /** Constructor.
+        * @param protoeventprov Protocol event provider the event is an instance of.
+        */
+       Event(EventProvider& protoeventprov)
+               : event(&protoeventprov)
+               , initialmsg(NULL)
+               , initialmsglist(NULL)
+               , eventinit_done(false)
+       {
+       }
+
+       /** Constructor.
+        * @param protoeventprov Protocol event provider the event is an instance of.
+        * @param msg Message to include in this event by default.
+        */
+       Event(EventProvider& protoeventprov, ClientProtocol::Message& msg)
+               : event(&protoeventprov)
+               , initialmsg(&msg)
+               , initialmsglist(NULL)
+               , eventinit_done(false)
+       {
+       }
+
+       /** Set a single message as the initial message in the event.
+        * Modules may alter this later.
+        */
+       void SetMessage(Message* msg)
+       {
+               initialmsg = msg;
+               initialmsglist = NULL;
+       }
+
+       /** Set a list of messages as the initial messages in the event.
+        * Modules may alter this later.
+        */
+       void SetMessageList(const MessageList& msglist)
+       {
+               initialmsg = NULL;
+               initialmsglist = &msglist;
+       }
+
+       /** Get a list of messages to send to a user.
+        * The exact messages sent to a user are determined by the initial message(s) set and hooks.
+        * @param user User to get the messages for.
+        * @param messagelist List to fill in with messages to send to the user for the event
+        */
+       void GetMessagesForUser(LocalUser* user, MessageList& messagelist);
+};
+
+/** Base class for message tag providers.
+ * All message tags belong to a message tag provider. Message tag providers can populate messages
+ * with tags before the message is sent and they have the job of determining whether a user should
+ * get a message tag or be allowed to send one.
+ */
+class ClientProtocol::MessageTagProvider : public Events::ModuleEventListener
+{
+ public:
+       /** Constructor.
+        * @param mod Module owning the provider.
+        */
+       MessageTagProvider(Module* mod)
+               : Events::ModuleEventListener(mod, "event/messagetag")
+       {
+       }
+
+       /** Called when a message is ready to be sent to give the tag provider a chance to add tags to the message.
+        * To add tags call Message::AddTag(). If the provided tag or tags have been added already elsewhere or if the
+        * provider doesn't want its tag(s) to be on the message, the implementation doesn't have to do anything special.
+        * The default implementation does nothing.
+        * @param msg Message to be populated with tags.
+        */
+       virtual void OnPopulateTags(ClientProtocol::Message& msg)
+       {
+       }
+
+       /** Called for each tag that the server receives from a client in a message.
+        * @param user User that sent the tag.
+        * @param tagname Name of the tag.
+        * @param tagvalue Value of the tag, empty string if the tag has no value. May be modified.
+        * @return MOD_RES_ALLOW to accept the tag with the value in 'value', MOD_RES_DENY to reject the tag and act as if it wasn't sent,
+        * MOD_RES_PASSTHRU to make no decision. If no hooks accept a tag, the tag is rejected.
+        * The default implementation returns MOD_RES_PASSTHRU.
+        */
+       virtual ModResult OnProcessTag(User* user, const std::string& tagname, std::string& tagvalue)
+       {
+               return MOD_RES_PASSTHRU;
+       }
+
+       /** Called whenever a user is about to receive a message that has a tag attached which is provided by this provider
+        * to determine whether or not the user should get the tag.
+        * @param user User in question.
+        * @param tagdata Tag in question.
+        * @return True if the tag should be sent to the user, false otherwise.
+        */
+       virtual bool ShouldSendTag(LocalUser* user, const MessageTagData& tagdata) = 0;
+};
+
+/** Base class for client protocol event hooks.
+ * A protocol event hook is attached to a single event type. It has the ability to alter or block messages
+ * sent to users which belong to the event the hook is attached to.
+ */
+class ClientProtocol::EventHook : public Events::ModuleEventListener
+{
+ public:
+       static std::string GetEventName(const std::string& name)
+       {
+               return "event/protoevent_" + name;
+       }
+
+       /** Constructor.
+        * @param mod Owner of the hook.
+        * @param name Name of the event to hook.
+        * @param priority Priority of the hook. Determines the order in which hooks for the same event get called.
+        * Optional.
+        */
+       EventHook(Module* mod, const std::string& name, unsigned int priority = Events::ModuleEventListener::DefaultPriority)
+               : Events::ModuleEventListener(mod, GetEventName(name), priority)
+       {
+       }
+
+       /** Called exactly once before an event is sent to any user.
+        * The default implementation doesn't do anything.
+        * @param ev Event being sent to one or more users.
+        */
+       virtual void OnEventInit(const ClientProtocol::Event& ev)
+       {
+       }
+
+       /** Called for each user that may receive the event.
+        * The hook may alter the messages sent to the user and decide whether further hooks should be executed.
+        * @param user User the message list is being prepared to be sent to.
+        * @param ev Event associated with the messages.
+        * @param messagelist List of messages to send to the user. The hook can alter this in any way it sees fit, for example it can replace messages,
+        * add new messages, etc. The list must not be empty when the method returns.
+        * @return MOD_RES_PASSTHRU to run hooks after the called hook or if no hooks are after the called hook, send the messages in messagelist to the user.
+        * MOD_RES_DENY to not send any messages to the user and to not run other hooks,
+        * MOD_RES_ALLOW to send the messages in messagelist to the user and to not run other hooks.
+        */
+       virtual ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) = 0;
+};
+
+/** Event provider for client protocol events.
+ * Protocol event hooks can be attached to the instances of these providers. The core has event
+ * providers for most common IRC events defined in RFC1459.
+ */
+class ClientProtocol::EventProvider : public Events::ModuleEventProvider
+{
+ public:
+       /** Constructor.
+        * @param Mod Module that owns the event provider.
+        * @param eventname Name of the event this provider is for, e.g. "JOIN", "PART", "NUMERIC".
+        * Should match command name if applicable.
+        */
+       EventProvider(Module* Mod, const std::string& eventname)
+               : Events::ModuleEventProvider(Mod, ClientProtocol::EventHook::GetEventName(eventname))
+       {
+       }
+};
+
+/** Commonly used client protocol events.
+ * Available via InspIRCd::GetRFCEvents().
+ */
+struct ClientProtocol::RFCEvents
+{
+       EventProvider numeric;
+       EventProvider join;
+       EventProvider part;
+       EventProvider kick;
+       EventProvider quit;
+       EventProvider nick;
+       EventProvider mode;
+       EventProvider topic;
+       EventProvider privmsg;
+       EventProvider invite;
+       EventProvider ping;
+       EventProvider pong;
+       EventProvider error;
+
+       RFCEvents()
+               : numeric(NULL, "NUMERIC")
+               , join(NULL, "JOIN")
+               , part(NULL, "PART")
+               , kick(NULL, "KICK")
+               , quit(NULL, "QUIT")
+               , nick(NULL, "NICK")
+               , mode(NULL, "MODE")
+               , topic(NULL, "TOPIC")
+               , privmsg(NULL, "PRIVMSG")
+               , invite(NULL, "INVITE")
+               , ping(NULL, "PING")
+               , pong(NULL, "PONG")
+               , error(NULL, "ERROR")
+       {
+       }
+};
+
+/** Base class for client protocol serializers.
+ * A serializer has to implement serialization and parsing of protocol messages to/from wire format.
+ */
+class CoreExport ClientProtocol::Serializer : public DataProvider
+{
+       Events::ModuleEventProvider evprov;
+
+       /** Make a white list containing which tags a user should get.
+        * @param user User in question.
+        * @param tagmap Tag map that contains all possible tags.
+        * @return Whitelist of tags to send to the user.
+        */
+       TagSelection MakeTagWhitelist(LocalUser* user, const TagMap& tagmap) const;
+
+ public:
+       /** Constructor.
+        * @param mod Module owning the serializer.
+        * @param Name Name of the serializer, e.g. "rfc".
+        */
+       Serializer(Module* mod, const char* Name);
+
+       /** Handle a tag in a message being parsed. Call this method for each parsed tag.
+        * @param user User sending the tag.
+        * @param tagname Name of the tag.
+        * @param tagvalue Tag value, may be empty.
+        * @param tags TagMap to place the tag into, if it gets accepted.
+        * @return True if no error occured, false if the tag name is invalid or if this tag already exists.
+        */
+       bool HandleTag(LocalUser* user, const std::string& tagname, std::string& tagvalue, TagMap& tags) const;
+
+       /** Serialize a message for a user.
+        * @param user User to serialize the message for.
+        * @param msg Message to serialize.
+        * @return Raw serialized message, only containing the appropriate tags for the user.
+        * The reference is guaranteed to be valid as long as the Message object is alive and until the same
+        * Message is serialized for another user.
+        */
+       const SerializedMessage& SerializeForUser(LocalUser* user, Message& msg);
+
+       /** Serialize a high level protocol message into wire format.
+        * @param msg High level message to serialize. Contains all necessary information about the message, including all possible tags.
+        * @param tagwl Message tags to include in the serialized message. Tags attached to the message but not included in the whitelist must not
+        * appear in the output. This is because each user may get a different set of tags for the same message.
+        * @return Protocol message in wire format. Must contain message delimiter as well, if any (e.g. CRLF for RFC1459).
+        */
+       virtual std::string Serialize(const Message& msg, const TagSelection& tagwl) const = 0;
+
+       /** Parse a protocol message from wire format.
+        * @param user Source of the message.
+        * @param line Raw protocol message.
+        * @param parseoutput Output of the parser.
+        * @return True if the message was parsed successfully into parseoutput and should be processed, false to drop the message.
+        */
+       virtual bool Parse(LocalUser* user, const std::string& line, ParseOutput& parseoutput) = 0;
+};
+
+inline ClientProtocol::MessageTagData::MessageTagData(MessageTagProvider* prov, const std::string& val, void* data)
+       : tagprov(prov)
+       , value(val)
+       , provdata(data)
+{
+}
diff --git a/include/clientprotocolevent.h b/include/clientprotocolevent.h
new file mode 100644 (file)
index 0000000..6b89d0f
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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 ClientProtocol
+{
+       namespace Events
+       {
+               struct Join;
+               class Mode;
+       }
+}
+
+struct ClientProtocol::Events::Join : public ClientProtocol::Messages::Join, public ClientProtocol::Event
+{
+       Join()
+               : ClientProtocol::Event(ServerInstance->GetRFCEvents().join, *this)
+       {
+       }
+
+       Join(Membership* Memb)
+               : ClientProtocol::Messages::Join(Memb)
+               , ClientProtocol::Event(ServerInstance->GetRFCEvents().join, *this)
+       {
+       }
+
+       Join(Membership* Memb, const std::string& Sourcestr)
+               : ClientProtocol::Messages::Join(Memb, Sourcestr)
+               , ClientProtocol::Event(ServerInstance->GetRFCEvents().join, *this)
+       {
+       }
+};
+
+class ClientProtocol::Events::Mode : public ClientProtocol::Event
+{
+       std::list<ClientProtocol::Messages::Mode> modelist;
+       std::vector<Message*> modemsgplist;
+       const Modes::ChangeList& modechanges;
+
+ public:
+       static void BuildMessages(User* source, Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist, std::list<ClientProtocol::Messages::Mode>& modelist, std::vector<Message*>& modemsgplist)
+       {
+               // Build as many MODEs as necessary
+               for (Modes::ChangeList::List::const_iterator i = changelist.getlist().begin(); i != changelist.getlist().end(); i = modelist.back().GetEndIterator())
+               {
+                       modelist.push_back(ClientProtocol::Messages::Mode(source, Chantarget, Usertarget, changelist, i));
+                       modemsgplist.push_back(&modelist.back());
+               }
+       }
+
+       Mode(User* source, Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist)
+               : ClientProtocol::Event(ServerInstance->GetRFCEvents().mode)
+               , modechanges(changelist)
+       {
+               BuildMessages(source, Chantarget, Usertarget, changelist, modelist, modemsgplist);
+               SetMessageList(modemsgplist);
+       }
+
+       const Modes::ChangeList& GetChangeList() const { return modechanges; }
+       const std::list<ClientProtocol::Messages::Mode>& GetMessages() const { return modelist; }
+};
diff --git a/include/clientprotocolmsg.h b/include/clientprotocolmsg.h
new file mode 100644 (file)
index 0000000..d2f838d
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * 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 ClientProtocol
+{
+       namespace Messages
+       {
+               class Numeric;
+               class Join;
+               struct Part;
+               struct Kick;
+               struct Quit;
+               struct Nick;
+               class Mode;
+               struct Topic;
+               class Privmsg;
+               struct Invite;
+               struct Ping;
+               struct Pong;
+               struct Error;
+       }
+}
+
+/** Numeric message.
+ * Doesn't have a fixed command name, it's always a 3 digit number padded with zeroes if necessary.
+ * The first parameter is the target of the numeric which is almost always the nick of the user
+ * the numeric will be sent to.
+ */
+class ClientProtocol::Messages::Numeric : public ClientProtocol::Message
+{
+       char numericstr[4];
+
+       void InitCommand(unsigned int number)
+       {
+               snprintf(numericstr, sizeof(numericstr), "%03u", number);
+               SetCommand(numericstr);
+       }
+
+       void InitFromNumeric(const ::Numeric::Numeric& numeric)
+       {
+               InitCommand(numeric.GetNumeric());
+               for (std::vector<std::string>::const_iterator i = numeric.GetParams().begin(); i != numeric.GetParams().end(); ++i)
+                       PushParamRef(*i);
+       }
+
+ public:
+       /** Constructor, target is a User.
+        * @param num Numeric object to send. Must remain valid as long as this object is alive and must not be modified.
+        * @param user User to send the numeric to. May be unregistered, must remain valid as long as this object is alive.
+        */
+       Numeric(const ::Numeric::Numeric& num, User* user)
+               : ClientProtocol::Message(NULL, (num.GetServer() ? num.GetServer()->GetName() : ServerInstance->Config->ServerName))
+       {
+               if (user->registered & REG_NICK)
+                       PushParamRef(user->nick);
+               else
+                       PushParam("*");
+               InitFromNumeric(num);
+       }
+
+       /** Constructor, target is a string.
+        * @param num Numeric object to send. Must remain valid as long as this object is alive and must not be modified.
+        * @param target Target string, must stay valid as long as this object is alive.
+        */
+       Numeric(const ::Numeric::Numeric& num, const std::string& target)
+               : ClientProtocol::Message(NULL, (num.GetServer() ? num.GetServer()->GetName() : ServerInstance->Config->ServerName))
+       {
+               PushParamRef(target);
+               InitFromNumeric(num);
+       }
+
+       /** Constructor. Only the numeric number has to be specified.
+        * @param num Numeric number.
+        */
+       Numeric(unsigned int num)
+               : ClientProtocol::Message(NULL, ServerInstance->Config->ServerName)
+       {
+               InitCommand(num);
+               PushParam("*");
+       }
+};
+
+/** JOIN message.
+ * Sent when a user joins a channel.
+ */
+class ClientProtocol::Messages::Join : public ClientProtocol::Message
+{
+       Membership* memb;
+
+ public:
+       /** Constructor. Does not populate parameters, call SetParams() before sending the message.
+        */
+       Join()
+               : ClientProtocol::Message("JOIN")
+               , memb(NULL)
+       {
+       }
+
+       /** Constructor.
+        * @param Memb Membership of the joining user.
+        */
+       Join(Membership* Memb)
+               : ClientProtocol::Message("JOIN", Memb->user)
+       {
+               SetParams(Memb);
+       }
+
+       /** Constructor.
+        * @param Memb Membership of the joining user.
+        * @param sourcestrref Message source string, must remain valid as long as this object is alive.
+        */
+       Join(Membership* Memb, const std::string& sourcestrref)
+               : ClientProtocol::Message("JOIN", sourcestrref, Memb->user)
+       {
+               SetParams(Memb);
+       }
+
+       /** Populate parameters from a Membership
+        * @param Memb Membership of the joining user.
+        */
+       void SetParams(Membership* Memb)
+       {
+               memb = Memb;
+               PushParamRef(memb->chan->name);
+       }
+
+       /** Get the Membership of the joining user.
+        * @return Membership of the joining user.
+        */
+       Membership* GetMember() const { return memb; }
+};
+
+/** PART message.
+ * Sent when a user parts a channel.
+ */
+struct ClientProtocol::Messages::Part : public ClientProtocol::Message
+{
+       /** Constructor.
+        * @param memb Member parting.
+        * @param reason Part reason, may be empty. If non-empty, must remain valid as long as this object is alive.
+        */
+       Part(Membership* memb, const std::string& reason)
+               : ClientProtocol::Message("PART", memb->user)
+       {
+               PushParamRef(memb->chan->name);
+               if (!reason.empty())
+                       PushParamRef(reason);
+       }
+};
+
+/** KICK message.
+ * Sent when a user is kicked from a channel.
+ */
+struct ClientProtocol::Messages::Kick : public ClientProtocol::Message
+{
+       /** Constructor.
+        * @param source User that does the kick.
+        * @param memb Membership of the user being kicked.
+        * @param reason Kick reason. Must remain valid as long as this object is alive.
+        */
+       Kick(User* source, Membership* memb, const std::string& reason)
+               : ClientProtocol::Message("KICK", source)
+       {
+               PushParamRef(memb->chan->name);
+               PushParamRef(memb->user->nick);
+               PushParamRef(reason);
+       }
+};
+
+/** QUIT message.
+ * Sent when a user quits.
+ */
+struct ClientProtocol::Messages::Quit : public ClientProtocol::Message
+{
+       /** Constructor.
+        * @param source User quitting.
+        * @param reason Quit reason, may be empty. Must remain valid as long as this object is alive.
+        */
+       Quit(User* source, const std::string& reason)
+               : ClientProtocol::Message("QUIT", source)
+       {
+               if (!reason.empty())
+                       PushParamRef(reason);
+       }
+};
+
+/** NICK message.
+ * Sent when a user changes their nickname.
+ */
+struct ClientProtocol::Messages::Nick : public ClientProtocol::Message
+{
+       /** Constructor.
+        * @param source User changing nicks.
+        * @param newnick New nickname. Must remain valid as long as this object is alive.
+        */
+       Nick(User* source, const std::string& newnick)
+               : ClientProtocol::Message("NICK", source)
+       {
+               PushParamRef(newnick);
+       }
+};
+
+/** MODE message.
+ * Sent when modes are changed on a user or channel.
+ */
+class ClientProtocol::Messages::Mode : public ClientProtocol::Message
+{
+       Channel* chantarget;
+       User* usertarget;
+       Modes::ChangeList::List::const_iterator beginit;
+       Modes::ChangeList::List::const_iterator lastit;
+
+       /** Convert a range of a mode change list to mode letters and '+', '-' symbols.
+        * @param list Mode change list.
+        * @param maxlinelen Maximum output length.
+        * @param beginit Iterator to the first element in 'list' to process.
+        * @param lastit Iterator which is set to the first element not processed due to length limitations by the method.
+        */
+       static std::string ToModeLetters(const Modes::ChangeList::List& list, std::string::size_type maxlinelen, Modes::ChangeList::List::const_iterator beginit, Modes::ChangeList::List::const_iterator& lastit)
+       {
+               std::string ret;
+               std::string::size_type paramlength = 0;
+               char output_pm = '\0'; // current output state, '+' or '-'
+
+               Modes::ChangeList::List::const_iterator i;
+               for (i = beginit; i != list.end(); ++i)
+               {
+                       const Modes::Change& item = *i;
+
+                       const char needed_pm = (item.adding ? '+' : '-');
+                       if (needed_pm != output_pm)
+                       {
+                               output_pm = needed_pm;
+                               ret.push_back(output_pm);
+                       }
+
+                       if (!item.param.empty())
+                               paramlength += item.param.length() + 1;
+                       if (ret.length() + 1 + paramlength > maxlinelen)
+                       {
+                               // Mode sequence is getting too long
+                               const char c = *ret.rbegin();
+                               if ((c == '+') || (c == '-'))
+                                       ret.erase(ret.size()-1);
+                               break;
+                       }
+
+                       ret.push_back(item.mh->GetModeChar());
+               }
+
+               lastit = i;
+               return ret;
+       }
+
+       /** Push mode parameters for modes that have one, starting at beginit to lastit (not including lastit).
+        */
+       void PushModeParams()
+       {
+               for (Modes::ChangeList::List::const_iterator i = beginit; i != lastit; ++i)
+               {
+                       const Modes::Change& item = *i;
+                       if (!item.param.empty())
+                               PushParamRef(item.param);
+               }
+       }
+
+ public:
+       /** Convert an entire mode change list into mode letters and '+' and '-' characters.
+        * @param changelist Mode change list to convert into mode letters.
+        * @return Mode letters.
+        */
+       static std::string ToModeLetters(const Modes::ChangeList& changelist)
+       {
+               // TODO: This assumes that std::string::max_size() >= UINT_MAX
+               Modes::ChangeList::List::const_iterator dummy;
+               return ToModeLetters(changelist.getlist(), UINT_MAX, changelist.getlist().begin(), dummy);
+       }
+
+       /** Constructor, populate parameters starting from a given position in a mode change list.
+        * @param source User doing the mode change.
+        * @param Chantarget Channel target of the mode change. May be NULL if Usertarget is non-NULL.
+        * @param Usertarget User target of the mode change. May be NULL if Chantarget is non-NULL.
+        * @param changelist Mode change list. Must remain valid and unchanged as long as this object is alive or until the next SetParams() call.
+        * @param beginiter Starting position of mode changes in 'changelist'.
+        */
+       Mode(User* source, Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist, Modes::ChangeList::List::const_iterator beginiter)
+               : ClientProtocol::Message("MODE", source)
+               , chantarget(Chantarget)
+               , usertarget(Usertarget)
+               , beginit(beginiter)
+       {
+               PushParamRef(GetStrTarget());
+               PushParam(ToModeLetters(changelist.getlist(), 450, beginit, lastit));
+               PushModeParams();
+       }
+
+       /** Constructor, populate parameters starting from the beginning of a mode change list.
+        * @param source User doing the mode change.
+        * @param Chantarget Channel target of the mode change. May be NULL if Usertarget is non-NULL.
+        * @param Usertarget User target of the mode change. May be NULL if Chantarget is non-NULL.
+        * @param changelist Mode change list. Must remain valid and unchanged as long as this object is alive or until the next SetParams() call.
+        */
+       Mode(User* source, Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist)
+               : ClientProtocol::Message("MODE", source)
+               , chantarget(Chantarget)
+               , usertarget(Usertarget)
+               , beginit(changelist.getlist().begin())
+       {
+               PushParamRef(GetStrTarget());
+               PushParam(ToModeLetters(changelist.getlist(), 450, beginit, lastit));
+               PushModeParams();
+       }
+
+       /** Constructor. Does not populate parameters, call SetParams() before sending the message.
+        * The message source is set to the local server.
+        */
+       Mode()
+               : ClientProtocol::Message("MODE", ServerInstance->FakeClient)
+               , chantarget(NULL)
+               , usertarget(NULL)
+       {
+       }
+
+       /** Set parameters
+        * @param Chantarget Channel target of the mode change. May be NULL if Usertarget is non-NULL.
+        * @param Usertarget User target of the mode change. May be NULL if Chantarget is non-NULL.
+        * @param changelist Mode change list. Must remain valid and unchanged as long as this object is alive or until the next SetParams() call.
+        */
+       void SetParams(Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist)
+       {
+               ClearParams();
+
+               chantarget = Chantarget;
+               usertarget = Usertarget;
+               beginit = changelist.getlist().begin();
+
+               PushParamRef(GetStrTarget());
+               PushParam(ToModeLetters(changelist.getlist(), 450, beginit, lastit));
+               PushModeParams();
+       }
+
+       /** Get first mode change included in this MODE message.
+        * @return Iterator to the first mode change that is included in this MODE message.
+        */
+       Modes::ChangeList::List::const_iterator GetBeginIterator() const { return beginit; }
+
+       /** Get first mode change not included in this MODE message.
+        * @return Iterator to the first mode change that is not included in this MODE message.
+        */
+       Modes::ChangeList::List::const_iterator GetEndIterator() const { return lastit; }
+
+       /** Get mode change target as a string.
+        * This is the name of the channel if the mode change targets a channel or the nickname of the user
+        * if the target is a user.
+        * @return Name of target as a string.
+        */
+       const std::string& GetStrTarget() const { return (chantarget ? chantarget->name : usertarget->nick); }
+
+       /** Get user target.
+        * @return User target or NULL if the mode change targets a channel.
+        */
+       User* GetUserTarget() const { return usertarget; }
+
+       /** Get channel target.
+        * @return Channel target or NULL if the mode change targets a user.
+        */
+       Channel* GetChanTarget() const { return chantarget; }
+};
+
+/** TOPIC message.
+ */
+struct ClientProtocol::Messages::Topic : public ClientProtocol::Message
+{
+       /** Constructor.
+        * @param source User changing the topic.
+        * @param chan Channel the topic is being changed on.
+        * @param newtopic New topic. May be empty, must remain valid as long as this object is alive.
+        */
+       Topic(User* source, const Channel* chan, const std::string& newtopic)
+               : ClientProtocol::Message("TOPIC", source)
+       {
+               PushParamRef(chan->name);
+               PushParamRef(newtopic);
+       }
+};
+
+/** PRIVMSG and NOTICE message.
+ */
+class ClientProtocol::Messages::Privmsg : public ClientProtocol::Message
+{
+       void PushTargetChan(char status, const Channel* targetchan)
+       {
+               if (status)
+               {
+                       std::string rawtarget(1, status);
+                       rawtarget.append(targetchan->name);
+                       PushParam(rawtarget);
+               }
+               else
+               {
+                       PushParamRef(targetchan->name);
+               }
+       }
+
+       void PushTargetUser(const User* targetuser)
+       {
+               if (targetuser->registered & REG_NICK)
+                       PushParamRef(targetuser->nick);
+               else
+                       PushParam("*");
+       }
+
+ public:
+       /** Used to differentiate constructors that copy the text from constructors that do not.
+        */
+       enum NoCopy { nocopy };
+
+       /** Get command name from MessageType.
+        * @param mt Message type to get command name for.
+        * @return Command name for the message type.
+        */
+       static const char* CommandStrFromMsgType(MessageType mt)
+       {
+               return ((mt == MSG_PRIVMSG) ? "PRIVMSG" : "NOTICE");
+       }
+
+       /** Constructor, user source, string target, copies text.
+        * @param source Source user.
+        * @param target Privmsg target string.
+        * @param text Privmsg text, will be copied.
+        * @param mt Message type.
+        */
+       Privmsg(User* source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushParam(target);
+               PushParam(text);
+       }
+
+       /** Constructor, user source, user target, copies text.
+        * @param source Source user.
+        * @param targetchan Target channel.
+        * @param text Privmsg text, will be copied.
+        * @param mt Message type.
+        * @param status Prefix character for status messages. If non-zero the message is a status message. Optional, defaults to 0.
+        */
+       Privmsg(User* source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushTargetChan(status, targetchan);
+               PushParam(text);
+       }
+
+       /** Constructor, user source, user target, copies text.
+        * @param source Source user.
+        * @param targetuser Target user.
+        * @param text Privmsg text, will be copied.
+        * @param mt Message type.
+        */
+       Privmsg(User* source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushTargetUser(targetuser);
+               PushParam(text);
+       }
+
+       /** Constructor, string source, string target, copies text.
+        * @param source Source user.
+        * @param target Target string.
+        * @param text Privmsg text, will be copied.
+        * @param mt Message type.
+        */
+       Privmsg(const std::string& source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushParam(target);
+               PushParam(text);
+       }
+
+       /** Constructor, string source, channel target, copies text.
+        * @param source Source string.
+        * @param targetchan Target channel.
+        * @param text Privmsg text, will be copied.
+        * @param status Prefix character for status messages. If non-zero the message is a status message. Optional, defaults to 0.
+        * @param mt Message type.
+        */
+       Privmsg(const std::string& source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushTargetChan(status, targetchan);
+               PushParam(text);
+       }
+
+       /** Constructor, string source, user target, copies text.
+        * @param source Source string.
+        * @param targetuser Target user.
+        * @param text Privmsg text, will be copied.
+        * @param mt Message type.
+        */
+       Privmsg(const std::string& source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushTargetUser(targetuser);
+               PushParam(text);
+       }
+
+       /** Constructor, user source, string target, copies text.
+        * @param source Source user.
+        * @param target Target string.
+        * @param text Privmsg text, will not be copied.
+        * @param mt Message type.
+        */
+       Privmsg(NoCopy, User* source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushParam(target);
+               PushParamRef(text);
+       }
+
+       /** Constructor, user source, channel target, does not copy text.
+        * @param source Source user.
+        * @param targetchan Target channel.
+        * @param text Privmsg text, will not be copied.
+        * @param status Prefix character for status messages. If non-zero the message is a status message. Optional, defaults to 0.
+        * @param mt Message type.
+        */
+       Privmsg(NoCopy, User* source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushTargetChan(status, targetchan);
+               PushParamRef(text);
+       }
+
+       /** Constructor, user source, user target, does not copy text.
+        * @param source Source user.
+        * @param targetuser Target user.
+        * @param text Privmsg text, will not be copied.
+        * @param mt Message type.
+        */
+       Privmsg(NoCopy, User* source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushTargetUser(targetuser);
+               PushParamRef(text);
+       }
+
+       /** Constructor, string source, string target, does not copy text.
+        * @param source Source string.
+        * @param target Target string.
+        * @param text Privmsg text, will not be copied.
+        * @param mt Message type.
+        */
+       Privmsg(NoCopy, const std::string& source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushParam(target);
+               PushParamRef(text);
+       }
+
+       /** Constructor, string source, channel target, does not copy text.
+        * @param source Source string.
+        * @param targetchan Target channel.
+        * @param text Privmsg text, will not be copied.
+        * @param status Prefix character for status messages. If non-zero the message is a status message. Optional, defaults to 0.
+        * @param mt Message type.
+        */
+       Privmsg(NoCopy, const std::string& source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushTargetChan(status, targetchan);
+               PushParamRef(text);
+       }
+
+       /** Constructor, string source, user target, does not copy text.
+        * @param source Source string.
+        * @param targetuser Target user.
+        * @param text Privmsg text, will not be copied.
+        * @param mt Message type.
+        */
+       Privmsg(NoCopy, const std::string& source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG)
+               : ClientProtocol::Message(CommandStrFromMsgType(mt), source)
+       {
+               PushTargetUser(targetuser);
+               PushParamRef(text);
+       }
+};
+
+/** INVITE message.
+ * Sent when a user is invited to join a channel.
+ */
+struct ClientProtocol::Messages::Invite : public ClientProtocol::Message
+{
+       /** Constructor.
+        * @param source User inviting the target user.
+        * @param target User being invited by source.
+        * @param chan Channel the target user is being invited to.
+        */
+       Invite(User* source, User* target, Channel* chan)
+               : ClientProtocol::Message("INVITE", source)
+       {
+               PushParamRef(target->nick);
+               PushParamRef(chan->name);
+       }
+};
+
+/** PING message.
+ * Used to check if a connection is still alive.
+ */
+struct ClientProtocol::Messages::Ping : public ClientProtocol::Message
+{
+       /** Constructor.
+        * The ping cookie is the name of the local server.
+        */
+       Ping()
+               : ClientProtocol::Message("PING")
+       {
+               PushParamRef(ServerInstance->Config->ServerName);
+       }
+
+       /** Constructor.
+        * @param cookie Ping cookie. Must remain valid as long as this object is alive.
+        */
+       Ping(const std::string& cookie)
+               : ClientProtocol::Message("PING")
+       {
+               PushParamRef(cookie);
+       }
+};
+
+/** PONG message.
+ * Sent as a reply to PING.
+ */
+struct ClientProtocol::Messages::Pong : public ClientProtocol::Message
+{
+       /** Constructor.
+        * @param cookie Ping cookie. Must remain valid as long as this object is alive.
+        */
+       Pong(const std::string& cookie)
+               : ClientProtocol::Message("PONG", ServerInstance->Config->ServerName)
+       {
+               PushParamRef(ServerInstance->Config->ServerName);
+               PushParamRef(cookie);
+       }
+};
+
+/** ERROR message.
+ * Sent to clients upon disconnection.
+ */
+struct ClientProtocol::Messages::Error : public ClientProtocol::Message
+{
+       /** Constructor.
+        * @param text Error text.
+        */
+       Error(const std::string& text)
+                       : ClientProtocol::Message("ERROR")
+       {
+               PushParam(text);
+       }
+};
index f9e3a740c3012bc3e955f2b6da841600707cdde7..ccea2ac50aa55695dcec1f6ee78d9d3efe1f9ff3 100644 (file)
@@ -20,8 +20,7 @@
  */
 
 
-#ifndef COMMAND_PARSE_H
-#define COMMAND_PARSE_H
+#pragma once
 
 /** This class handles command management and parsing.
  * It allows you to add and remove commands from the map,
  */
 class CoreExport CommandParser
 {
- private:
-       /** Process a parameter string into a list of items
-        * @param command_p The output list of items
-        * @param parameters The input string
-        * @return The number of parameters parsed into command_p
-        */
-       int ProcessParameters(std::vector<std::string>& command_p, char* parameters);
+ public:
+       typedef TR1NS::unordered_map<std::string, Command*, irc::insensitive, irc::StrHashComp> CommandMap;
 
+ private:
        /** Process a command from a user.
-        * @param user The user to parse the command for
-        * @param cmd The command string to process
+        * @param user The user to parse the command for.
+        * @param command The name of the command.
+        * @param parameters The parameters to the command.
         */
-       bool ProcessCommand(LocalUser *user, std::string &cmd);
+       void ProcessCommand(LocalUser* user, std::string& command, CommandBase::Params& parameters);
 
- 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
         * @param user The user to call the handler on behalf of
+        * @param cmd If non-NULL and the command was executed it is set to the command handler,
+        * otherwise it isn't written to.
         * @return This method will return CMD_SUCCESS if the command handler was found and called,
         * and the command completeld successfully. It will return CMD_FAILURE if the command handler was found
         * and called, but the command did not complete successfully, and it will return CMD_INVALID if the
         * command simply did not exist at all or the wrong number of parameters were given, or the user
         * was not privilaged enough to execute the command.
         */
-       CmdResult CallHandler(const std::string &commandname, const std::vector<std::string>& parameters, User *user);
+       CmdResult CallHandler(const std::string& commandname, const CommandBase::Params& parameters, User* user, Command** cmd = NULL);
 
        /** Get the handler function for a command.
         * @param commandname The command required. Always use uppercase for this parameter.
@@ -71,44 +74,50 @@ class CoreExport CommandParser
         */
        Command* GetHandler(const std::string &commandname);
 
-       /** This function returns true if a command is valid with the given number of parameters and user.
-        * @param commandname The command name to check
-        * @param pcnt The parameter count
-        * @param user The user to check against
-        * @return If the user given has permission to execute the command, and the parameter count is
-        * equal to or greater than the minimum number of parameters to the given command, then this
-        * function will return true, otherwise it will return false.
-        */
-       bool IsValidCommand(const std::string &commandname, unsigned int pcnt, User * user);
-
-       /** LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
-        * There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
-        * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
+       /** LoopCall is used to call a command handler repeatedly based on the contents of a comma seperated list.
+        * There are two ways to call this method, either with one potential list or with two potential lists.
+        * We need to handle two potential lists for JOIN, because a JOIN may contain two lists of items at once:
         * the channel names and their keys as follows:
         *
         * JOIN \#chan1,\#chan2,\#chan3 key1,,key3
         *
-        * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
-        * two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
-        * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
-        * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
+        * Therefore, we need to deal with both lists concurrently. If there are two lists then the method reads
+        * them both together until the first runs out of tokens.
+        * 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 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.
+        *
+        * The OnPostCommand hook is executed for each item after it has been processed by the handler, with the
+        * original line parameter being empty (to indicate that the command in that form was created by this function).
+        * This only applies if the user executing the command is local.
+        *
+        * If there are two lists and the second list runs out of tokens before the first list then parameters[extra]
+        * will be an EMPTY string when Handle() is called for the remaining tokens in the first list, even if it is
+        * in the middle of parameters[]! Moreover, empty tokens in the second list are allowed, and those will also
+        * result in the appropiate entry being empty in parameters[].
+        * This is different than what command handlers usually expect; the command parser only allows an empty param
+        * as the last item in the vector.
         *
         * @param user The user who sent the command
-        * @param CommandObj the command object to call for each parameter in the list
-        * @param parameters Parameter list as an array of array of char (that's not a typo).
+        * @param handler The command handler to call for each parameter in the list
+        * @param parameters Parameter list as a vector of strings
         * @param splithere The first parameter index to split as a comma seperated list
-        * @param extra The second parameter index to split as a comma seperated list
-        * @param usemax Limit the command to MaxTargets targets
-        * @return This function will return 1 when there are no more parameters to process. When this occurs, its
-        * caller should return without doing anything, otherwise it should continue into its main section of code.
+        * @param extra The second parameter index to split as a comma seperated list, or -1 (the default) if there is only one list
+        * @param usemax True to limit the command to MaxTargets targets (default), or false to process all tokens
+        * @return This function returns true when it identified a list in the given parameter and finished calling the
+        * command handler for each entry on the list. When this occurs, the caller should return without doing anything,
+        * otherwise it should continue into its main section of code.
         */
-       int LoopCall(User* user, Command* CommandObj, const std::vector<std::string>& parameters, unsigned int splithere, int extra = -1, bool usemax = true);
+       static bool LoopCall(User* user, Command* handler, const CommandBase::Params& parameters, unsigned int splithere, int extra = -1, bool usemax = true);
 
        /** Take a raw input buffer from a recvq, and process it on behalf of a user.
         * @param buffer The buffer line to process
         * @param user The user to whom this line belongs
         */
-       bool ProcessBuffer(std::string &buffer,LocalUser *user);
+       void ProcessBuffer(LocalUser* user, const std::string& buffer);
 
        /** Add a new command to the commands hash
         * @param f The new Command to add to the list
@@ -120,50 +129,21 @@ class CoreExport CommandParser
         */
        void RemoveCommand(Command* x);
 
-       /** Translate nicknames in a string into UIDs, based on the TranslationType given.
-        * @param to The translation type to use for the process.
-        * @param source The input string
-        * @param dest The output string, it is safe to pass source and dest as the same variable only for translation type TR_TEXT.
-        * @return returns the number of substitutions made. Will always be 0 or 1
+       /** Translate a single item based on the TranslationType given.
+        * @param to The translation type to use for the process
+        * @param item The input string
+        * @param dest The output string. The translation result will be appended to this string
+        * @param custom_translator Used to translate the parameter if the translation type is TR_CUSTOM, if NULL, TR_CUSTOM will act like TR_TEXT
+        * @param paramnumber The index of the parameter we are translating.
         */
-       int TranslateUIDs(TranslateType to, const std::string &source, std::string &dest);
+       static void TranslateSingleParam(TranslateType to, const std::string& item, std::string& dest, CommandBase* custom_translator = NULL, unsigned int paramnumber = 0);
 
        /** Translate nicknames in a list of strings into UIDs, based on the TranslateTypes given.
         * @param to The translation types to use for the process. If this list is too short, TR_TEXT is assumed for the rest.
         * @param source The strings to translate
-        * @param dest The output string
         * @param prefix_final True if the final source argument should have a colon prepended (if it could contain a space)
-        * @param custom_translator Used to translate the parameter if the TR_CUSTOM type is found in to
-        * @return returns the number of substitutions made.
+        * @param custom_translator Used to translate the parameter if the translation type is TR_CUSTOM, if NULL, TR_CUSTOM will act like TR_TEXT
+        * @return dest The output string
         */
-       int TranslateUIDs(const std::vector<TranslateType> to, const std::vector<std::string> &source, std::string &dest, bool prefix_final = false, Command* custom_translator = NULL);
+       static std::string TranslateUIDs(const std::vector<TranslateType>& to, const CommandBase::Params& source, bool prefix_final = false, CommandBase* custom_translator = NULL);
 };
-
-/** A lookup table of values for multiplier characters used by
- * InspIRCd::Duration(). In this lookup table, the indexes for
- * the ascii values 'm' and 'M' have the value '60', the indexes
- * for the ascii values 'D' and 'd' have a value of '86400', etc.
- */
-const int duration_multi[] =
-{
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 86400, 1, 1, 1, 3600,
-       1, 1, 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       604800, 1, 31557600, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 86400, 1, 1, 1, 3600, 1, 1, 1, 1, 60,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 604800, 1, 31557600,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
-};
-
-#endif
index d333541228903ae722d7d58d9f9928093bd10053..2447024a1e9d1fa08d782a87ba7c10fd512410c6 100644 (file)
  */
 
 
-#ifndef CMD_WHOWAS_H
-#define CMD_WHOWAS_H
+#pragma once
+
 #include "modules.h"
 
-struct WhowasRequest : public Request
+namespace WhoWas
 {
-       /* list of available internal commands */
-       enum Internals
+       /** One entry for a nick. There may be multiple entries for a nick.
+        */
+       struct Entry
        {
-               WHOWAS_ADD = 1,
-               WHOWAS_STATS = 2,
-               WHOWAS_PRUNE = 3,
-               WHOWAS_MAINTAIN = 4
-       };
+               /** Real host
+                */
+               const std::string host;
 
-       const Internals type;
-       std::string value;
-       User* user;
+               /** Displayed host
+                */
+               const std::string dhost;
 
-       WhowasRequest(Module* src, Module* whowas, Internals Type) : Request(src, whowas, "WHOWAS"), type(Type)
-       {}
-};
+               /** Ident
+                */
+               const std::string ident;
 
-/* Forward ref for timer */
-class WhoWasMaintainTimer;
+               /** Server name
+                */
+               const std::string server;
 
-/* Forward ref for typedefs */
-class WhoWasGroup;
+               /** Real name
+                */
+               const std::string real;
 
-/** Timer that is used to maintain the whowas list, called once an hour
- */
-extern WhoWasMaintainTimer* timer;
+               /** Signon time
               */
+               const time_t signon;
 
-/** A group of users related by nickname
- */
-typedef std::deque<WhoWasGroup*> whowas_set;
+               /** Initialize this Entry with a user
+                */
+               Entry(User* user);
+       };
 
-/** Sets of users in the whowas system
- */
-typedef std::map<irc::string,whowas_set*> whowas_users;
+       /** 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;
 
-/** Sets of time and users in whowas list
- */
-typedef std::deque<std::pair<time_t,irc::string> > whowas_users_fifo;
+               /** 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
@@ -71,71 +192,16 @@ typedef std::deque<std::pair<time_t,irc::string> > whowas_users_fifo;
  */
 class CommandWhowas : public Command
 {
-  private:
-       /** Whowas container, contains a map of vectors of users tracked by WHOWAS
-        */
-       whowas_users whowas;
-
-       /** Whowas container, contains a map of time_t to users tracked by WHOWAS
+  public:
+       /** Manager handling all whowas database related tasks
         */
-       whowas_users_fifo whowas_fifo;
+       WhoWas::Manager manager;
 
-  public:
        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 PruneWhoWas(time_t t);
-       void MaintainWhoWas(time_t t);
-       ~CommandWhowas();
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
-
-/** 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);
-       /** Destructor
-        */
-       ~WhoWasGroup();
-};
-
-class WhoWasMaintainTimer : public Timer
-{
-  public:
-       WhoWasMaintainTimer(long interval)
-       : Timer(interval, ServerInstance->Time(), true)
-       {
-       }
-       virtual void Tick(time_t TIME);
-};
-
-#endif
diff --git a/include/compat.h b/include/compat.h
new file mode 100644 (file)
index 0000000..7a95e90
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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
+
+/**
+ * Some implementations of the C++11 standard library are incomplete so we use
+ * the implementation of the same types from C++ Technical Report 1 instead.
+ */
+#if defined _LIBCPP_VERSION || defined _WIN32
+# define TR1NS std
+# include <array>
+# include <functional>
+# include <unordered_map>
+# include <type_traits>
+#else
+# define TR1NS std::tr1
+# include <tr1/array>
+# include <tr1/functional>
+# include <tr1/unordered_map>
+# include <tr1/type_traits>
+#endif
+
+/**
+ * This macro enables the compile-time checking of printf format strings. This
+ * makes the compiler show a warning if the format of a printf arguments are
+ * incorrect.
+ */
+#if defined __clang__ || defined __GNUC__
+# define CUSTOM_PRINTF(stringpos, firstpos) __attribute__((format(printf, stringpos, firstpos)))
+#else
+# define CUSTOM_PRINTF(stringpos, firstpos)
+#endif
+
+/**
+ * These macros enable the use of the C++11 override control keywords in
+ * compilers which support them.
+ */
+#if __cplusplus >= 201103L
+# define HAS_CXX11_FINAL_OVERRIDE
+#elif defined __clang__
+# if __has_feature(cxx_override_control)
+#  define HAS_CXX11_FINAL_OVERRIDE
+# endif
+#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
+# if defined __GXX_EXPERIMENTAL_CXX0X__
+#  define HAS_CXX11_FINAL_OVERRIDE
+# endif
+#elif _MSC_VER >= 1700
+# define HAS_CXX11_FINAL_OVERRIDE
+#endif
+
+#if defined HAS_CXX11_FINAL_OVERRIDE
+# define CXX11_FINAL final
+# define CXX11_OVERRIDE override
+#else
+# define CXX11_FINAL
+# define CXX11_OVERRIDE
+#endif
+
+/**
+ * This macro allows methods to be marked as deprecated. To use this, wrap the
+ * method declaration in the header file with the macro.
+ */
+#if defined __clang__ || defined __GNUC__
+# define DEPRECATED_METHOD(function) function __attribute__((deprecated))
+#elif defined _MSC_VER
+# define DEPRECATED_METHOD(function) __declspec(deprecated) function
+#else
+# define DEPRECATED_METHOD(function) function
+#endif
+
+/**
+ * Windows is very different to UNIX so we have to wrap certain features in
+ * order to build on Windows correctly.
+ */
+#if defined _WIN32
+# include "inspircd_win32wrapper.h"
+# include "threadengines/threadengine_win32.h"
+#else
+# define ENTRYPOINT int main(int argc, char** argv)
+# define DllExport __attribute__ ((visibility ("default")))
+# define CoreExport __attribute__ ((visibility ("default")))
+# include <unistd.h>
+# include "threadengines/threadengine_pthread.h"
+#endif
index 999d79e2460d452122e290e2c82f47c6fdded4c1..680f11a61763d0995753bcccfaeb8730581b0acb 100644 (file)
  */
 
 
-struct fpos
-{
-       std::string filename;
-       int line;
-       int col;
-       fpos(const std::string& name, int l = 1, int c = 1) : filename(name), line(l), col(c) {}
-       std::string str()
-       {
-               return filename + ":" + ConvToStr(line) + ":" + ConvToStr(col);
-       }
-};
-
-enum ParseFlags
-{
-       FLAG_USE_XML = 1,
-       FLAG_NO_EXEC = 2,
-       FLAG_NO_INC = 4
-};
+#pragma once
 
 struct ParseStack
 {
        std::vector<std::string> reading;
-       std::map<std::string, std::string> vars;
+       insp::flat_map<std::string, std::string, irc::insensitive_swo> vars;
        ConfigDataHash& output;
        ConfigFileCache& FilesOutput;
        std::stringstream& errstr;
@@ -51,30 +34,7 @@ struct ParseStack
                vars["quot"] = "\"";
                vars["newline"] = vars["nl"] = "\n";
        }
-       bool ParseFile(const std::string& name, int flags, const std::string& mandatory_tag = "");
-       bool ParseExec(const std::string& name, int flags, const std::string& mandatory_tag = "");
+       bool ParseFile(const std::string& name, int flags, const std::string& mandatory_tag = std::string(), bool isexec = false);
        void DoInclude(ConfigTag* includeTag, int flags);
        void DoReadFile(const std::string& key, const std::string& file, int flags, bool exec);
 };
-
-/** RAII wrapper on FILE* to close files on exceptions */
-struct FileWrapper
-{
-       FILE* const f;
-       bool close_with_pclose;
-       FileWrapper(FILE* file, bool use_pclose = false) : f(file), close_with_pclose(use_pclose) {}
-       operator bool() { return (f != NULL); }
-       operator FILE*() { return f; }
-       ~FileWrapper()
-       {
-               if (f)
-               {
-                       if (close_with_pclose)
-                               pclose(f);
-                       else
-                               fclose(f);
-               }
-       }
-};
-
-
index 4a697d05c314a1edbe5de12ec9240978f2cb3b43..511bedbee2618ecfb42977844d4455aed4096939 100644 (file)
@@ -21,8 +21,7 @@
  */
 
 
-#ifndef INSPIRCD_CONFIGREADER
-#define INSPIRCD_CONFIGREADER
+#pragma once
 
 #include <sstream>
 #include <string>
 #include "modules.h"
 #include "socketengine.h"
 #include "socket.h"
+#include "token_list.h"
 
 /** Structure representing a single \<tag> in config */
 class CoreExport ConfigTag : public refcountbase
 {
-       std::vector<KeyVal> items;
+       ConfigItems items;
  public:
        const std::string tag;
        const std::string src_name;
        const int src_line;
 
        /** Get the value of an option, using def if it does not exist */
-       std::string getString(const std::string& key, const std::string& def = "");
+       std::string getString(const std::string& key, const std::string& def, const TR1NS::function<bool(const std::string&)>& validator);
        /** Get the value of an option, using def if it does not exist */
-       long getInt(const std::string& key, long def = 0);
+       std::string getString(const std::string& key, const std::string& def = "", size_t minlen = 0, size_t maxlen = UINT32_MAX);
        /** Get the value of an option, using def if it does not exist */
-       double getFloat(const std::string& key, double def = 0);
+       long getInt(const std::string& key, long def, long min = LONG_MIN, long max = LONG_MAX);
+       /** Get the value of an option, using def if it does not exist */
+       unsigned long getUInt(const std::string& key, unsigned long def, unsigned long min = 0, unsigned long max = ULONG_MAX);
+       /** Get the value of an option, using def if it does not exist */
+       double getFloat(const std::string& key, double def, double min = DBL_MIN, double max = DBL_MAX);
        /** Get the value of an option, using def if it does not exist */
        bool getBool(const std::string& key, bool def = false);
 
+       /** Get the value in seconds of a duration that is in the user-friendly "1h2m3s" format,
+        * using a default value if it does not exist or is out of bounds.
+        * @param key The config key name
+        * @param def Default value (optional)
+        * @param min Minimum acceptable value (optional)
+        * @param max Maximum acceptable value (optional)
+        * @return The duration in seconds
+        */
+       unsigned long getDuration(const std::string& key, unsigned long def, unsigned long min = 0, unsigned long max = ULONG_MAX);
+
        /** Get the value of an option
         * @param key The option to get
         * @param value The location to store the value (unmodified if does not exist)
@@ -61,10 +75,10 @@ class CoreExport ConfigTag : public refcountbase
 
        std::string getTagLocation();
 
-       inline const std::vector<KeyVal>& getItems() const { return items; }
+       inline const ConfigItems& getItems() const { return items; }
 
-       /** Create a new ConfigTag, giving access to the private KeyVal item list */
-       static ConfigTag* create(const std::string& Tag, const std::string& file, int line, std::vector<KeyVal>*& Items);
+       /** Create a new ConfigTag, giving access to the private ConfigItems item list */
+       static ConfigTag* create(const std::string& Tag, const std::string& file, int line, ConfigItems*& Items);
  private:
        ConfigTag(const std::string& Tag, const std::string& file, int line);
 };
@@ -89,18 +103,22 @@ class ServerLimits
        size_t MaxTopic;
        /** Maximum kick message length */
        size_t MaxKick;
-       /** Maximum GECOS (real name) length */
-       size_t MaxGecos;
+       /** Maximum real name length */
+       size_t MaxReal;
        /** Maximum away message length */
        size_t MaxAway;
+       /** Maximum line length */
+       size_t MaxLine;
+       /** 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)
-       {
-       }
+       ServerLimits(ConfigTag* tag);
+
+       /** Maximum length of a n!u\@h mask */
+       size_t GetMaxMask() const { return NickMax + 1 + IdentMax + 1 + MaxHost; }
 };
 
 struct CommandLineConf
@@ -130,10 +148,17 @@ struct CommandLineConf
         */
        bool writelog;
 
-       /** True if we have been told to run the testsuite from the commandline,
-        * rather than entering the mainloop.
+       /** If this is true, a PID file will be written
+        * to the file given in the "file" variable of
+        * the \<pid> tag in the config file. This is
+        * the default.
+        * Passing --nopid as a command line argument
+        * sets this to false; in this case, a PID file
+        * will not be written, even the default PID
+        * file that is usually written when the \<pid>
+        * tag is not defined in the config file.
         */
-       bool TestSuite;
+       bool writepid;
 
        /** Saved argc from startup
         */
@@ -142,15 +167,13 @@ struct CommandLineConf
        /** Saved argv from startup
         */
        char** argv;
-
-       std::string startup_log;
 };
 
 class CoreExport OperInfo : public refcountbase
 {
  public:
-       std::set<std::string> AllowedOperCommands;
-       std::set<std::string> AllowedPrivs;
+       TokenList AllowedOperCommands;
+       TokenList AllowedPrivs;
 
        /** Allowed user modes from oper classes. */
        std::bitset<64> AllowedUserModes;
@@ -167,14 +190,14 @@ class CoreExport OperInfo : public refcountbase
        /** Name of the oper type; i.e. the one shown in WHOIS */
        std::string name;
 
+       /** Creates a new OperInfo with the specified oper type name.
+        * @param Name The name of the oper type.
+        */
+       OperInfo(const std::string& Name);
+
        /** Get a configuration item, searching in the oper, type, and class blocks (in that order) */
        std::string getConfig(const std::string& key);
        void init();
-
-       inline const char* NameStr()
-       {
-               return irc::Spacify(name.c_str());
-       }
 };
 
 /** This class holds the bulk of the runtime configuration for the ircd.
@@ -189,12 +212,60 @@ class CoreExport ServerConfig
        void CrossCheckConnectBlocks(ServerConfig* current);
 
  public:
+       /** How to treat a user in a channel who is banned. */
+       enum BannedUserTreatment
+       {
+               /** Don't treat a banned user any different to normal. */
+               BUT_NORMAL,
+
+               /** Restrict the actions of a banned user. */
+               BUT_RESTRICT_SILENT,
+
+               /** Restrict the actions of a banned user and notify them of their treatment. */
+               BUT_RESTRICT_NOTIFY
+       };
+
+       class ServerPaths
+       {
+        public:
+               /** Config path */
+               std::string Config;
+
+               /** Data path */
+               std::string Data;
+
+               /** Log path */
+               std::string Log;
+
+               /** Module path */
+               std::string Module;
+
+               ServerPaths(ConfigTag* tag);
 
-       /** Get a configuration tag
-        * @param tag The name of the tag to get
+               std::string PrependConfig(const std::string& fn) const { return FileSystem::ExpandPath(Config, fn); }
+               std::string PrependData(const std::string& fn) const { return FileSystem::ExpandPath(Data, fn); }
+               std::string PrependLog(const std::string& fn) const { return FileSystem::ExpandPath(Log, fn); }
+               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 by name. If one or more tags are present then the first is returned.
+        * @param tag The name of the tag to get.
+        * @returns Either a tag from the config or EmptyTag.
         */
        ConfigTag* ConfValue(const std::string& tag);
 
+       /** Get a list of configuration tags by name.
+        * @param tag The name of the tags to get.
+        * @returns Either a list of tags from the config or an empty ConfigTagList.
+        */
        ConfigTagList ConfTags(const std::string& tag);
 
        /** An empty configuration tag. */
@@ -210,11 +281,7 @@ class CoreExport ServerConfig
        /** Bind to IPv6 by default */
        bool WildcardIPv6;
 
-       /** Used to indicate who we announce invites to on a channel */
-       enum InviteAnnounceState { INVITE_ANNOUNCE_NONE, INVITE_ANNOUNCE_ALL, INVITE_ANNOUNCE_OPS, INVITE_ANNOUNCE_DYNAMIC };
-       enum OperSpyWhoisState { SPYWHOIS_NONE, SPYWHOIS_SINGLEMSG, SPYWHOIS_SPLITMSG };
-
-       /** This holds all the information in the config file,
+       /** This holds all the information in the config file,
         * it's indexed by tag name to a vector of key/values.
         */
        ConfigDataHash config_data;
@@ -228,6 +295,9 @@ class CoreExport ServerConfig
         */
        ServerLimits Limits;
 
+       /** Locations of various types of file (config, module, etc). */
+       ServerPaths Paths;
+
        /** Configuration parsed from the command line.
         */
        CommandLineConf cmdline;
@@ -235,34 +305,21 @@ class CoreExport ServerConfig
        /** Clones CIDR range for ipv4 (0-32)
         * Defaults to 32 (checks clones on all IPs seperately)
         */
-       int c_ipv4_range;
+       unsigned char c_ipv4_range;
 
        /** Clones CIDR range for ipv6 (0-128)
         * Defaults to 128 (checks on all IPs seperately)
         */
-       int c_ipv6_range;
-
-       /** Max number of WhoWas entries per user.
-        */
-       int WhoWasGroupSize;
-
-       /** Max number of cumulative user-entries in WhoWas.
-        *  When max reached and added to, push out oldest entry FIFO style.
-        */
-       int WhoWasMaxGroups;
-
-       /** Max seconds a user is kept in WhoWas before being pruned.
-        */
-       int WhoWasMaxKeep;
+       unsigned char c_ipv6_range;
 
        /** Holds the server name of the local server
         * as defined by the administrator.
         */
        std::string ServerName;
 
-       /** Notice to give to users when they are Xlined
+       /** Notice to give to users when they are banned by an XLine
         */
-       std::string MoronBanner;
+       std::string XLineMessage;
 
        /* Holds the network name the local server
         * belongs to. This is an arbitary field defined
@@ -275,117 +332,8 @@ class CoreExport ServerConfig
         */
        std::string ServerDesc;
 
-       /** Holds the admin's name, for output in
-        * the /ADMIN command.
-        */
-       std::string AdminName;
-
-       /** Holds the email address of the admin,
-        * for output in the /ADMIN command.
-        */
-       std::string AdminEmail;
-
-       /** Holds the admin's nickname, for output
-        * in the /ADMIN command
-        */
-       std::string AdminNick;
-
-       /** The admin-configured /DIE password
-        */
-       std::string diepass;
-
-       /** The admin-configured /RESTART password
-        */
-       std::string restartpass;
-
-       /** The hash method for *BOTH* the die and restart passwords.
-        */
-       std::string powerhash;
-
-       /** The pathname and filename of the message of the
-        * day file, as defined by the administrator.
-        */
-       std::string motd;
-
-       /** The pathname and filename of the rules file,
-        * as defined by the administrator.
-        */
-       std::string rules;
-
-       /** The quit prefix in use, or an empty string
-        */
-       std::string PrefixQuit;
-
-       /** The quit suffix in use, or an empty string
-        */
-       std::string SuffixQuit;
-
-       /** The fixed quit message in use, or an empty string
-        */
-       std::string FixedQuit;
-
-       /** The part prefix in use, or an empty string
-        */
-       std::string PrefixPart;
-
-       /** The part suffix in use, or an empty string
-        */
-       std::string SuffixPart;
-
-       /** The fixed part message in use, or an empty string
-        */
-       std::string FixedPart;
-
-       /** The DNS server to use for DNS queries
-        */
-       std::string DNSServer;
-
-       /** Pretend disabled commands don't exist.
-        */
-       bool DisabledDontExist;
-
-       /** This variable contains a space-seperated list
-        * of commands which are disabled by the
-        * administrator of the server for non-opers.
-        */
-       std::string DisabledCommands;
-
-       /** This variable identifies which usermodes have been diabled.
-        */
-       char DisabledUModes[64];
-
-       /** This variable identifies which chanmodes have been disabled.
-        */
-       char DisabledCModes[64];
-
-       /** The full path to the modules directory.
-        * This is either set at compile time, or
-        * overridden in the configuration file via
-        * the \<path> tag.
-        */
-       std::string ModPath;
-
-       /** If set to true, then all opers on this server are
-        * shown with a generic 'is an IRC operator' line rather
-        * than the oper type. Oper types are still used internally.
-        */
-       bool GenericOper;
-
-       /** If this value is true, banned users (+b, not extbans) will not be able to change nick
-        * if banned on any channel, nor to message them.
-        */
-       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.
-        */
-       int dns_timeout;
+       /** How to treat a user in a channel who is banned. */
+       BannedUserTreatment RestrictBannedUsers;
 
        /** The size of the read() buffer in the user
         * handling code, used to read data into a user's
@@ -398,6 +346,13 @@ class CoreExport ServerConfig
         */
        int MaxConn;
 
+       /** If we should check for clones during CheckClass() in AddUser()
+        * Setting this to false allows to not trigger on maxclones for users
+        * that may belong to another class after DNS-lookup is complete.
+        * It does, however, make the server spend more time on users we may potentially not want.
+        */
+       bool CCOnConnect;
+
        /** The soft limit value assigned to the irc server.
         * The IRC server will not allow more than this
         * number of local users.
@@ -409,38 +364,19 @@ class CoreExport ServerConfig
         */
        unsigned int MaxTargets;
 
-       /** True if we're going to hide netsplits as *.net *.split for non-opers
-        */
-       bool HideSplits;
+       /** The number of seconds that the server clock can skip by before server operators are warned. */
+       time_t TimeSkipWarn;
 
-       /** True if we're going to hide ban reasons for non-opers (e.g. G-Lines,
-        * K-Lines, Z-Lines)
+       /** True if we're going to hide ban reasons for non-opers (e.g. G-lines,
+        * K-lines, Z-lines)
         */
        bool HideBans;
 
-       /** Announce invites to the channel with a server notice
-        */
-       InviteAnnounceState AnnounceInvites;
-
-       /** If this is enabled then operators will
-        * see invisible (+i) channels in /whois.
-        */
-       OperSpyWhoisState OperSpyWhois;
-
        /** True if raw I/O is being logged */
        bool RawLog;
 
-       /** Set to a non-empty string to obfuscate the server name of users in WHOIS
-        */
-       std::string HideWhoisServer;
-
-       /** Set to a non empty string to obfuscate nicknames prepended to a KILL.
-        */
-       std::string HideKillsServer;
-
-       /** Set to hide kills from clients of ulined servers in snotices.
-        */
-       bool HideULineKills;
+       /** Set to a non-empty string to obfuscate server names. */
+       std::string HideServer;
 
        /** The full pathname and filename of the PID
         * file as defined in the configuration.
@@ -451,21 +387,6 @@ class CoreExport ServerConfig
         */
        ClassVector Classes;
 
-       /** The 005 tokens of this server (ISUPPORT)
-        * populated/repopulated upon loading or unloading
-        * modules.
-        */
-       std::string data005;
-
-       /** isupport strings
-        */
-       std::vector<std::string> isupport;
-
-       /** STATS characters in this list are available
-        * only to operators.
-        */
-       std::string UserStats;
-
        /** Default channel modes
         */
        std::string DefaultModes;
@@ -474,52 +395,32 @@ class CoreExport ServerConfig
         */
        std::string CustomVersion;
 
-       /** List of u-lined servers
-        */
-       std::map<irc::string, bool> ulines;
-
-       /** Max banlist sizes for channels (the std::string is a glob)
-        */
-       std::map<std::string, int> maxbans;
-
-       /** If set to true, no user DNS lookups are to be performed
-        */
-       bool NoUserDns;
-
        /** If set to true, provide syntax hints for unknown commands
         */
        bool SyntaxHints;
 
-       /** If set to true, users appear to quit then rejoin when their hosts change.
-        * This keeps clients synchronized properly.
+       /** The name of the casemapping method used by this server.
         */
-       bool CycleHosts;
-
-       /** If set to true, the CycleHosts mode change will be sourced from the user,
-        * rather than the server
-        */
-       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;
+       std::string CaseMapping;
 
        /** 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.
         */
        bool FullHostInTopic;
 
-       /** Oper block and type index.
-        * For anonymous oper blocks (type only), prefix with a space.
+       /** Oper blocks keyed by their name
         */
        OperIndex oper_blocks;
 
-       /** Max channels per user
+       /** Oper types keyed by their name
+        */
+       OperIndex OperTypes;
+
+       /** Default value for <connect:maxchans>, deprecated in 3.0
         */
        unsigned int MaxChans;
 
-       /** Oper max channels per user
+       /** Default value for <oper:maxchans>, deprecated in 3.0
         */
        unsigned int OperMaxChans;
 
@@ -538,15 +439,7 @@ class CoreExport ServerConfig
 
        /** Get server ID as string with required leading zeroes
         */
-       const std::string& GetSID();
-
-       /** Update the 005 vector
-        */
-       void Update005();
-
-       /** Send the 005 numerics (ISUPPORT) to a user
-        */
-       void Send005(User* user);
+       const std::string& GetSID() const { return sid; }
 
        /** Read the entire configuration into memory
         * and initialize this class. All other methods
@@ -561,36 +454,15 @@ class CoreExport ServerConfig
 
        void Fill();
 
-       /** Returns true if the given string starts with a windows drive letter
-        */
-       bool StartsWithWindowsDriveLetter(const std::string &path);
-
-       bool ApplyDisabledCommands(const std::string& data);
-
-       /** Clean a filename, stripping the directories (and drives) from string.
-        * @param name Directory to tidy
-        * @return The cleaned filename
-        */
-       static const char* CleanFilename(const char* name);
-
-       /** Check if a file exists.
-        * @param file The full path to a file
-        * @return True if the file exists and is readable.
-        */
-       static bool FileExists(const char* file);
-
-       /** If this value is true, invites will bypass more than just +i
+       /** Escapes a value for storage in a configuration key.
+        * @param str The string to escape.
+        * @param xml Are we using the XML config format?
         */
-       bool InvBypassModes;
+       static std::string Escape(const std::string& str, bool xml = true);
 
        /** If this value is true, snotices will not stack when repeats are sent
         */
        bool NoSnoticeStack;
-
-       /** If true, a "Welcome to <networkname>!" NOTICE will be sent to
-        * connecting users
-        */
-       bool WelcomeNotice;
 };
 
 /** The background thread for config reading, so that reading from executable includes
@@ -612,10 +484,19 @@ class CoreExport ConfigReaderThread : public Thread
                delete Config;
        }
 
-       void Run();
+       void Run() CXX11_OVERRIDE;
        /** Run in the main thread to apply the configuration */
        void Finish();
        bool IsDone() { return done; }
 };
 
-#endif
+class CoreExport ConfigStatus
+{
+ public:
+       User* const srcuser;
+
+       ConfigStatus(User* user = NULL)
+               : srcuser(user)
+       {
+       }
+};
index f7ca1335ed244f16514d457d4fa035971fab2996..8d154f7ee5eead60d71fcda19038b9c1a01b4fb4 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef CONSOLECOLORS_H
-#define CONSOLECOLORS_H
+
+#pragma once
 
 #include <ostream>
+#include <stdio.h>
+
+#ifdef _WIN32
+# include <io.h>
+# define isatty(x) _isatty((x))
+# define fileno(x) _fileno((x))
+#else
+# include <unistd.h>
+#endif
+
+namespace
+{
+       inline bool CanUseColors()
+       {
+#ifdef INSPIRCD_DISABLE_COLORS
+               return false;
+#else
+               return isatty(fileno(stdout));
+#endif
+       }
+}
 
 #ifdef _WIN32
 
@@ -29,72 +50,88 @@ extern HANDLE g_hStdout;
 
 inline std::ostream& con_green(std::ostream &s)
 {
-    SetConsoleTextAttribute(g_hStdout, FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor);
-    return s;
+       if (CanUseColors())
+               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;
+       if (CanUseColors())
+               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;
+       if (CanUseColors())
+               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;
+       if (CanUseColors())
+               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;
+       if (CanUseColors())
+               SetConsoleTextAttribute(g_hStdout, FOREGROUND_INTENSITY|g_wBackgroundColor);
+       return s;
 }
 
 inline std::ostream& con_reset(std::ostream &s)
 {
-    SetConsoleTextAttribute(g_hStdout, g_wOriginalColors);
-    return s;
+       if (CanUseColors())
+               SetConsoleTextAttribute(g_hStdout, g_wOriginalColors);
+       return s;
 }
 
 #else
 
 inline std::ostream& con_green(std::ostream &s)
 {
-    return s << "\033[1;32m";
+       if (!CanUseColors())
+               return s;
+       return s << "\033[1;32m";
 }
 
 inline std::ostream& con_red(std::ostream &s)
 {
-    return s << "\033[1;31m";
+       if (!CanUseColors())
+               return s;
+       return s << "\033[1;31m";
 }
 
 inline std::ostream& con_white(std::ostream &s)
 {
-    return s << "\033[0m";
+       if (!CanUseColors())
+               return s;
+       return s << "\033[0m";
 }
 
 inline std::ostream& con_white_bright(std::ostream &s)
 {
-    return s << "\033[1m";
+       if (!CanUseColors())
+               return s;
+       return s << "\033[1m";
 }
 
 inline std::ostream& con_bright(std::ostream &s)
 {
-    return s << "\033[1m";
+       if (!CanUseColors())
+               return s;
+       return s << "\033[1m";
 }
 
 inline std::ostream& con_reset(std::ostream &s)
 {
-    return s << "\033[0m";
+       if (!CanUseColors())
+               return s;
+       return s << "\033[0m";
 }
 
 #endif
-
-#endif
diff --git a/include/convto.h b/include/convto.h
new file mode 100644 (file)
index 0000000..2f808d1
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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 a std::string to any numeric type.
+ */
+template<typename TOut> inline TOut ConvToNum(const std::string& in)
+{
+       TOut ret;
+       std::istringstream tmp(in);
+       if (!(tmp >> ret))
+               return 0;
+       return ret;
+}
+
+template<> inline char ConvToNum<char>(const std::string& in)
+{
+       // We specialise ConvToNum for char to avoid istringstream treating
+       // the input as a character literal.
+       uint16_t num = ConvToNum<uint16_t>(in);
+       return num <= UINT8_MAX ? num : 0;
+}
+
+template<> inline unsigned char ConvToNum<unsigned char>(const std::string& in)
+{
+       // We specialise ConvToNum for unsigned char to avoid istringstream
+       // treating the input as a character literal.
+       int16_t num = ConvToNum<int16_t>(in);
+       return num >= INT8_MIN && num <= INT8_MAX ? num : 0;
+}
index f9cd08cb3ae9dc8b209770bb05d1de4d6d7c468c..1c7d5b4bd5dadd1e45d619323ebab6411b71ca72 100644 (file)
  */
 
 
-#ifndef CTABLES_H
-#define CTABLES_H
+#pragma once
 
-/** Used to indicate command success codes
- */
+/** Used to indicate the result of trying to execute a command. */
 enum CmdResult
 {
-       CMD_FAILURE = 0,        /* Command exists, but failed */
-       CMD_SUCCESS = 1,        /* Command exists, and succeeded */
-       CMD_INVALID = 2,        /* Command doesnt exist at all! */
-       CMD_EPERM = 3       /* Command failed because of a permission check */
+       /** The command exists but its execution failed. */
+       CMD_FAILURE = 0,
+
+       /** The command exists and its execution succeeded. */
+       CMD_SUCCESS = 1,
+
+       /* The command does not exist. */
+       CMD_INVALID = 2
 };
 
 /** Flag for commands that are only allowed from servers */
@@ -44,7 +46,6 @@ const char FLAG_SERVERONLY = 7; // technically anything nonzero below 'A' works
  */
 enum TranslateType
 {
-       TR_END,                 /* End of known parameters, everything after this is TR_TEXT */
        TR_TEXT,                /* Raw text, leave as-is */
        TR_NICK,                /* Nickname, translate to UUID for server->server */
        TR_CUSTOM               /* Custom translation handled by EncodeParameter/DecodeParameter */
@@ -77,10 +78,17 @@ struct RouteDescriptor
         */
        std::string serverdest;
 
+       /** For unicast, the destination Server
+        */
+       Server* server;
+
        /** Create a RouteDescriptor
         */
        RouteDescriptor(RouteType t, const std::string &d)
-               : type(t), serverdest(d) { }
+               : type(t), serverdest(d), server(NULL) { }
+
+       RouteDescriptor(RouteType t, Server* srv)
+               : type(t), server(srv) { }
 };
 
 /** Do not route this command */
@@ -99,12 +107,47 @@ struct RouteDescriptor
 /** A structure that defines a command. Every command available
  * in InspIRCd must be defined as derived from Command.
  */
-class CoreExport Command : public ServiceProvider
+class CoreExport CommandBase : public ServiceProvider
 {
  public:
+       /** Encapsulates parameters to a command. */
+       class Params : public std::vector<std::string>
+       {
+        private:
+               /* IRCv3 message tags. */
+               ClientProtocol::TagMap tags;
+
+        public:
+               /** Initializes a new instance from parameter and tag references.
+                * @param paramsref Message parameters.
+                * @param tagsref IRCv3 message tags.
+                */
+               Params(const std::vector<std::string>& paramsref, const ClientProtocol::TagMap& tagsref)
+                       : std::vector<std::string>(paramsref)
+                       , tags(tagsref)
+               {
+               }
+
+               /** Initializes a new instance from parameter iterators.
+                * @param first The first element in the parameter array.
+                * @param last The last element in the parameter array.
+                */
+               template<typename Iterator>
+               Params(Iterator first, Iterator last)
+                       : std::vector<std::string>(first, last)
+               {
+               }
+
+               /** Initializes a new empty instance. */
+               Params() { }
+
+               /** Retrieves the IRCv3 message tags. */
+               const ClientProtocol::TagMap& GetTags() const { return tags; }
+       };
+
        /** User flags needed to execute the command or 0
         */
-       char flags_needed;
+       unsigned char flags_needed;
 
        /** Minimum number of parameters command takes
        */
@@ -120,14 +163,6 @@ class CoreExport Command : public ServiceProvider
         */
        unsigned long use_count;
 
-       /** used by /stats m
-        */
-       unsigned long total_bytes;
-
-       /** True if the command is disabled to non-opers
-        */
-       bool disabled;
-
        /** True if the command can be issued before registering
         */
        bool works_before_reg;
@@ -153,7 +188,7 @@ class CoreExport Command : public ServiceProvider
 
        /** How many seconds worth of penalty does this command have?
         */
-       int Penalty;
+       unsigned int Penalty;
 
        /** Create a new command.
         * @param me The module which created this command.
@@ -162,80 +197,63 @@ class CoreExport Command : public ServiceProvider
         * @param maxpara Maximum number of parameters this command may have - extra parameters
         * will be tossed into one last space-seperated param.
         */
-       Command(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), total_bytes(0), disabled(false), works_before_reg(false), allow_empty_last_param(true),
-               Penalty(1)
-       {
-       }
-
-       /** Handle the command from a user.
-        * @param parameters The parameters for the command.
-        * @param user The user who issued the command.
-        * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure.
-        */
-       virtual CmdResult Handle(const std::vector<std::string>& parameters, User* user) = 0;
+       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 CommandBase::Params& 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)
-       {
-       }
+       virtual void EncodeParameter(std::string& parameter, unsigned 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).
+       /** @return true if the command works before registration.
         */
-       virtual void DecodeParameter(std::string& parameter, int index)
+       bool WorksBeforeReg()
        {
+               return works_before_reg;
        }
 
-       /** Disable or enable this command.
-        * @param setting True to disable the command.
+       virtual ~CommandBase();
+};
+
+class CoreExport Command : public CommandBase
+{
+ public:
+       /** If true, the command will not be forwarded by the linking module even if it comes via ENCAP.
+        * Can be used to forward commands before their effects.
         */
-       void Disable(bool setting)
-       {
-               disabled = setting;
-       }
+       bool force_manual_route;
+
+       Command(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0);
 
-       /** Obtain this command's disable state.
-        * @return true if the command is currently disabled
-        * (disabled commands can be used only by operators)
+       /** Handle the command from a user.
+        * @param parameters The parameters for the command.
+        * @param user The user who issued the command.
+        * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure.
         */
-       bool IsDisabled()
-       {
-               return disabled;
-       }
+       virtual CmdResult Handle(User* user, const Params& parameters) = 0;
 
-       /** @return true if the command works before registration.
+       /** Register this object in the CommandParser
         */
-       bool WorksBeforeReg()
-       {
-               return works_before_reg;
-       }
+       void RegisterService() CXX11_OVERRIDE;
 
-       virtual ~Command();
+       /** Destructor
+        * Removes this command from the command parser
+        */
+       ~Command();
 };
 
 class CoreExport SplitCommand : public Command
 {
  public:
-       SplitCommand(Module* me, const std::string &cmd, int minpara = 0, int maxpara = 0)
+       SplitCommand(Module* me, const std::string &cmd, unsigned int minpara = 0, unsigned int maxpara = 0)
                : Command(me, cmd, minpara, maxpara) {}
-       virtual CmdResult Handle(const std::vector<std::string>& parameters, User* user);
-       virtual CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
-       virtual CmdResult HandleRemote(const std::vector<std::string>& parameters, RemoteUser* user);
-       virtual CmdResult HandleServer(const std::vector<std::string>& parameters, FakeUser* user);
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+       virtual CmdResult HandleLocal(LocalUser* user, const Params& parameters);
+       virtual CmdResult HandleRemote(RemoteUser* user, const Params& parameters);
+       virtual CmdResult HandleServer(FakeUser* user, const Params& parameters);
 };
 
 /** Shortcut macros for defining translation lists
@@ -252,5 +270,3 @@ class CoreExport SplitCommand : public Command
        translation.push_back(x5);translation.push_back(x6);translation.push_back(x7);
 #define TRANSLATE8(x1,x2,x3,x4,x5,x6,x7,x8)  translation.push_back(x1);translation.push_back(x2);translation.push_back(x3);translation.push_back(x4);\
        translation.push_back(x5);translation.push_back(x6);translation.push_back(x7);translation.push_back(x8);
-
-#endif
index 75b08b7a373d686e0c51993908a6d0525e44417e..f5087f512e0a4af50aec7035dab8bd1fc72b77c4 100644 (file)
@@ -20,8 +20,7 @@
  */
 
 
-#ifndef CULL_LIST_H
-#define CULL_LIST_H
+#pragma once
 
 /**
  * The CullList class is used to delete objects at the end of the main loop to
@@ -44,20 +43,25 @@ class CoreExport CullList
        void Apply();
 };
 
+/** Represents an action which is executable by an action list */
+class CoreExport ActionBase : public classbase
+{
+ public:
+        /** Executes this action. */
+       virtual void Call() = 0;
+};
+
 class CoreExport ActionList
 {
-       std::vector<HandlerBase0<void>*> list;
+       std::vector<ActionBase*> list;
 
  public:
        /** Adds an item to the list
         */
-       void AddAction(HandlerBase0<void>* item) { list.push_back(item); }
+       void AddAction(ActionBase* item) { list.push_back(item); }
 
        /** Runs the items
         */
        void Run();
 
 };
-
-#endif
-
diff --git a/include/dns.h b/include/dns.h
deleted file mode 100644 (file)
index 05df6f6..0000000
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * 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) 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/>.
- */
-
-
-/*
-dns.h - dns library very very loosely based on
-firedns, Copyright (C) 2002 Ian Gulliver
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of version 2 of the GNU General Public License as
-published by the Free Software Foundation.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#ifndef DNS_H
-#define DNS_H
-
-#include "socket.h"
-#include "hashcomp.h"
-
-/**
- * Query and resource record types
- */
-enum QueryType
-{
-       /** Uninitialized Query */
-       DNS_QUERY_NONE  = 0,
-       /** 'A' record: an ipv4 address */
-       DNS_QUERY_A     = 1,
-       /** 'CNAME' record: An alias */
-       DNS_QUERY_CNAME = 5,
-       /** 'PTR' record: a hostname */
-       DNS_QUERY_PTR   = 12,
-       /** 'AAAA' record: an ipv6 address */
-       DNS_QUERY_AAAA  = 28,
-
-       /** Force 'PTR' to use IPV4 scemantics */
-       DNS_QUERY_PTR4  = 0xFFFD,
-       /** Force 'PTR' to use IPV6 scemantics */
-       DNS_QUERY_PTR6  = 0xFFFE
-};
-
-/**
- * Result status, used internally
- */
-class CoreExport DNSResult
-{
- public:
-       /** Result ID
-        */
-       int id;
-       /** Result body, a hostname or IP address
-        */
-       std::string result;
-       /** Time-to-live value of the result
-        */
-       unsigned long ttl;
-       /** The original request, a hostname or IP address
-        */
-       std::string original;
-       /** The type of the request
-        */
-       QueryType type;
-
-       /** Build a DNS result.
-        * @param i The request ID
-        * @param res The request result, a hostname or IP
-        * @param timetolive The request time-to-live
-        * @param orig The original request, a hostname or IP
-        * @param qt The type of DNS query this result represents.
-        */
-       DNSResult(int i, const std::string &res, unsigned long timetolive, const std::string &orig, QueryType qt = DNS_QUERY_NONE) : id(i), result(res), ttl(timetolive), original(orig), type(qt) { }
-};
-
-/**
- * Information on a completed lookup, used internally
- */
-typedef std::pair<unsigned char*, std::string> DNSInfo;
-
-/** Cached item stored in the query cache.
- */
-class CoreExport CachedQuery
-{
- public:
-       /** The cached result data, an IP or hostname
-        */
-       std::string data;
-       /** The type of result this is
-        */
-       QueryType type;
-       /** The time when the item is due to expire
-        */
-       time_t expires;
-
-       /** Build a cached query
-        * @param res The result data, an IP or hostname
-        * @param qt The type of DNS query this instance represents.
-        * @param ttl The time-to-live value of the query result
-        */
-       CachedQuery(const std::string &res, QueryType qt, unsigned int ttl);
-
-       /** Returns the number of seconds remaining before this
-        * cache item has expired and should be removed.
-        */
-       int CalcTTLRemaining();
-};
-
-/** DNS cache information. Holds IPs mapped to hostnames, and hostnames mapped to IPs.
- */
-typedef nspace::hash_map<irc::string, CachedQuery, irc::hash> dnscache;
-
-/**
- * Error types that class Resolver can emit to its error method.
- */
-enum ResolverError
-{
-       RESOLVER_NOERROR        =       0,
-       RESOLVER_NSDOWN         =       1,
-       RESOLVER_NXDOMAIN       =       2,
-       RESOLVER_BADIP          =       3,
-       RESOLVER_TIMEOUT        =       4,
-       RESOLVER_FORCEUNLOAD    =       5
-};
-
-/**
- * Used internally to force PTR lookups to use a certain protocol scemantics,
- * e.g. x.x.x.x.in-addr.arpa for v4, and *.ip6.arpa for v6.
- */
-enum ForceProtocol
-{
-       /** Forced to use ipv4 */
-       PROTOCOL_IPV4 = 0,
-       /** Forced to use ipv6 */
-       PROTOCOL_IPV6 = 1
-};
-
-/**
- * The Resolver class is a high-level abstraction for resolving DNS entries.
- * It can do forward and reverse IPv4 lookups, and where IPv6 is supported, will
- * also be able to do those, transparent of protocols. Module developers must
- * extend this class via inheritence, and then insert a pointer to their derived
- * class into the core using Server::AddResolver(). Once you have done this,
- * the class will be able to receive callbacks. There are two callbacks which
- * can occur by calling virtual methods, one is a success situation, and the other
- * an error situation.
- */
-class CoreExport Resolver
-{
- protected:
-       /**
-        * Pointer to creator module (if any, or NULL)
-        */
-       ModuleRef Creator;
-       /**
-        * The input data, either a host or an IP address
-        */
-       std::string input;
-       /**
-        * True if a forward lookup is being performed, false if otherwise
-        */
-       QueryType querytype;
-       /**
-        * The DNS erver being used for lookups. If this is an empty string,
-        * the value of ServerConfig::DNSServer is used instead.
-        */
-       std::string server;
-       /**
-        * The ID allocated to your lookup. This is a pseudo-random number
-        * between 0 and 65535, a value of -1 indicating a failure.
-        * The core uses this to route results to the correct objects.
-        */
-       int myid;
-
-       /**
-        * Cached result, if there is one
-        */
-       CachedQuery *CQ;
-
-       /**
-        * Time left before cache expiry
-        */
-       int time_left;
-
- public:
-       /**
-        * Initiate DNS lookup. Your class should not attempt to delete or free these
-        * objects, as the core will do this for you. They must always be created upon
-        * the heap using new, as you cannot be sure at what time they will be deleted.
-        * Allocating them on the stack or attempting to delete them yourself could cause
-        * the object to go 'out of scope' and cause a segfault in the core if the result
-        * arrives at a later time.
-        * @param source The IP or hostname to resolve
-        * @param qt The query type to perform. Resolution of 'A', 'AAAA', 'PTR' and 'CNAME' records
-        * is supported. Use one of the QueryType enum values to initiate this type of
-        * lookup. Resolution of 'AAAA' ipv6 records is always supported, regardless of
-        * wether InspIRCd is built with ipv6 support.
-        * To look up reverse records, specify one of DNS_QUERY_PTR4 or DNS_QUERY_PTR6 depending
-        * on the type of address you are looking up.
-        * @param cached The constructor will set this boolean to true or false depending
-        * on whether the DNS lookup you are attempting is cached (and not expired) or not.
-        * If the value is cached, upon return this will be set to true, otherwise it will
-        * be set to false. You should pass this value to InspIRCd::AddResolver(), which
-        * will then influence the behaviour of the method and determine whether a cached
-        * or non-cached result is obtained. The value in this variable is always correct
-        * for the given request when the constructor exits.
-        * @param creator See the note below.
-        * @throw ModuleException This class may throw an instance of ModuleException, in the
-        * event a lookup could not be allocated, or a similar hard error occurs such as
-        * the network being down. This will also be thrown if an invalid IP address is
-        * passed when resolving a 'PTR' record.
-        *
-        * NOTE: If you are instantiating your DNS lookup from a module, you should set the
-        * value of creator to point at your Module class. This way if your module is unloaded
-        * whilst lookups are in progress, they can be safely removed and your module will not
-        * crash the server.
-        */
-       Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator);
-
-       /**
-        * The default destructor does nothing.
-        */
-       virtual ~Resolver();
-
-       /**
-        * When your lookup completes, this method will be called.
-        * @param result The resulting DNS lookup, either an IP address or a hostname.
-        * @param ttl The time-to-live value of the result, in the instance of a cached
-        * result, this is the number of seconds remaining before refresh/expiry.
-        * @param cached True if the result is a cached result, false if it was requested
-        * from the DNS server.
-        */
-       virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) = 0;
-
-       /**
-        * If an error occurs (such as NXDOMAIN, no domain name found) then this method
-        * will be called.
-        * @param e A ResolverError enum containing the error type which has occured.
-        * @param errormessage The error text of the error that occured.
-        */
-       virtual void OnError(ResolverError e, const std::string &errormessage);
-
-       /**
-        * Returns the id value of this class. This is primarily used by the core
-        * to determine where in various tables to place a pointer to your class, but it
-        * is safe to call and use this method.
-        * As specified in RFC1035, each dns request has a 16 bit ID value, ranging
-        * from 0 to 65535. If there is an issue and the core cannot send your request,
-        * this method will return -1.
-        */
-       int GetId();
-
-       /**
-        * Returns the creator module, or NULL
-        */
-       Module* GetCreator();
-
-       /**
-        * If the result is a cached result, this triggers the objects
-        * OnLookupComplete. This is done because it is not safe to call
-        * the abstract virtual method from the constructor.
-        */
-       void TriggerCachedResult();
-};
-
-/** DNS is a singleton class used by the core to dispatch dns
- * requests to the dns server, and route incoming dns replies
- * back to Resolver objects, based upon the request ID. You
- * should never use this class yourself.
- */
-class CoreExport DNS : public EventHandler
-{
- private:
-
-       /**
-        * The maximum value of a dns request id,
-        * 16 bits wide, 0xFFFF.
-        */
-       static const int MAX_REQUEST_ID = 0xFFFF;
-
-       /** Maximum number of entries in cache
-        */
-       static const unsigned int MAX_CACHE_SIZE = 1000;
-
-       /**
-        * Currently cached items
-        */
-       dnscache* cache;
-
-       /** A timer which ticks every hour to remove expired
-        * items from the DNS cache.
-        */
-       class CacheTimer* PruneTimer;
-
-       /**
-        * Build a dns packet payload
-        */
-       int MakePayload(const char* name, const QueryType rr, const unsigned short rr_class, unsigned char* payload);
-
- public:
-
-       irc::sockets::sockaddrs myserver;
-
-       /**
-        * Currently active Resolver classes
-        */
-       Resolver* Classes[MAX_REQUEST_ID];
-
-       /**
-        * Requests that are currently 'in flight'
-        */
-       DNSRequest* requests[MAX_REQUEST_ID];
-
-       /**
-        * The port number DNS requests are made on,
-        * and replies have as a source-port number.
-        */
-       static const int QUERY_PORT = 53;
-
-       /**
-        * Fill an rr (resource record) with data from input
-        */
-       static void FillResourceRecord(ResourceRecord* rr, const unsigned char* input);
-
-       /**
-        * Fill a header with data from input limited by a length
-        */
-       static void FillHeader(DNSHeader *header, const unsigned char *input, const int length);
-
-       /**
-        * Empty out a header into a data stream ready for transmission "on the wire"
-        */
-       static void EmptyHeader(unsigned char *output, const DNSHeader *header, const int length);
-
-       /**
-        * Start the lookup of an ipv4 from a hostname
-        */
-       int GetIP(const char* name);
-
-       /**
-        * Start lookup of a hostname from an ip, but
-        * force a specific protocol to be used for the lookup
-        * for example to perform an ipv6 reverse lookup.
-        */
-       int GetNameForce(const char *ip, ForceProtocol fp);
-
-       /**
-        * Start lookup of an ipv6 from a hostname
-        */
-       int GetIP6(const char *name);
-
-       /**
-        * Start lookup of a CNAME from another hostname
-        */
-       int GetCName(const char* alias);
-
-       /**
-        * Fetch the result string (an ip or host)
-        * and/or an error message to go with it.
-        */
-       DNSResult GetResult();
-
-       /**
-        * Handle a SocketEngine read event
-        * Inherited from EventHandler
-        */
-       void HandleEvent(EventType et, int errornum = 0);
-
-       /**
-        * Add a Resolver* to the list of active classes
-        */
-       bool AddResolverClass(Resolver* r);
-
-       /**
-        * Add a query to the list to be sent
-        */
-       DNSRequest* AddQuery(DNSHeader *header, int &id, const char* original);
-
-       /**
-        * The constructor initialises the dns socket,
-        * and clears the request lists.
-        */
-       DNS();
-
-       /**
-        * Re-initialize the DNS subsystem.
-        */
-       void Rehash();
-
-       /**
-        * Destructor
-        */
-       ~DNS();
-
-       /**
-        * Turn an in6_addr into a .ip6.arpa domain
-        */
-       static void MakeIP6Int(char* query, const in6_addr *ip);
-
-       /**
-        * Clean out all dns resolvers owned by a particular
-        * module, to make unloading a module safe if there
-        * are dns requests currently in progress.
-        */
-       void CleanResolvers(Module* module);
-
-       /** Return the cached value of an IP or hostname
-        * @param source An IP or hostname to find in the cache.
-        * @return A pointer to a CachedQuery if the item exists,
-        * otherwise NULL.
-        */
-       CachedQuery* GetCache(const std::string &source);
-
-       /** Delete a cached item from the DNS cache.
-        * @param source An IP or hostname to remove
-        */
-       void DelCache(const std::string &source);
-
-       /** Clear all items from the DNS cache immediately.
-        */
-       int ClearCache();
-
-       /** Prune the DNS cache, e.g. remove all expired
-        * items and rehash the cache buckets, but leave
-        * items in the hash which are still valid.
-        */
-       int PruneCache();
-};
-
-#endif
-
index bbe89dc7ee94c04de5b386fa2531217664bfe068..c14452f8c1f42a7401ee48cace9e29695f69d476 100644 (file)
@@ -20,8 +20,7 @@
  */
 
 
-#ifndef DLL_H
-#define DLL_H
+#pragma once
 
 /** The DLLManager class is able to load a module file by filename,
  * and locate its init_module symbol.
@@ -60,9 +59,12 @@ class CoreExport DLLManager : public classbase
         */
        Module* CallInit();
 
+       /** Retrieves the value of the specified symbol.
+        * @param name The name of the symbol to retrieve.
+        * @return Either the value of the specified symbol or or NULL if it does not exist.
+        */
+       void* GetSymbol(const char* name);
+
        /** Get detailed version information from the module file */
        std::string GetVersion();
 };
-
-#endif
-
diff --git a/include/dynref.h b/include/dynref.h
new file mode 100644 (file)
index 0000000..6e2e174
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *   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/>.
+ */
+
+
+#pragma once
+
+#include "base.h"
+
+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;
+ public:
+       ModuleRef creator;
+       dynamic_reference_base(Module* Creator, const std::string& Name);
+       ~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();
+};
+
+inline void dynamic_reference_base::check()
+{
+       if (!value)
+               throw ModuleException("Dynamic reference to '" + name + "' failed to resolve");
+}
+
+template<typename T>
+class dynamic_reference : public dynamic_reference_base
+{
+ public:
+       dynamic_reference(Module* Creator, const std::string& Name)
+               : dynamic_reference_base(Creator, Name) {}
+
+       inline T* operator->()
+       {
+               check();
+               return static_cast<T*>(value);
+       }
+
+       T* operator*()
+       {
+               return operator->();
+       }
+
+       const T* operator->() const
+       {
+               return static_cast<T*>(value);
+       }
+
+       const T* operator*() const
+       {
+               return operator->();
+       }
+};
+
+template<typename T>
+class dynamic_reference_nocheck : public dynamic_reference_base
+{
+ public:
+       dynamic_reference_nocheck(Module* Creator, const std::string& Name)
+               : dynamic_reference_base(Creator, Name) {}
+
+       T* operator->()
+       {
+               return static_cast<T*>(value);
+       }
+
+       T* operator*()
+       {
+               return operator->();
+       }
+
+       const T* operator->() const
+       {
+               return static_cast<T*>(value);
+       }
+
+       const T* operator*() const
+       {
+               return operator->();
+       }
+};
+
+class ModeHandler;
+class ChanModeReference : public dynamic_reference_nocheck<ModeHandler>
+{
+ public:
+       ChanModeReference(Module* mod, const std::string& modename)
+               : dynamic_reference_nocheck<ModeHandler>(mod, "mode/" + modename) {}
+};
+
+class UserModeReference : public dynamic_reference_nocheck<ModeHandler>
+{
+ public:
+       UserModeReference(Module* mod, const std::string& modename)
+               : dynamic_reference_nocheck<ModeHandler>(mod, "umode/" + modename) {}
+};
diff --git a/include/event.h b/include/event.h
new file mode 100644 (file)
index 0000000..92bb4ff
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * 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:
+       struct Comp
+       {
+               bool operator()(ModuleEventListener* one, ModuleEventListener* two) const;
+       };
+
+       typedef insp::flat_multiset<ModuleEventListener*, Comp, std::less<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;
+
+       const unsigned int eventpriority;
+
+       /** Called by the dynref when the event provider becomes available
+        */
+       void OnCapture() CXX11_OVERRIDE
+       {
+               prov->subscribers.insert(this);
+       }
+
+ public:
+       static const unsigned int DefaultPriority = 100;
+
+       /** Constructor
+        * @param mod Module subscribing
+        * @param eventid Identifier of the event to subscribe to
+        * @param eventprio The priority to give this event listener
+        */
+       ModuleEventListener(Module* mod, const std::string& eventid, unsigned int eventprio = DefaultPriority)
+               : prov(mod, eventid)
+               , eventpriority(eventprio)
+       {
+               prov.SetCaptureHook(this);
+               // If the dynamic_reference resolved at construction our capture handler wasn't called
+               if (prov)
+                       ModuleEventListener::OnCapture();
+       }
+
+       ~ModuleEventListener()
+       {
+               if (prov)
+                       prov->subscribers.erase(this);
+       }
+
+       friend struct ModuleEventProvider::Comp;
+};
+
+inline bool Events::ModuleEventProvider::Comp::operator()(Events::ModuleEventListener* one, Events::ModuleEventListener* two) const
+{
+       return (one->eventpriority < two->eventpriority);
+}
+
+/**
+ * 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);
index d4890c94d5bcf24ae877d949525c28725651b7ad..b1090d1414c438ed91c1a7e11841246d0d0b0dfe 100644 (file)
@@ -19,8 +19,7 @@
  */
 
 
-#ifndef EXITCODE_H
-#define EXITCODE_H
+#pragma once
 
 /** Valid exit codes to be used with InspIRCd::Exit()
  */
@@ -28,30 +27,18 @@ enum ExitStatus
 {
        EXIT_STATUS_NOERROR = 0,                /* No error */
        EXIT_STATUS_DIE = 1,                    /* Operator issued DIE */
-       EXIT_STATUS_FAILED_EXEC = 2,            /* execv() failed */
-       EXIT_STATUS_INTERNAL = 3,               /* Internal error */
-       EXIT_STATUS_CONFIG = 4,                 /* Config error */
-       EXIT_STATUS_LOG = 5,                    /* Log file error */
-       EXIT_STATUS_FORK = 6,                   /* fork() failed */
-       EXIT_STATUS_ARGV = 7,                   /* Invalid program arguments */
-       EXIT_STATUS_BIND = 8,                   /* Port binding failed on all ports */
-       EXIT_STATUS_PID = 9,                    /* Couldn't write PID file */
-       EXIT_STATUS_SOCKETENGINE = 10,          /* Couldn't start socket engine */
-       EXIT_STATUS_ROOT = 11,                  /* Refusing to start as root */
-       EXIT_STATUS_DIETAG = 12,                /* Found a die tag in the config file */
-       EXIT_STATUS_MODULE = 13,                /* Couldn't load a required module */
-       EXIT_STATUS_CREATEPROCESS = 14,         /* CreateProcess failed (windows) */
-       EXIT_STATUS_SIGTERM = 15,               /* Note: dont move this value. It corresponds with the value of #define SIGTERM. */
-       EXIT_STATUS_BADHANDLER = 16,            /* Bad command handler loaded */
-       EXIT_STATUS_RSCH_FAILED = 17,           /* Windows service specific failure, will name these later */
-       EXIT_STATUS_UPDATESCM_FAILED = 18,      /* Windows service specific failure, will name these later */
-       EXIT_STATUS_CREATE_EVENT_FAILED = 19    /* Windows service specific failure, will name these later */
+       EXIT_STATUS_CONFIG = 2,                 /* Config error */
+       EXIT_STATUS_LOG = 3,                    /* Log file error */
+       EXIT_STATUS_FORK = 4,                   /* fork() failed */
+       EXIT_STATUS_ARGV = 5,                   /* Invalid program arguments */
+       EXIT_STATUS_PID = 6,                    /* Couldn't write PID file */
+       EXIT_STATUS_SOCKETENGINE = 7,   /* Couldn't start socket engine */
+       EXIT_STATUS_ROOT = 8,                   /* Refusing to start as root */
+       EXIT_STATUS_MODULE = 9,                 /* Couldn't load a required module */
+       EXIT_STATUS_SIGTERM = 10                /* Received SIGTERM */
 };
 
 /** Array that maps exit codes (ExitStatus types) to
  * human-readable strings to be shown on shutdown.
  */
 extern const char * ExitCodes[];
-
-#endif
-
index bcc4992bb4867cf1fcad7923ecb8a4750a23b9a3..f88ede4611805e449f6c690864f0327b7abe09cf 100644 (file)
  */
 
 
-#ifndef EXTENSIBLE_H
-#define EXTENSIBLE_H
-
-#include <stdint.h>
+#pragma once
 
 enum SerializeFormat
 {
@@ -39,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
         *
@@ -55,7 +65,11 @@ class CoreExport ExtensionItem : public ServiceProvider, public usecountbase
         */
        virtual void unserialize(SerializeFormat format, Extensible* container, const std::string& value) = 0;
        /** Free the item */
-       virtual void free(void* item) = 0;
+       virtual void free(Extensible* container, void* item) = 0;
+
+       /** Register this object in the ExtensionManager
+        */
+       void RegisterService() CXX11_OVERRIDE;
 
  protected:
        /** Get the item from the internal map */
@@ -76,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;
@@ -85,6 +99,11 @@ class CoreExport Extensible : public classbase
         * Holds all extensible metadata for the class.
         */
        ExtensibleStore extensions;
+
+       /** True if this Extensible has been culled.
+        * A warning is generated if false on destruction.
+        */
+       unsigned int culled:1;
  public:
        /**
         * Get the extension items for iteraton (i.e. for metadata sync during netburst)
@@ -92,36 +111,51 @@ class CoreExport Extensible : public classbase
        inline const ExtensibleStore& GetExtList() const { return extensions; }
 
        Extensible();
-       virtual CullResult cull();
+       CullResult cull() CXX11_OVERRIDE;
        virtual ~Extensible();
        void doUnhookExtensions(const std::vector<reference<ExtensionItem> >& toRemove);
+
+       /**
+        * Free all extension items attached to this Extensible
+        */
+       void FreeAllExtItems();
 };
 
 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);
-       virtual void free(void* item) = 0;
+       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE;
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE;
+       void free(Extensible* container, void* item) CXX11_OVERRIDE = 0;
 };
 
-template<typename T>
+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)
        {
        }
 
@@ -138,57 +172,62 @@ class SimpleExtItem : public LocalExtItem
        {
                T* ptr = new T(value);
                T* old = static_cast<T*>(set_raw(container, ptr));
-               delete old;
+               Del del;
+               del(old);
        }
 
        inline void set(Extensible* container, T* value)
        {
                T* old = static_cast<T*>(set_raw(container, value));
-               delete old;
+               Del del;
+               del(old);
        }
 
        inline void unset(Extensible* container)
        {
                T* old = static_cast<T*>(unset_raw(container));
-               delete old;
+               Del del;
+               del(old);
        }
 
-       virtual void free(void* item)
+       void free(Extensible* container, void* item) CXX11_OVERRIDE
        {
-               delete static_cast<T*>(item);
+               Del del;
+               del(static_cast<T*>(item));
        }
 };
 
 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;
+       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE;
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE;
 };
 
 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;
+       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE;
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE;
        intptr_t get(const Extensible* container) const;
        intptr_t set(Extensible* container, intptr_t value);
-       void free(void* item);
+       void unset(Extensible* container) { set(container, 0); }
+       void free(Extensible* container, void* item) CXX11_OVERRIDE;
 };
 
 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;
-       void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
+       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE;
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE;
        void set(Extensible* container, const std::string& value);
        void unset(Extensible* container);
-       void free(void* item);
+       void free(Extensible* container, void* item) CXX11_OVERRIDE;
 };
-
-#endif
index 22a94c934cd3c288c4ea320e34dde0587d649437..af43a6d90ab7c58b2b7971a8e8a4153479d46da8 100644 (file)
  */
 
 
-#ifndef FILELOGGER_H
-#define FILELOGGER_H
+#pragma once
 
 #include "logger.h"
 
-/** Debug levels for use with InspIRCd::Log()
- *  */
-enum DebugLevel
-{
-    RAWIO       =   5,
-    DEBUG       =   10,
-    VERBOSE     =   20,
-    DEFAULT     =   30,
-    SPARSE      =   40,
-    NONE        =   50
-};
-
-
-/* Forward declaration -- required */
-class InspIRCd;
-
 /** A logging class which logs to a streamed file.
  */
 class CoreExport FileLogStream : public LogStream
@@ -46,12 +29,9 @@ class CoreExport FileLogStream : public LogStream
  private:
        FileWriter *f;
  public:
-       FileLogStream(int loglevel, FileWriter *fw);
+       FileLogStream(LogLevel loglevel, FileWriter *fw);
 
        virtual ~FileLogStream();
 
-       virtual void OnLog(int loglevel, const std::string &type, const std::string &msg);
+       void OnLog(LogLevel loglevel, const std::string& type, const std::string& msg) CXX11_OVERRIDE;
 };
-
-#endif
-
diff --git a/include/fileutils.h b/include/fileutils.h
new file mode 100644 (file)
index 0000000..89f92f9
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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
+
+/** Provides an easy method of reading a text file into memory. */
+class CoreExport FileReader
+{
+       /** The lines of text in the file. */
+       std::vector<std::string> lines;
+
+       /** File size in bytes. */
+       unsigned long totalSize;
+
+ public:
+       /** Initializes a new file reader. */
+       FileReader() : totalSize(0) { }
+
+       /** Initializes a new file reader and reads the specified file.
+        * @param filename The file to read into memory.
+        */
+       FileReader(const std::string& filename);
+
+       /** Loads a text file from disk.
+        * @param filename The file to read into memory.
+        * @throw CoreException The file can not be loaded.
+        */
+       void Load(const std::string& filename);
+
+       /** Retrieves the entire contents of the file cache as a single string. */
+       std::string GetString() const;
+
+       /** Retrieves the entire contents of the file cache as a vector of strings. */
+       const std::vector<std::string>& GetVector() const { return lines; }
+
+       /** Retrieves the total size in bytes of the file. */
+       unsigned long TotalSize() const { return totalSize; }
+};
+
+/** Implements methods for file system access */
+class CoreExport FileSystem
+{
+private:
+       FileSystem() { }
+
+public:
+       /** Expands a path fragment to a full path.
+        * @param base The base path to expand from
+        * @param fragment The path fragment to expand on top of base.
+        */
+       static std::string ExpandPath(const std::string& base, const std::string& fragment);
+
+       /**
+        * Checks whether a file with the specified name exists on the filesystem.
+        * @param path The path to a file.
+        * @return True if the file exists; otherwise, false.
+       */
+       static bool FileExists(const std::string& path);
+
+       /** Gets the file name segment of a path.
+        * @param path The path to extract the file name from.
+        * @return The file name segment of a path.
+        */
+       static std::string GetFileName(const std::string& path);
+
+       /** Determines whether the given path starts with a Windows drive letter.
+        * @param path The path to validate.
+        * @returns True if the path begins with a Windows drive letter; otherwise, false.
+        */
+       static bool StartsWithWindowsDriveLetter(const std::string& path);
+};
diff --git a/include/flat_map.h b/include/flat_map.h
new file mode 100644 (file)
index 0000000..6281516
--- /dev/null
@@ -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>, typename ElementComp = Comp>
+class flat_set : public detail::flat_map_base<T, Comp, T, ElementComp>
+{
+       typedef detail::flat_map_base<T, Comp, T, ElementComp> 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>, typename ElementComp = Comp>
+class flat_multiset : public detail::flat_map_base<T, Comp, T, ElementComp>
+{
+       typedef detail::flat_map_base<T, Comp, T, ElementComp> 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>, typename ElementComp = Comp >
+class flat_map : public detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, ElementComp> >
+{
+       typedef detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, ElementComp> > 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>, typename ElementComp = Comp >
+class flat_multimap : public detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, ElementComp> >
+{
+       typedef detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, ElementComp> > 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/hash_map.h b/include/hash_map.h
deleted file mode 100644 (file)
index e789ea6..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   Copyright (C) 2006 Oliver Lupton <oliverlupton@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/>.
- */
-
-
-#ifndef INSPIRCD_HASHMAP_H
-#define INSPIRCD_HASHMAP_H
-       /** Where hash_map is varies from compiler to compiler
-        * as it is not standard unless we have tr1.
-        *
-        * TODO: in 2.2 if we drop support for libstdc++ older than 3.4.7 and GCC older
-        *       than 4.1 this can be cleaned up massively.
-        */
-       #if !defined _LIBCPP_VERSION && !defined _WIN32
-               #if !defined __GLIBCXX__ || __GLIBCXX__ > 20060309
-                       // GCC4+ has deprecated hash_map and uses tr1. But of course, uses a different include to MSVC. FOR FUCKS SAKE.
-                       #include <tr1/unordered_map>
-                       #define HAS_TR1_UNORDERED
-                       #define HASHMAP_DEPRECATED
-                       #define hash_map unordered_map
-                       #define nspace std::tr1
-                       #define BEGIN_HASHMAP_NAMESPACE namespace std { namespace tr1 {
-                       #define END_HASHMAP_NAMESPACE } }
-               #else
-                       #include <ext/hash_map>
-                       /** Oddball linux namespace for hash_map */
-                       #define nspace __gnu_cxx
-                       #define BEGIN_HASHMAP_NAMESPACE namespace nspace {
-                       #define END_HASHMAP_NAMESPACE }
-               #endif
-       #else
-               #include <unordered_map>
-               #define HAS_TR1_UNORDERED
-               #define HASHMAP_DEPRECATED
-               #define hash_map unordered_map
-               #define nspace std
-               #define BEGIN_HASHMAP_NAMESPACE namespace std {
-               #define END_HASHMAP_NAMESPACE }
-       #endif
-
-#endif
index 78d7ee878d230719ca692ff9b13fe2f9ac674023..453e28c4572d6e1dd8f050c9f9a838d1119849ad 100644 (file)
@@ -22,8 +22,7 @@
  */
 
 
-#ifndef HASHCOMP_H
-#define HASHCOMP_H
+#pragma once
 
 #include <cstring>
 #include <string>
@@ -31,7 +30,7 @@
 #include <deque>
 #include <map>
 #include <set>
-#include "hash_map.h"
+#include "inspircd.h"
 
 /*******************************************************
  * This file contains classes and templates that deal
  * 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
@@ -67,340 +64,96 @@ CoreExport extern unsigned const char rfc_case_insensitive_map[256];
  */
 CoreExport extern unsigned const char ascii_case_insensitive_map[256];
 
-/** Case sensitive (identity) map.
- */
-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);
+
+       /** Check whether \p needle exists within \p haystack.
+        * @param haystack The string to search within.
+        * @param needle The string to search for.
+        * @return Either the index at which \p needle was found or std::string::npos.
+        */
+       CoreExport size_t find(const std::string& haystack, const std::string& needle);
 
        /** 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;
-       };
-
-       /** The irc_char_traits class is used for RFC-style comparison of strings.
-        * This class is used to implement irc::string, a case-insensitive, RFC-
-        * comparing string class.
-        */
-       struct irc_char_traits : std::char_traits<char> {
-
-               /** Check if two chars match.
-                * @param c1st First character
-                * @param c2nd Second character
-                * @return true if the characters are equal
-                */
-               static bool eq(char c1st, char c2nd);
-
-               /** Check if two chars do NOT match.
-                * @param c1st First character
-                * @param c2nd Second character
-                * @return true if the characters are unequal
-                */
-               static bool ne(char c1st, char c2nd);
-
-               /** Check if one char is less than another.
-                * @param c1st First character
-                * @param c2nd Second character
-                * @return true if c1st is less than c2nd
-                */
-               static bool lt(char c1st, char c2nd);
-
-               /** Compare two strings of size n.
-                * @param str1 First string
-                * @param str2 Second string
-                * @param n Length to compare to
-                * @return similar to strcmp, zero for equal, less than zero for str1
-                * being less and greater than zero for str1 being greater than str2.
-                */
-               static CoreExport int compare(const char* str1, const char* str2, size_t n);
-
-               /** Find a char within a string up to position n.
-                * @param s1 String to find in
-                * @param n Position to search up to
-                * @param c Character to search for
-                * @return Pointer to the first occurance of c in s1
-                */
-               static CoreExport const char* find(const char* s1, int  n, char c);
-       };
-
-       /** Compose a hex string from raw data.
-        * @param raw The raw data to compose hex from
-        * @param rawsz The size of the raw data buffer
-        * @return The hex string.
-        */
-       CoreExport std::string hex(const unsigned char *raw, size_t rawsz);
-
-       /** This typedef declares irc::string based upon irc_char_traits.
-        */
-       typedef std::basic_string<char, irc_char_traits, std::allocator<char> > string;
-
-       /** irc::stringjoiner joins string lists into a string, using
-        * the given seperator string.
-        * This class can join a vector of std::string, a deque of
-        * std::string, or a const char* const* array, using overloaded
-        * constructors.
-        */
-       class CoreExport stringjoiner
-       {
-        private:
-
-               /** Output string
-                */
-               std::string joined;
-
-        public:
-
-               /** Join elements of a vector, between (and including) begin and end
-                * @param seperator The string to seperate values with
-                * @param sequence One or more items to seperate
-                * @param begin The starting element in the sequence to be joined
-                * @param end The ending element in the sequence to be joined
-                */
-               stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end);
-
-               /** Join elements of a deque, between (and including) begin and end
-                * @param seperator The string to seperate values with
-                * @param sequence One or more items to seperate
-                * @param begin The starting element in the sequence to be joined
-                * @param end The ending element in the sequence to be joined
-                */
-               stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end);
-
-               /** Join elements of an array of char arrays, between (and including) begin and end
-                * @param seperator The string to seperate values with
-                * @param sequence One or more items to seperate
-                * @param begin The starting element in the sequence to be joined
-                * @param end The ending element in the sequence to be joined
-                */
-               stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end);
-
-               /** Get the joined sequence
-                * @return A reference to the joined string
-                */
-               std::string& GetJoined();
+               bool operator()(const std::string& s1, const std::string& s2) const
+               {
+                       return equals(s1, s2);
+               }
        };
 
-       /** 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
+       struct insensitive
        {
-        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);
-
-               /** deprecated compatability interface - TODO remove */
-               int GetStackedLine(std::deque<std::string> &result, int max_line_size = 360) {
-                       std::vector<std::string> r;
-                       int n = GetStackedLine(r, max_line_size);
-                       result.clear();
-                       result.insert(result.end(), r.begin(), r.end());
-                       return n;
-               }
+               size_t CoreExport operator()(const std::string &s) const;
        };
 
-       /** irc::tokenstream reads a string formatted as per RFC1459 and RFC2812.
-        * It will split the string into 'tokens' each containing one parameter
-        * from the string.
-        * For instance, if it is instantiated with the string:
-        * "PRIVMSG #test :foo bar baz qux"
-        * then each successive call to tokenstream::GetToken() will return
-        * "PRIVMSG", "#test", "foo bar baz qux", "".
-        * Note that if the whole string starts with a colon this is not taken
-        * to mean the string is all one parameter, and the first item in the
-        * list will be ":item". This is to allow for parsing 'source' fields
-        * from data.
-        */
-       class CoreExport tokenstream
+       struct insensitive_swo
        {
-        private:
-
-               /** Original string
-                */
-               std::string tokens;
-
-               /** Last position of a seperator token
-                */
-               std::string::iterator last_starting_position;
-
-               /** Current string position
-                */
-               std::string::iterator n;
-
-               /** True if the last value was an ending value
-                */
-               bool last_pushed;
-        public:
-
-               /** Create a tokenstream and fill it with the provided data
-                */
-               tokenstream(const std::string &source);
-
-               /** Destructor
-                */
-               ~tokenstream();
-
-               /** Fetch the next token from the stream as a std::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(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.
-                */
-               bool GetToken(int &token);
-
-               /** Fetch the next token from the stream as a long 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.
-                */
-               bool GetToken(long &token);
+               bool CoreExport operator()(const std::string& a, const std::string& b) const;
        };
 
        /** 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
        {
-        private:
+        protected:
                /** Original string.
                 */
                std::string tokens;
-               /** Last position of a seperator token
+               /** Separator value
                 */
-               std::string::iterator last_starting_position;
+               char sep;
                /** Current string position
                 */
-               std::string::iterator n;
-               /** Seperator value
+               size_t pos;
+               /** If set then GetToken() can return an empty string
                 */
-               char sep;
+               bool allow_empty;
         public:
                /** Create a sepstream and fill it with the provided data
                 */
-               sepstream(const std::string &source, char seperator);
-
-               /** Destructor
-                */
-               virtual ~sepstream();
+               sepstream(const std::string &source, char separator, bool allowempty = false);
 
                /** Fetch the next token from the stream
                 * @param token The next token from the stream is placed here
                 * @return True if tokens still remain, false if there are none left
                 */
-               virtual bool GetToken(std::string &token);
+               bool GetToken(std::string& token);
 
                /** Fetch the entire remaining stream, without tokenizing
                 * @return The remaining part of the stream
                 */
-               virtual const std::string GetRemaining();
+               const std::string GetRemaining();
 
                /** Returns true if the end of the stream has been reached
                 * @return True if the end of the stream has been reached, otherwise false
                 */
-               virtual bool StreamEnd();
+               bool StreamEnd();
+
+               /** Returns true if the specified value exists in the stream
+                * @param value The value to search for
+                * @return True if the value was found, False otherwise
+                */
+               bool Contains(const std::string& value);
        };
 
        /** A derived form of sepstream, which seperates on commas
@@ -408,9 +161,9 @@ namespace irc
        class CoreExport commasepstream : public sepstream
        {
         public:
-               /** Initialize with comma seperator
+               /** Initialize with comma separator
                 */
-               commasepstream(const std::string &source) : sepstream(source, ',')
+               commasepstream(const std::string &source, bool allowempty = false) : sepstream(source, ',', allowempty)
                {
                }
        };
@@ -420,13 +173,54 @@ namespace irc
        class CoreExport spacesepstream : public sepstream
        {
         public:
-               /** Initialize with space seperator
+               /** Initialize with space separator
                 */
-               spacesepstream(const std::string &source) : sepstream(source, ' ')
+               spacesepstream(const std::string &source, bool allowempty = false) : sepstream(source, ' ', allowempty)
                {
                }
        };
 
+       /** irc::tokenstream reads a string formatted as per RFC1459 and RFC2812.
+        * It will split the string into 'tokens' each containing one parameter
+        * from the string.
+        * For instance, if it is instantiated with the string:
+        * "PRIVMSG #test :foo bar baz qux"
+        * then each successive call to tokenstream::GetToken() will return
+        * "PRIVMSG", "#test", "foo bar baz qux", "".
+        * Note that if the whole string starts with a colon this is not taken
+        * to mean the string is all one parameter, and the first item in the
+        * list will be ":item". This is to allow for parsing 'source' fields
+        * from data.
+        */
+       class CoreExport tokenstream
+       {
+       private:
+               /** The message we are parsing tokens from. */
+               std::string message;
+
+               /** The current position within the message. */
+               size_t position;
+
+        public:
+               /** Create a tokenstream and fill it with the provided data. */
+               tokenstream(const std::string& msg, size_t start = 0, size_t end = std::string::npos);
+
+               /** Retrieves the underlying message. */
+               std::string& GetMessage() { return message; }
+
+               /** Retrieve the next \<middle> token in the token stream.
+                * @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 GetMiddle(std::string& token);
+
+               /** Retrieve the next \<trailing> token in the token stream.
+                * @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 GetTrailing(std::string& token);
+       };
+
        /** The portparser class seperates out a port range into integers.
         * A port range may be specified in the input string in the form
         * "6660,6661,6662-6669,7020". The end of the stream is indicated by
@@ -480,182 +274,4 @@ namespace irc
                 */
                long GetToken();
        };
-
-       /** Turn _ characters in a string into spaces
-        * @param n String to translate
-        * @return The new value with _ translated to space.
-        */
-       CoreExport const char* Spacify(const char* n);
-
-       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;
-}
-
-/** Hashing stuff is totally different on vc++'s hash_map implementation, so to save a buttload of
- * \#ifdefs we'll just do it all at once. Except, of course, with TR1, when it's the same as GCC.
- */
-BEGIN_HASHMAP_NAMESPACE
-
-       /** Hashing function to hash irc::string
-        */
-#if defined(_WIN32) && !defined(HAS_TR1_UNORDERED)
-       template<> class CoreExport hash_compare<irc::string, std::less<irc::string> >
-       {
-       public:
-               enum { bucket_size = 4, min_buckets = 8 }; /* Got these numbers from the CRT source, if anyone wants to change them feel free. */
-
-               /** Compare two irc::string values for hashing in hash_map
-                */
-               bool operator()(const irc::string & s1, const irc::string & s2) const
-               {
-                       if(s1.length() != s2.length()) return true;
-                       return (irc::irc_char_traits::compare(s1.c_str(), s2.c_str(), (size_t)s1.length()) < 0);
-               }
-
-               /** Hash an irc::string value for hash_map
-                */
-               size_t operator()(const irc::string & s) const;
-       };
-
-       template<> class CoreExport hash_compare<std::string, std::less<std::string> >
-       {
-       public:
-               enum { bucket_size = 4, min_buckets = 8 }; /* Again, from the CRT source */
-
-               /** Compare two std::string values for hashing in hash_map
-                */
-               bool operator()(const std::string & s1, const std::string & s2) const
-               {
-                       if(s1.length() != s2.length()) return true;
-                       return (irc::irc_char_traits::compare(s1.c_str(), s2.c_str(), (size_t)s1.length()) < 0);
-               }
-
-               /** Hash a std::string using RFC1459 case sensitivity rules
-               * @param s A string to hash
-               * @return The hash value
-               */
-               size_t operator()(const std::string & s) const;
-       };
-#else
-
-       /* XXX FIXME: Implement a hash function overriding std::string's that works with TR1! */
-
-#ifdef HASHMAP_DEPRECATED
-       struct insensitive
-#else
-       CoreExport template<> struct hash<std::string>
-#endif
-       {
-               size_t CoreExport operator()(const std::string &s) const;
-       };
-
-#endif
-
-       /** Convert a string to lower case respecting RFC1459
-       * @param n A string to lowercase
-       */
-       void strlower(char *n);
-
-END_HASHMAP_NAMESPACE
-
-#endif
index 78348ed54c4957d411c97a6a657d0c13eee8f4c7..9b14598e40286bbfcb86e6bf7198d6ec801c81e4 100644 (file)
  */
 
 
-#ifndef INSPIRCD_H
-#define INSPIRCD_H
+#pragma once
 
-#define _FILE_OFFSET_BITS 64
-#ifndef _LARGEFILE_SOURCE
-#define _LARGEFILE_SOURCE
-#endif
-
-#ifndef _WIN32
-#define DllExport
-#define CoreExport
-#else
-#include "inspircd_win32wrapper.h"
-/** Windows defines these already */
-#undef ERROR
-#endif
-
-#ifdef __GNUC__
-#define CUSTOM_PRINTF(STRING, FIRST) __attribute__((format(printf, STRING, FIRST)))
-#else
-#define CUSTOM_PRINTF(STRING, FIRST)
-#endif
-
-// Required system headers.
+#include <cfloat>
+#include <climits>
+#include <cmath>
 #include <csignal>
-#include <ctime>
 #include <cstdarg>
-#include <algorithm>
-#include <cmath>
-#include <cstring>
-#include <climits>
 #include <cstdio>
-#ifndef _WIN32
-#include <unistd.h>
-#endif
+#include <cstring>
+#include <ctime>
+#include <stdint.h>
 
-#include <sstream>
-#include <string>
-#include <vector>
-#include <list>
+#include <algorithm>
+#include <bitset>
 #include <deque>
+#include <list>
 #include <map>
-#include <bitset>
 #include <set>
-#include <time.h>
-#include "inspircd_config.h"
-#include "inspircd_version.h"
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "intrusive_list.h"
+#include "flat_map.h"
+#include "compat.h"
+#include "aligned_storage.h"
 #include "typedefs.h"
-#include "consolecolors.h"
+#include "convto.h"
+#include "stdalgo.h"
 
 CoreExport extern InspIRCd* ServerInstance;
 
-#include "caller.h"
+/** 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 "dynref.h"
+#include "consolecolors.h"
 #include "cull_list.h"
 #include "extensible.h"
+#include "fileutils.h"
+#include "ctables.h"
 #include "numerics.h"
+#include "numeric.h"
 #include "uid.h"
+#include "server.h"
 #include "users.h"
 #include "channels.h"
 #include "timer.h"
@@ -87,110 +84,20 @@ CoreExport extern InspIRCd* ServerInstance;
 #include "logger.h"
 #include "usermanager.h"
 #include "socket.h"
-#include "ctables.h"
 #include "command_parse.h"
 #include "mode.h"
 #include "socketengine.h"
 #include "snomasks.h"
 #include "filelogger.h"
+#include "message.h"
 #include "modules.h"
+#include "clientprotocol.h"
 #include "threadengine.h"
 #include "configreader.h"
 #include "inspstring.h"
 #include "protocol.h"
-
-#ifndef PATH_MAX
-#warning Potentially broken system, PATH_MAX undefined
-#define PATH_MAX 4096
-#endif
-
-/**
- * Used to define the maximum number of parameters a command may have.
- */
-#define MAXPARAMETERS 127
-
-/** Returned by some functions to indicate failure.
- */
-#define ERROR -1
-
-/** Support for librodent -
- * see http://www.chatspike.net/index.php?z=64
- */
-#define ETIREDHAMSTERS EAGAIN
-
-/** 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";
-       char res[MAXBUF];
-       char* out = res;
-       T quotient = in;
-       while (quotient) {
-               *out = "0123456789"[ std::abs( (long)quotient % 10 ) ];
-               ++out;
-               quotient /= 10;
-       }
-       if (in < 0)
-               *out++ = '-';
-       *out = 0;
-       std::reverse(res,out);
-       return res;
-}
-
-/** 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
@@ -201,38 +108,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
        */
@@ -254,23 +161,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)
        {
        }
 };
 
-DEFINE_HANDLER2(IsNickHandler, bool, const char*, size_t);
-DEFINE_HANDLER2(GenRandomHandler, void, char*, size_t);
-DEFINE_HANDLER1(IsIdentHandler, bool, const char*);
-DEFINE_HANDLER1(FloodQuitUserHandler, void, User*);
-DEFINE_HANDLER2(IsChannelHandler, bool, const char*, size_t);
-DEFINE_HANDLER1(IsSIDHandler, bool, const std::string&);
-DEFINE_HANDLER1(RehashHandler, void, const std::string&);
-DEFINE_HANDLER3(OnCheckExemptionHandler, ModResult, User*, Channel*, const std::string&);
-
-class TestSuite;
-
 /** The main class of the irc server.
  * This class contains instances of all the other classes in this software.
  * Amongst other things, it contains a ModeParser, a DNS object, a CommandParser
@@ -280,10 +176,6 @@ class TestSuite;
 class CoreExport InspIRCd
 {
  private:
-       /** Holds the current UID. Used to generate the next one.
-        */
-       char current_uid[UUID_LENGTH];
-
        /** Set up the signal handlers
         */
        void SetSignals();
@@ -293,25 +185,6 @@ class CoreExport InspIRCd
         */
        bool DaemonSeed();
 
-       /** Iterate the list of BufferedSocket objects, removing ones which have timed out
-        * @param TIME the current time
-        */
-       void DoSocketTimeouts(time_t TIME);
-
-       /** Increments the current UID by one.
-        */
-       void IncrementUID(int pos);
-
-       /** Perform background user events such as PING checks
-        */
-       void DoBackgroundUserStuff();
-
-       /** Returns true when all modules have done pre-registration checks on a user
-        * @param user The user to verify
-        * @return True if all modules have finished checking this user
-        */
-       bool AllModulesReportReady(LocalUser* user);
-
        /** The current time, updated in the mainloop
         */
        struct timespec TIME;
@@ -321,25 +194,23 @@ class CoreExport InspIRCd
         */
        char ReadBuffer[65535];
 
+       ClientProtocol::RFCEvents rfcevents;
+
+       /** Check we aren't running as root, and exit if we are
+        * with exit code EXIT_STATUS_ROOT.
+        */
+       void CheckRoot();
+
  public:
 
+       UIDGenerator UIDGen;
+
        /** Global cull list, will be processed on next iteration
         */
        CullList GlobalCulls;
        /** Actions that must happen outside of the current call stack */
        ActionList AtomicActions;
 
-       /**** Functors ****/
-
-       IsNickHandler HandleIsNick;
-       IsIdentHandler HandleIsIdent;
-       FloodQuitUserHandler HandleFloodQuitUser;
-       OnCheckExemptionHandler HandleOnCheckExemption;
-       IsChannelHandler HandleIsChannel;
-       IsSIDHandler HandleIsSID;
-       RehashHandler HandleRehash;
-       GenRandomHandler HandleGenRandom;
-
        /** Globally accessible fake user record. This is used to force mode changes etc across s2s, etc.. bit ugly, but.. better than how this was done in 1.1
         * Reason for it:
         * kludge alert!
@@ -350,28 +221,12 @@ class CoreExport InspIRCd
         */
        FakeUser* FakeClient;
 
-       /** Returns the next available UID for this server.
-        */
-       std::string GetUID();
-
-       static const char LogHeader[];
-
        /** Find a user in the UUID hash
         * @param uid The UUID to find
         * @return A pointer to the user, or NULL if the user does not exist
         */
        User* FindUUID(const std::string &uid);
 
-       /** Find a user in the UUID hash
-        * @param uid The UUID to find
-        * @return A pointer to the user, or NULL if the user does not exist
-        */
-       User* FindUUID(const char *uid);
-
-       /** Build the ISUPPORT string by triggering all modules On005Numeric events
-        */
-       void BuildISupport();
-
        /** Time this ircd was booted
         */
        time_t startup_time;
@@ -384,19 +239,15 @@ class CoreExport InspIRCd
 
        /** Mode handler, handles mode setting and removal
         */
-       ModeParser* Modes;
+       ModeParser Modes;
 
        /** Command parser, handles client to server commands
         */
-       CommandParser* Parser;
-
-       /** Socket engine, handles socket activity events
-        */
-       SocketEngine* SE;
+       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
         */
@@ -404,21 +255,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
         */
@@ -427,33 +278,29 @@ class CoreExport InspIRCd
        /** Snomask manager - handles routing of snomask messages
         * to opers.
         */
-       SnomaskManager* SNO;
-
-       /** DNS class, provides resolver facilities to the core and modules
-        */
-       DNS* Res;
+       SnomaskManager SNO;
 
        /** Timer manager class, triggers Timer timer events
         */
-       TimerManager* Timers;
+       TimerManager Timers;
 
-       /** X-Line manager. Handles G/K/Q/E line setting, removal and matching
+       /** X-line manager. Handles G/K/Q/E-line setting, removal and matching
         */
        XLineManager* XLines;
 
        /** 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
         */
-       chan_hash* chanlist;
+       chan_hash chanlist;
 
        /** List of the open ports
         */
        std::vector<ListenSocket*> ports;
 
-       /** Set to the current signal recieved
+       /** Set to the current signal received
         */
        static sig_atomic_t s_signal;
 
@@ -461,13 +308,12 @@ class CoreExport InspIRCd
         */
        ProtocolInterface* PI;
 
-       /** Holds extensible for user nickforced
+       /** Default implementation of the ProtocolInterface, does nothing
         */
-       LocalIntExt NICKForced;
+       ProtocolInterface DefaultProtocolInterface;
 
-       /** Holds extensible for user operquit
-        */
-       LocalStringExt OperQuit;
+       /** Manages the generation and transmission of ISUPPORT. */
+       ISupportManager ISupport;
 
        /** Get the current time
         * Because this only calls time() once every time around the mainloop,
@@ -485,37 +331,33 @@ class CoreExport InspIRCd
         * @param printable if false, the string will use characters 0-255; otherwise,
         * it will be limited to 0x30-0x7E ('0'-'~', nonspace printable characters)
         */
-       std::string GenRandomStr(int length, bool printable = true);
+       std::string GenRandomStr(unsigned int length, bool printable = true);
        /** Generate a random integer.
         * This is generally more secure than rand()
         */
        unsigned long GenRandomInt(unsigned long max);
 
        /** Fill a buffer with random bits */
-       caller2<void, char*, size_t> GenRandom;
+       TR1NS::function<void(char*, size_t)> GenRandom;
 
-       /** Bind all ports specified in the configuration file.
-        * @return The number of ports bound without error
+       /** Fills the output buffer with the specified number of random characters.
+        * This is the default function for InspIRCd::GenRandom.
+        * @param output The output buffer to store random characters in.
+        * @param max The maximum number of random characters to put in the buffer.
         */
-       int BindPorts(FailedPortList &failed_ports);
+       static void DefaultGenRandom(char* output, size_t max);
 
-       /** 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
+       /** Bind to a specific port from a config tag.
+        * @param tag the tag that contains bind information.
+        * @param sa The endpoint to listen on.
+        * @param old_ports Previously listening ports that may be on the same endpoint.
         */
-       bool BindSocket(int sockfd, int port, const char* addr, bool dolisten = true);
+       bool BindPort(ConfigTag* tag, const irc::sockets::sockaddrs& sa, std::vector<ListenSocket*>& old_ports);
 
-       /** Gets the GECOS (description) field of the given server.
-        * If the servername is not that of the local server, the name
-        * is passed to handling modules which will attempt to determine
-        * the GECOS that bleongs to the given servername.
-        * @param servername The servername to find the description of
-        * @return The description of this server, or of the local server
+       /** Bind all ports specified in the configuration file.
+        * @return The number of ports bound without error
         */
-       std::string GetServerDescription(const std::string& servername);
+       int BindPorts(FailedPortList &failed_ports);
 
        /** Find a user in the nick hash.
         * If the user cant be found in the nick hash check the uuid hash
@@ -524,17 +366,6 @@ class CoreExport InspIRCd
         */
        User* FindNick(const std::string &nick);
 
-       /** 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
-        * @return A pointer to the user, or NULL if the user does not exist
-        */
-       User* FindNick(const char* nick);
-
-       /** Find a user in the nick hash ONLY
-        */
-       User* FindNickOnly(const char* nick);
-
        /** Find a user in the nick hash ONLY
         */
        User* FindNickOnly(const std::string &nick);
@@ -545,46 +376,39 @@ class CoreExport InspIRCd
         */
        Channel* FindChan(const std::string &chan);
 
-       /** Find a channel in the channels hash
-        * @param chan The channel to find
-        * @return A pointer to the channel, or NULL if the channel does not exist
+       /** Get a hash map containing all channels, keyed by their name
+        * @return A hash map mapping channel names to Channel pointers
         */
-       Channel* FindChan(const char* chan);
+       chan_hash& GetChans() { return chanlist; }
 
-       /** Check we aren't running as root, and exit if we are
-        * @return Depending on the configuration, this function may never return
-        */
-       void CheckRoot();
+       /** Determines whether an channel name is valid. */
+       TR1NS::function<bool(const std::string&)> IsChannel;
 
-       /** Determine the right path for, and open, the logfile
-        * @param argv The argv passed to main() initially, used to calculate program path
-        * @param argc The argc passed to main() initially, used to calculate program path
-        * @return True if the log could be opened, false if otherwise
-        */
-       bool OpenLog(char** argv, int argc);
+       /** Determines whether a channel name is valid according to the RFC 1459 rules.
+        * This is the default function for InspIRCd::IsChannel.
+        * @param channel The channel name to validate.
+        * @return True if the channel name is valid according to RFC 1459 rules; otherwise, false.
+       */
+       static bool DefaultIsChannel(const std::string& channel);
 
-       /** Return true if a channel name is valid
-        * @param chname A channel name to verify
-        * @return True if the name is valid
+       /** Determines whether a hostname is valid according to RFC 5891 rules.
+        * @param host The hostname to validate.
+        * @return True if the hostname is valid; otherwise, false.
         */
-       caller2<bool, const char*, size_t> IsChannel;
+       static bool IsHost(const std::string& host);
 
        /** Return true if str looks like a server ID
-        * @param string to check against
-        */
-       caller1<bool, const std::string&> IsSID;
-
-       /** Rehash the local server
+        * @param sid string to check against
         */
-       caller1<void, const std::string&> Rehash;
+       static bool IsSID(const std::string& sid);
 
        /** Handles incoming signals after being set
-        * @param signal the signal recieved
+        * @param signal the signal received
         */
        void SignalHandler(int signal);
 
-       /** Sets the signal recieved
-        * @param signal the signal recieved
+       /** Sets the signal received
+        * @param signal the signal received
         */
        static void SetSignal(int signal);
 
@@ -596,75 +420,33 @@ class CoreExport InspIRCd
         */
        void Exit(int status);
 
-       /** Causes the server to exit immediately with exit code 0.
-        * The status code is required for signal handlers, and ignored.
-        */
-       static void QuickExit(int status);
-
-       /** Return a count of channels on the network
-        * @return The number of channels
-        */
-       long ChannelCount();
-
-       /** 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
-        */
-       caller2<bool, const char*, size_t> IsNick;
-
-       /** Return true if an ident is valid
-        * @param An ident to verify
-        * @return True if the ident is valid
-        */
-       caller1<bool, const char*> IsIdent;
+       /** Formats the input string with the specified arguments.
+       * @param formatString The string to format
+       * @param ... A variable number of format arguments.
+       * @return The formatted string
+       */
+       static std::string Format(const char* formatString, ...) CUSTOM_PRINTF(1, 2);
+       static std::string Format(va_list& vaList, const char* formatString) CUSTOM_PRINTF(2, 0);
 
-       /** Add a dns Resolver class to this server's active set
-        * @param r The resolver to add
-        * @param cached If this value is true, then the cache will
-        * be searched for the DNS result, immediately. If the value is
-        * false, then a request will be sent to the nameserver, and the
-        * result will not be immediately available. You should usually
-        * use the boolean value which you passed to the Resolver
-        * constructor, which Resolver will set appropriately depending
-        * on if cached results are available and haven't expired. It is
-        * however safe to force this value to false, forcing a remote DNS
-        * lookup, but not an update of the cache.
-        * @return True if the operation completed successfully. Note that
-        * if this method returns true, you should not attempt to access
-        * the resolver class you pass it after this call, as depending upon
-        * the request given, the object may be deleted!
-        */
-       bool AddResolver(Resolver* r, bool cached);
+       /** Determines whether a nickname is valid. */
+       TR1NS::function<bool(const std::string&)> IsNick;
 
-       /** Add a command to this server's command parser
-        * @param f A Command command handler object to add
-        * @throw ModuleException Will throw ModuleExcption if the command already exists
+       /** Determines whether a nickname is valid according to the RFC 1459 rules.
+        * This is the default function for InspIRCd::IsNick.
+        * @param nick The nickname to validate.
+        * @return True if the nickname is valid according to RFC 1459 rules; otherwise, false.
         */
-       inline void AddCommand(Command *f)
-       {
-               Modules->AddService(*f);
-       }
+       static bool DefaultIsNick(const std::string& nick);
 
-       /** Send a modechange.
-        * The parameters provided are identical to that sent to the
-        * handler for class cmd_mode.
-        * @param parameters The mode parameters
-        * @param user The user to send error messages to
-        */
-       void SendMode(const std::vector<std::string>& parameters, User *user);
+       /** Determines whether an ident is valid. */
+       TR1NS::function<bool(const std::string&)> IsIdent;
 
-       /** Send a modechange and route it to the network.
-        * The parameters provided are identical to that sent to the
-        * handler for class cmd_mode.
-        * @param parameters The mode parameters
-        * @param user The user to send error messages to
-        */
-       void SendGlobalMode(const std::vector<std::string>& parameters, User *user);
+       /** Determines whether a ident is valid according to the RFC 1459 rules.
+        * This is the default function for InspIRCd::IsIdent.
+        * @param ident The ident to validate.
+        * @return True if the ident is valid according to RFC 1459 rules; otherwise, false.
+       */
+       static bool DefaultIsIdent(const std::string& ident);
 
        /** Match two strings using pattern matching, optionally, with a map
         * to check case against (may be NULL). If map is null, match will be case insensitive.
@@ -672,8 +454,8 @@ class CoreExport InspIRCd
         * @param mask The glob pattern to match against.
         * @param map The character map to use when matching.
         */
-       static bool Match(const std::string &str, const std::string &mask, unsigned const char *map = NULL);
-       static bool Match(const  char *str, const char *mask, unsigned const char *map = NULL);
+       static bool Match(const std::string& str, const std::string& mask, unsigned const char* map = NULL);
+       static bool Match(const char* str, const char* mask, unsigned const char* map = NULL);
 
        /** Match two strings using pattern matching, optionally, with a map
         * to check case against (may be NULL). If map is null, match will be case insensitive.
@@ -682,32 +464,23 @@ class CoreExport InspIRCd
         * @param mask The glob or CIDR pattern to match against.
         * @param map The character map to use when matching.
         */
-       static bool MatchCIDR(const std::string &str, const std::string &mask, unsigned const char *map = NULL);
-       static bool MatchCIDR(const  char *str, const char *mask, unsigned const char *map = NULL);
-
-       /** Call the handler for a given command.
-        * @param commandname The command whos handler you wish to call
-        * @param parameters The mode parameters
-        * @param user The user to execute the command as
-        * @return True if the command handler was called successfully
-        */
-       CmdResult CallCommandHandler(const std::string &commandname, const std::vector<std::string>& parameters, User* user);
+       static bool MatchCIDR(const std::string& str, const std::string& mask, unsigned const char* map = NULL);
+       static bool MatchCIDR(const char* str, const char* mask, unsigned const char* map = NULL);
 
-       /** Return true if the command is a module-implemented command and the given parameters are valid for it
-        * @param commandname The command name to check
-        * @param pcnt The parameter count
-        * @param user The user to test-execute the command as
-        * @return True if the command handler is a module command, and there are enough parameters and the user has permission to the command
+       /** Matches a hostname and IP against a space delimited list of hostmasks.
+        * @param masks The space delimited masks to match against.
+        * @param hostname The hostname to try and match.
+        * @param ipaddr The IP address to try and match.
         */
-       bool IsValidModuleCommand(const std::string &commandname, int pcnt, User* user);
+       static bool MatchMask(const std::string& masks, const std::string& hostname, const std::string& ipaddr);
 
        /** Return true if the given parameter is a valid nick!user\@host mask
         * @param mask A nick!user\@host masak to match against
         * @return True i the mask is valid
         */
-       bool IsValidMask(const std::string &mask);
+       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);
@@ -718,36 +491,36 @@ class CoreExport InspIRCd
        static void ProcessColors(file_cache& input);
 
        /** Rehash the local server
+        * @param uuid The uuid of the user who started the rehash, can be empty
         */
-       void RehashServer();
-
-       /** Check if the given nickmask matches too many users, send errors to the given user
-        * @param nick A nickmask to match against
-        * @param user A user to send error text to
-        * @return True if the nick matches too many users
-        */
-       bool NickMatchesEveryone(const std::string &nick, User* user);
-
-       /** Check if the given IP mask matches too many users, send errors to the given user
-        * @param ip An ipmask to match against
-        * @param user A user to send error text to
-        * @return True if the ip matches too many users
-        */
-       bool IPMatchesEveryone(const std::string &ip, User* user);
-
-       /** Check if the given hostmask matches too many users, send errors to the given user
-        * @param mask A hostmask to match against
-        * @param user A user to send error text to
-        * @return True if the host matches too many users
-        */
-       bool HostMatchesEveryone(const std::string &mask, User* user);
+       void Rehash(const std::string& uuid = "");
 
        /** Calculate a duration in seconds from a string in the form 1y2w3d4h6m5s
         * @param str A string containing a time in the form 1y2w3d4h6m5s
         * (one year, two weeks, three days, four hours, six minutes and five seconds)
         * @return The total number of seconds
         */
-       long Duration(const std::string &str);
+       static unsigned long Duration(const std::string& str);
+
+       /** Calculate a duration in seconds from a string in the form 1y2w3d4h6m5s
+        * @param str A string containing a time in the form 1y2w3d4h6m5s
+        * (one year, two weeks, three days, four hours, six minutes and five seconds)
+        * @param duration The location to place the parsed duration valur
+        * @return Whether the duration was a valid format or not
+        */
+       static bool Duration(const std::string& str, unsigned long& duration);
+
+       /** Determines whether a string contains a valid duration.
+        * @param str A string containing a time in the form 1y2w3d4h6m5s
+        * @return True if the string is a valid duration; otherwise, false.
+        */
+       static bool IsValidDuration(const std::string& str);
+
+       /** Return a duration in seconds as a human-readable string.
+        * @param duration The duration in seconds to convert to a human-readable string.
+        * @return A string representing the given duration.
+        */
+       static std::string DurationString(time_t duration);
 
        /** Attempt to compare a password to a string from the config file.
         * This will be passed to handling modules which will compare the data
@@ -756,26 +529,14 @@ class CoreExport InspIRCd
         * @param data The data from the config file
         * @param input The data input by the oper
         * @param hashtype The hash from the config file
-        * @return 0 if the strings match, 1 or -1 if they do not
-        */
-       int PassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype);
-
-       /** Check if a given server is a uline.
-        * An empty string returns true, this is by design.
-        * @param server The server to check for uline status
-        * @return True if the server is a uline OR the string is empty
-        */
-       bool ULine(const std::string& server);
-
-       /** Returns true if the uline is 'silent' (doesnt generate
-        * remote connect notices etc).
+        * @return True if the strings match, false if they do not
         */
-       bool SilentULine(const std::string& server);
+       bool PassCompare(Extensible* ex, const std::string& data, const std::string& input, const std::string& hashtype);
 
        /** Returns the full version string of this ircd
         * @return The version string
         */
-       std::string GetVersionString(bool rawversion = false);
+       std::string GetVersionString(bool getFullVersion = false);
 
        /** Attempt to write the process id to a given file
         * @param filename The PID file to attempt to write to
@@ -793,108 +554,64 @@ 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);
-
-       /** Handle /WHOIS
-        */
-       void DoWhois(User* user, User* dest,unsigned long signon, unsigned long idle, const char* nick);
-
-       /** Quit a user for excess flood, and if they are not
-        * fully registered yet, temporarily zline their IP.
-        * @param current user to quit
-        */
-       caller1<void, User*> FloodQuitUser;
-
-       /** 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
-        * @param Action name
-        */
-       caller3<ModResult, User*, Channel*, const std::string&> OnCheckExemption;
-
-       /** Restart the server.
-        * This function will not return. If an error occurs,
-        * it will throw an instance of CoreException.
-        * @param reason The restart reason to show to all clients
-        * @throw CoreException An instance of CoreException indicating the error from execv().
-        */
-       void Restart(const std::string &reason);
-
        /** Prepare the ircd for restart or shutdown.
         * This function unloads all modules which can be unloaded,
         * closes all open sockets, and closes the logfile.
         */
        void Cleanup();
 
-       /** This copies the user and channel hash_maps into new hash maps.
-        * This frees memory used by the hash_map allocator (which it neglects
-        * to free, most of the time, using tons of ram)
-        */
-       void RehashUsersAndChans();
-
-       /** Resets the cached max bans value on all channels.
-        * Called by rehash.
+       /** 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 curtime The timestamp to convert to a human-readable string.
+        * @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.
         */
-       void ResetMaxBans();
+       static std::string TimeString(time_t curtime, const char* format = NULL, bool utc = false);
 
-       /** Return a time_t as a human-readable string.
+       /** 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
         */
-       std::string TimeString(time_t curtime);
+       static bool TimingSafeCompare(const std::string& one, const std::string& two);
 
        /** Begin execution of the server.
         * NOTE: this function NEVER returns. Internally,
         * it will repeatedly loop.
-        * @return The return value for this function is undefined.
         */
-       int Run();
-
-       /** Adds an extban char to the 005 token.
-        */
-       void AddExtBanChar(char c);
+       void Run();
 
        char* GetReadBuffer()
        {
                return this->ReadBuffer;
        }
 
-       friend class TestSuite;
+       ClientProtocol::RFCEvents& GetRFCEvents() { return rfcevents; }
 };
 
 ENTRYPOINT;
 
-template<class Cmd>
-class CommandModule : public Module
+inline void stdalgo::culldeleter::operator()(classbase* item)
 {
-       Cmd cmd;
- public:
-       CommandModule() : cmd(this)
-       {
-       }
+       if (item)
+               ServerInstance->GlobalCulls.AddItem(item);
+}
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
+inline void Channel::Write(ClientProtocol::EventProvider& protoevprov, ClientProtocol::Message& msg, char status, const CUList& except_list)
+{
+       ClientProtocol::Event event(protoevprov, msg);
+       Write(event, status, except_list);
+}
 
-       Version GetVersion()
-       {
-               return Version(cmd.name, VF_VENDOR|VF_CORE);
-       }
-};
+inline void LocalUser::Send(ClientProtocol::EventProvider& protoevprov, ClientProtocol::Message& msg)
+{
+       ClientProtocol::Event event(protoevprov, msg);
+       Send(event);
+}
 
-#endif
+#include "numericbuilder.h"
+#include "clientprotocolmsg.h"
+#include "clientprotocolevent.h"
index c62c5a25099375193622b4331963256ef2e819ea..a41c3ebc733e666c08ccdf0c4c873c88c99b5236 100644 (file)
  */
 
 
-#ifndef INSPSOCKET_H
-#define INSPSOCKET_H
+#pragma once
 
 #include "timer.h"
 
+class IOHook;
+
 /**
  * States which a socket may be in
  */
@@ -87,13 +88,17 @@ class CoreExport SocketTimeout : public Timer
         * @param fd File descriptor of BufferedSocket
         * @param thesock BufferedSocket to attach to
         * @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, unsigned int secs_from_now)
+               : Timer(secs_from_now)
+               , sock(thesock)
+               , sfd(fd)
+       {
+       }
 
        /** Handle tick event
         */
-       virtual void Tick(time_t now);
+       bool Tick(time_t now) CXX11_OVERRIDE;
 };
 
 /**
@@ -102,30 +107,196 @@ class CoreExport SocketTimeout : public Timer
  */
 class CoreExport StreamSocket : public EventHandler
 {
-       /** Module that handles raw I/O for this socket, or NULL */
-       reference<Module> IOHook;
-       /** Private send queue. Note that individual strings may be shared
+ 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;
+       };
+
+       /** The type of socket this IOHook represents. */
+       enum Type
+       {
+               SS_UNKNOWN,
+               SS_USER
+       };
+
+ private:
+       /** The IOHook that handles raw I/O for this socket, or NULL */
+       IOHook* iohook;
+
+       /** 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() : sendq_len(0) {}
-       inline Module* GetIOHook();
-       inline void AddIOHook(Module* m);
-       inline void DelIOHook();
-       /** Handle event from socket engine.
-        * This will call OnDataReady if there is *new* data in recvq
-        */
-       virtual void HandleEvent(EventType et, int errornum = 0);
-       /** Dispatched from HandleEvent */
-       virtual void DoRead();
-       /** Dispatched from HandleEvent */
-       virtual void DoWrite();
+       const Type type;
+       StreamSocket(Type sstype = SS_UNKNOWN)
+               : iohook(NULL)
+               , type(sstype)
+       {
+       }
+       IOHook* GetIOHook() const;
+       void AddIOHook(IOHook* hook);
+       void DelIOHook();
+
+       /** 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
+        */
+       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; }
@@ -138,6 +309,13 @@ class CoreExport StreamSocket : public EventHandler
        /** Called when the socket gets an error from socket engine or IO hook */
        virtual void OnError(BufferedSocketError e) = 0;
 
+       /** Called when the endpoint addresses are changed.
+        * @param local The new local endpoint.
+        * @param remote The new remote endpoint.
+        * @return true if the connection is still open, false if it has been closed
+        */
+       virtual bool OnSetEndPoint(const irc::sockets::sockaddrs& local, const irc::sockets::sockaddrs& remote);
+
        /** Send the given data out the socket, either now or when writes unblock
         */
        void WriteData(const std::string& data);
@@ -148,14 +326,22 @@ 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
         */
        virtual void Close();
        /** This ensures that close is called prior to destructor */
-       virtual CullResult cull();
+       CullResult cull() CXX11_OVERRIDE;
+
+       /** 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
@@ -194,12 +380,11 @@ class CoreExport BufferedSocket : public StreamSocket
         * This will create a socket, register with socket engine, and start the asynchronous
         * connection process. If an error is detected at this point (such as out of file descriptors),
         * OnError will be called; otherwise, the state will become CONNECTING.
-        * @param ipaddr Address to connect to
-        * @param aport Port to connect on
+        * @param dest Remote endpoint to connect to.
+        * @param bind Local endpoint to connect from.
         * @param maxtime Time to wait for connection
-        * @param connectbindip Address to bind to (if NULL, no bind will be done)
         */
-       void DoConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip);
+       void DoConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned int maxtime);
 
        /** This method is called when an outbound connection on your socket is
         * completed.
@@ -209,7 +394,7 @@ class CoreExport BufferedSocket : public StreamSocket
        /** When there is data waiting to be read on a socket, the OnDataReady()
         * method is called.
         */
-       virtual void OnDataReady() = 0;
+       void OnDataReady() CXX11_OVERRIDE = 0;
 
        /**
         * When an outbound connection fails, and the attempt times out, you
@@ -224,14 +409,9 @@ class CoreExport BufferedSocket : public StreamSocket
 
        virtual ~BufferedSocket();
  protected:
-       virtual void DoWrite();
-       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);
+       void OnEventHandlerWrite() CXX11_OVERRIDE;
+       BufferedSocketError BeginConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned int timeout);
 };
 
-#include "modules.h"
-
-inline Module* StreamSocket::GetIOHook() { return IOHook; }
-inline void StreamSocket::AddIOHook(Module* m) { IOHook = m; }
-inline void StreamSocket::DelIOHook() { IOHook = NULL; }
-#endif
+inline IOHook* StreamSocket::GetIOHook() const { return iohook; }
+inline void StreamSocket::DelIOHook() { iohook = NULL; }
index a6ef5e552869da601f05ecd8bbbad0eb6b8bf63c..2501b76ce85baf339f0ce70fab71626f65719af8 100644 (file)
  */
 
 
-#ifndef INSPSTRING_H
-#define INSPSTRING_H
+#pragma once
 
-// This (inspircd_config) is needed as inspstring doesn't pull in the central header
-#include "inspircd_config.h"
+// This (config) is needed as inspstring doesn't pull in the central header
+#include "config.h"
 #include <cstring>
-//#include <cstddef>
 
-#ifndef HAS_STRLCPY
-/** strlcpy() implementation for systems that don't have it (linux) */
-CoreExport size_t strlcpy(char *dst, const char *src, size_t siz);
-/** strlcat() implementation for systems that don't have it (linux) */
-CoreExport size_t strlcat(char *dst, const char *src, size_t siz);
-#endif
-
-/** charlcat() will append one character to a string using the same
- * safety scemantics as strlcat().
- * @param x The string to operate on
- * @param y the character to append to the end of x
- * @param z The maximum allowed length for z including null terminator
- */
-CoreExport int charlcat(char* x,char y,int z);
-/** charremove() will remove all instances of a character from a string
- * @param mp The string to operate on
- * @param remove The character to remove
+/** Sets ret to the formated string. last is the last parameter before ..., and format is the format in printf-style */
+#define VAFORMAT(ret, last, format) \
+       do { \
+       va_list _vaList; \
+       va_start(_vaList, last); \
+       ret.assign(InspIRCd::Format(_vaList, format)); \
+       va_end(_vaList); \
+       } while (false);
+
+/** Compose a hex string from raw data.
+ * @param raw The raw data to compose hex from (can be NULL if rawsize is 0)
+ * @param rawsize The size of the raw data buffer
+ * @return The hex string
  */
-CoreExport bool charremove(char* mp, char remove);
+CoreExport std::string BinToHex(const void* raw, size_t rawsize);
 
-/** Binary to hexadecimal conversion */
-CoreExport std::string BinToHex(const std::string& data);
 /** Base64 encode */
 CoreExport std::string BinToBase64(const std::string& data, const char* table = NULL, char pad = 0);
 /** Base64 decode */
 CoreExport std::string Base64ToBin(const std::string& data, const char* table = NULL);
 
-#endif
-
+/** Compose a hex string from the data in a std::string.
+ * @param data The data to compose hex from
+ * @return The hex string.
+ */
+inline std::string BinToHex(const std::string& data)
+{
+       return BinToHex(data.data(), data.size());
+}
diff --git a/include/intrusive_list.h b/include/intrusive_list.h
new file mode 100644 (file)
index 0000000..de013ae
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+#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
+{
+       T* ptr_next;
+       T* ptr_prev;
+
+       void unlink()
+       {
+               if (ptr_next)
+                       ptr_next->intrusive_list_node<T, Tag>::ptr_prev = this->ptr_prev;
+               if (ptr_prev)
+                       ptr_prev->intrusive_list_node<T, Tag>::ptr_next = this->ptr_next;
+               ptr_next = ptr_prev = NULL;
+       }
+
+ public:
+       intrusive_list_node()
+               : ptr_next(NULL)
+               , ptr_prev(NULL)
+       {
+       }
+
+       friend class intrusive_list<T, Tag>;
+       friend class intrusive_list_tail<T, Tag>;
+};
+
+} // namespace insp
+
+// 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
+
+// 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 (file)
index 0000000..1dd36b0
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..85404b0
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 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 StreamSocket;
+
+class IOHookProvider : public refcountbase, public ServiceProvider
+{
+       const bool middlehook;
+
+ public:
+       enum Type
+       {
+               IOH_UNKNOWN,
+               IOH_SSL
+       };
+
+       const Type type;
+
+       /** 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 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 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;
+};
+
+class IOHook : public classbase
+{
+ public:
+       /** The IOHookProvider for this hook, contains information about the hook,
+        * such as the module providing it and the hook type.
+        */
+       reference<IOHookProvider> prov;
+
+       /** Constructor
+        * @param provider IOHookProvider that creates this object
+        */
+       IOHook(IOHookProvider* provider)
+               : prov(provider) { }
+
+       /**
+        * 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, StreamSocket::SendQueue& sendq) = 0;
+
+       /** Called immediately before the hooked socket is closed. When this event is called, shutdown()
+        * has not yet been called on the socket.
+        * @param sock Hooked socket
+        */
+       virtual void OnStreamSocketClose(StreamSocket* sock) = 0;
+
+       /**
+        * 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 (file)
index 0000000..e5eeb59
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+       /** Escapes an ISUPPORT token value and appends it to the buffer.
+        * @param buffer The buffer to append to.
+        * @param value An ISUPPORT token value.
+        */
+       void AppendValue(std::string& buffer, const std::string& value);
+
+ 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
new file mode 100644 (file)
index 0000000..66c3b84
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+/** The base class for list modes, should be inherited.
+ */
+class CoreExport ListModeBase : public ModeHandler
+{
+ public:
+       /** An item in a listmode's list
+        */
+       struct ListItem
+       {
+               std::string setter;
+               std::string mask;
+               time_t time;
+               ListItem(const std::string& Mask, const std::string& Setter, time_t Time)
+                       : setter(Setter), mask(Mask), time(Time) { }
+       };
+
+       /** Items stored in the channel's list
+        */
+       typedef std::vector<ListItem> ModeList;
+
+ private:
+       class ChanData
+       {
+       public:
+               ModeList list;
+               int maxitems;
+
+               ChanData() : maxitems(-1) { }
+       };
+
+       /** The number of items a listmode's list may contain
+        */
+       struct ListLimit
+       {
+               std::string mask;
+               unsigned int limit;
+               ListLimit(const std::string& Mask, unsigned int Limit) : mask(Mask), limit(Limit) { }
+               bool operator==(const ListLimit& other) const { return (this->mask == other.mask && this->limit == other.limit); }
+       };
+
+       /** Max items per channel by name
+        */
+       typedef std::vector<ListLimit> limitlist;
+
+       /** The default maximum list size. */
+       static const unsigned int DEFAULT_LIST_SIZE = 100;
+
+       /** Finds the limit of modes that can be placed on the given channel name according to the config
+        * @param channame The channel name to find the limit for
+        * @return The maximum number of modes of this type that we allow to be set on the given channel name
+        */
+       unsigned int FindLimit(const std::string& channame);
+
+       /** Returns the limit on the given channel for this mode.
+        * If the limit is cached then the cached value is returned,
+        * otherwise the limit is determined using FindLimit() and cached
+        * for later queries before it is returned
+        * @param channame The channel name to find the limit for
+        * @param cd The ChanData associated with channel channame
+        * @return The maximum number of modes of this type that we allow to be set on the given channel
+        */
+       unsigned int GetLimitInternal(const std::string& channame, ChanData* cd);
+
+ protected:
+       /** Numeric to use when outputting the list
+        */
+       unsigned int listnumeric;
+
+       /** Numeric to indicate end of list
+        */
+       unsigned int endoflistnumeric;
+
+       /** String to send for end of list
+        */
+       std::string endofliststring;
+
+       /** Automatically tidy up entries
+        */
+       bool tidy;
+
+       /** Limits on a per-channel basis read from the \<listmode>
+        * config tag.
+        */
+       limitlist chanlimits;
+
+       /** Storage key
+        */
+       SimpleExtItem<ChanData> extItem;
+
+ public:
+       /** Constructor.
+        * @param Creator The creator of this class
+        * @param Name Mode name
+        * @param modechar Mode character
+        * @param eolstr End of list string
+        * @param lnum List numeric
+        * @param eolnum End of list numeric
+        * @param autotidy Automatically tidy list entries on add
+        */
+       ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string& eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy);
+
+       /** Get limit of this mode on a channel
+        * @param channel The channel to inspect
+        * @return Maximum number of modes of this type that can be placed on the given channel
+        */
+       unsigned int GetLimit(Channel* channel);
+
+       /** Gets the lower list limit for this listmode.
+        */
+       unsigned int GetLowerLimit();
+
+       /** Retrieves the list of all modes set on the given channel
+        * @param channel Channel to get the list from
+        * @return A list with all modes of this type set on the given channel, can be NULL
+        */
+       ModeList* GetList(Channel* channel);
+
+       /** Display the list for this mode
+        * See mode.h
+        * @param user The user to send the list to
+        * @param channel The channel the user is requesting the list for
+        */
+       void DisplayList(User* user, Channel* channel) CXX11_OVERRIDE;
+
+       /** Tell a user that a list contains no elements.
+        * Sends 'eolnum' numeric with text 'eolstr', unless overridden (see constructor)
+        * @param user The user issuing the command
+        * @param channel The channel that has the empty list
+        * See mode.h
+        */
+       void DisplayEmptyList(User* user, Channel* channel) CXX11_OVERRIDE;
+
+       /** Remove all instances of the mode from a channel.
+        * Populates the given modestack with modes that remove every instance of
+        * this mode from the channel.
+        * See mode.h for more details.
+        * @param channel The channel to remove all instances of the mode from
+        * @param changelist Mode change list to populate with the removal of this mode
+        */
+       void RemoveMode(Channel* channel, Modes::ChangeList& changelist) CXX11_OVERRIDE;
+
+       /** Perform a rehash of this mode's configuration data
+        */
+       void DoRehash();
+
+       /** Handle the list mode.
+        * See mode.h
+        */
+       ModeAction OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding) CXX11_OVERRIDE;
+
+       /** Validate parameters.
+        * Overridden by implementing module.
+        * @param user Source user adding the parameter
+        * @param channel Channel the parameter is being added to
+        * @param parameter The actual parameter being added
+        * @return true if the parameter is valid
+        */
+       virtual bool ValidateParam(User* user, Channel* channel, std::string& parameter);
+
+       /** In the event that the mode should be given a parameter, and no parameter was provided, this method is called.
+        * This allows you to give special information to the user, or handle this any way you like.
+        * @param user The user issuing the mode change
+        * @param dest For user mode changes, the target of the mode. For channel mode changes, NULL.
+        * @param channel For channel mode changes, the target of the mode. For user mode changes, NULL.
+        * See mode.h
+        */
+       virtual void OnParameterMissing(User* user, User* dest, Channel* channel) CXX11_OVERRIDE;
+
+       /** Tell the user the list is too long.
+        * Overridden by implementing module.
+        * @param source Source user adding the parameter
+        * @param channel Channel the parameter is being added to
+        * @param parameter The actual parameter being added
+        */
+       virtual void TellListTooLong(User* source, Channel* channel, std::string& parameter);
+
+       /** Tell the user an item is already on the list.
+        * Overridden by implementing module.
+        * @param source Source user adding the parameter
+        * @param channel Channel the parameter is being added to
+        * @param parameter The actual parameter being added
+        */
+       virtual void TellAlreadyOnList(User* source, Channel* channel, std::string& parameter);
+
+       /** Tell the user that the parameter is not in the list.
+        * Overridden by implementing module.
+        * @param source Source user removing the parameter
+        * @param channel Channel the parameter is being removed from
+        * @param parameter The actual parameter being removed
+        */
+       virtual void TellNotSet(User* source, Channel* channel, std::string& parameter);
+};
+
+inline ListModeBase::ModeList* ListModeBase::GetList(Channel* channel)
+{
+       ChanData* cd = extItem.get(channel);
+       if (!cd)
+               return NULL;
+
+       return &cd->list;
+}
index 0fa4bc7cd029ce7374b3548128c61276d767089f..c03c84d9a410226e2bb77bc891064abf584c8e56 100644 (file)
  */
 
 
-#ifndef LOGGER_H
-#define LOGGER_H
+#pragma once
+
+/** Levels at which messages can be logged. */
+enum LogLevel
+{
+       LOG_RAWIO   = 5,
+       LOG_DEBUG   = 10,
+       LOG_VERBOSE = 20,
+       LOG_DEFAULT = 30,
+       LOG_SPARSE  = 40,
+       LOG_NONE    = 50
+};
 
 /** Simple wrapper providing periodic flushing to a disk-backed file.
  */
@@ -31,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,
@@ -77,9 +91,11 @@ class CoreExport FileWriter
 class CoreExport LogStream : public classbase
 {
  protected:
-       int loglvl;
+       LogLevel loglvl;
  public:
-       LogStream(int loglevel) : loglvl(loglevel)
+       static const char LogHeader[];
+
+       LogStream(LogLevel loglevel) : loglvl(loglevel)
        {
        }
 
@@ -91,18 +107,18 @@ class CoreExport LogStream : public classbase
        /** Changes the loglevel for this LogStream on-the-fly.
         * This is needed for -nofork. But other LogStreams could use it to change loglevels.
         */
-       void ChangeLevel(int lvl) { this->loglvl = lvl; }
+       void ChangeLevel(LogLevel lvl) { this->loglvl = lvl; }
 
        /** Called when there is stuff to log for this particular logstream. The derived class may take no action with it, or do what it
         * wants with the output, basically. loglevel and type are primarily for informational purposes (the level and type of the event triggered)
         * and msg is, of course, the actual message to log.
         */
-       virtual void OnLog(int loglevel, const std::string &type, const std::string &msg) = 0;
+       virtual void OnLog(LogLevel loglevel, const std::string &type, const std::string &msg) = 0;
 };
 
 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.
@@ -127,7 +143,6 @@ class CoreExport LogManager
        FileLogMap FileLogs;
 
  public:
-
        LogManager();
        ~LogManager();
 
@@ -187,7 +202,7 @@ class CoreExport LogManager
         */
        bool AddLogType(const std::string &type, LogStream *l, bool autoclose);
 
-       /** Removes a logstream from the core. After removal, it will not recieve further events.
+       /** Removes a logstream from the core. After removal, it will not receive further events.
         * If the LogStream was ever added with autoclose, it will be closed after this call (this means the pointer won't be valid anymore).
         */
        void DelLogStream(LogStream* l);
@@ -199,17 +214,15 @@ class CoreExport LogManager
 
        /** Logs an event, sending it to all LogStreams registered for the type.
         * @param type Log message type (ex: "USERINPUT", "MODULE", ...)
-        * @param loglevel Log message level (DEBUG, VERBOSE, DEFAULT, SPARSE, NONE)
+        * @param loglevel Log message level (LOG_DEBUG, LOG_VERBOSE, LOG_DEFAULT, LOG_SPARSE, LOG_NONE)
         * @param msg The message to be logged (literal).
         */
-       void Log(const std::string &type, int loglevel, const std::string &msg);
+       void Log(const std::string &type, LogLevel loglevel, const std::string &msg);
 
        /** Logs an event, sending it to all LogStreams registered for the type.
         * @param type Log message type (ex: "USERINPUT", "MODULE", ...)
-        * @param loglevel Log message level (DEBUG, VERBOSE, DEFAULT, SPARSE, NONE)
+        * @param loglevel Log message level (LOG_DEBUG, LOG_VERBOSE, LOG_DEFAULT, LOG_SPARSE, LOG_NONE)
         * @param fmt The format of the message to be logged. See your C manual on printf() for details.
         */
-       void Log(const std::string &type, int loglevel, const char *fmt, ...) CUSTOM_PRINTF(4, 5);
+       void Log(const std::string &type, LogLevel loglevel, const char *fmt, ...) CUSTOM_PRINTF(4, 5);
 };
-
-#endif
index 436a9371c5c30ef60505d3be81026d9b4aec016a..6d0bc274f94adf70c6ea1b7507580c4df1d5676b 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2012-2014 Attila Molnar <attilamolnar@hush.com>
  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
  *
  * This file is part of InspIRCd.  InspIRCd is free software: you can
  */
 
 
-#ifndef MEMBERSHIP_H
-#define MEMBERSHIP_H
+#pragma once
 
-class CoreExport Membership : public Extensible
+#include "convto.h"
+
+/**
+ * Represents a member of a channel.
+ * A Membership object is created when a user joins a channel, and destroyed when a user leaves
+ * (via kick, part or quit) a channel.
+ * 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 insp::intrusive_list_node<Membership>
 {
  public:
+       /** Type of the Membership id
+        */
+       typedef uint64_t Id;
+
+       /** User on the channel
+        */
        User* const user;
+
+       /** Channel the user is on
+        */
        Channel* const chan;
-       // mode list, sorted by prefix rank, higest first
+
+       /** List of prefix mode letters this member has,
+        * sorted by prefix rank, highest first
+        */
        std::string modes;
-       Membership(User* u, Channel* c) : user(u), chan(c) {}
-       inline bool hasMode(char m) const
+
+       /** 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 modes.find(m) != std::string::npos;
+               return ConvToNum<Id>(str);
        }
-       unsigned int getRank();
-};
 
-class CoreExport InviteBase
-{
- protected:
-       InviteList invites;
+       /** 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
+        * Membership objects directly.
+        */
+       Membership(User* u, Channel* c) : user(u), chan(c) {}
 
- public:
-       void ClearInvites();
+       /** 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
+        */
+       bool HasMode(const PrefixMode* pm) const
+       {
+               return (modes.find(pm->GetModeChar()) != std::string::npos);
+       }
 
-       friend class Invitation;
-};
+       /** Returns the rank of this member.
+        * The rank of a member is defined as the rank given by the 'strongest' prefix mode a
+        * member has. See the PrefixMode class description for more info.
+        * @return The rank of the member
+        */
+       unsigned int getRank();
 
-class Invitation : public classbase
-{
-       Invitation(Channel* c, LocalUser* u, time_t timeout) : user(u), chan(c), expiry(timeout) {}
+       /** Add a prefix character to a user.
+        * Only the core should call this method, usually from
+        * within the mode parser or when the first user joins
+        * the channel (to grant the default privs to them)
+        * @param mh The mode handler of the prefix mode to associate
+        * @param adding True if adding the prefix, false when removing
+        * @return True if a change was made
+        */
+       bool SetPrefix(PrefixMode* mh, bool adding);
 
- public:
-       LocalUser* const user;
-       Channel* const chan;
-       time_t expiry;
+       /** Get the highest prefix this user has on the channel
+        * @return A character containing the highest prefix.
+        * If the user has no prefix, 0 is returned. If the user has multiple prefixes,
+        * the highest is returned. If you do not recognise the prefix character you
+        * can get, you can deal with it in a 'proportional' manner compared to known
+        * prefixes, using GetPrefixValue().
+        */
+       char GetPrefixChar() const;
 
-       ~Invitation();
-       static void Create(Channel* c, LocalUser* u, time_t timeout);
-       static Invitation* Find(Channel* c, LocalUser* u, bool check_expired = true);
-};
+       /** Return all prefix chars this member has.
+        * @return A list of all prefix characters. The prefixes will always
+        * be in rank order, greatest first, as certain IRC clients require
+        * this when multiple prefixes are used names lists.
+        */
+       std::string GetAllPrefixChars() const;
 
-#endif
+       /** Sends a server notice to this user in the context of this channel.
+        * @param text The contents of the message to send.
+        */
+       void WriteNotice(const std::string& text) const;
+};
diff --git a/include/message.h b/include/message.h
new file mode 100644 (file)
index 0000000..1624a1d
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+/** Whether message was a PRIVMSG or a NOTICE. */
+enum MessageType
+{
+       /** The message is a PRIVMSG. */
+       MSG_PRIVMSG,
+
+       /** The message is a NOTICE. */
+       MSG_NOTICE
+};
+
+class CoreExport MessageDetails
+{
+ public:
+       /** Whether to echo the message at all. */
+       bool echo;
+
+       /* Whether to send the original message back to clients with echo-message support. */
+       bool echo_original;
+
+        /** The users who are exempted from receiving this message. */
+       CUList exemptions;
+
+       /* The original message as sent by the user. */
+       const std::string original_text;
+
+       /** IRCv3 message tags sent to the server by the user. */
+       const ClientProtocol::TagMap tags_in;
+
+       /** IRCv3 message tags sent out to users who get this message. */
+       ClientProtocol::TagMap tags_out;
+
+       /** The message which will be sent to clients. */
+       std::string text;
+
+       /** The type of message. */
+       const MessageType type;
+
+       /** Determines whether the specified message is a CTCP. If the specified message
+        * is a CTCP then the CTCP name and CTCP body are extracted and stored in the
+        * name and body references.
+        * @param name The location to store the parsed CTCP name.
+        * @param body The location to store the parsed CTCP body.
+        */
+       virtual bool IsCTCP(std::string& name, std::string& body) const = 0;
+
+       /** Determines whether the specified message is a CTCP. If the specified message
+        * is a CTCP then the CTCP name is extracted and stored in the name reference.
+        * @param name The location to store the parsed CTCP name.
+        */
+       virtual bool IsCTCP(std::string& name) const = 0;
+
+       /** Determines whether the specified message is a CTCP. */
+       virtual bool IsCTCP() const = 0;
+
+ protected:
+       MessageDetails(MessageType mt, const std::string& msg, const ClientProtocol::TagMap& tags)
+               : echo(true)
+               , echo_original(false)
+               , original_text(msg)
+               , tags_in(tags)
+               , text(msg)
+               , type(mt)
+       {
+       }
+};
+
+/** Represents the target of a message (NOTICE, PRIVMSG, etc). */
+class CoreExport MessageTarget
+{
+ public:
+       /** An enumeration of possible message target types. */
+       enum TargetType
+       {
+               /** The target of the message is a user. */
+               TYPE_USER,
+
+               /** The target of the message is a channel. */
+               TYPE_CHANNEL,
+
+               /** The target of the message is a server. */
+               TYPE_SERVER
+       };
+
+ private:
+       /** The target of the message. */
+       void* dest;
+
+ public:
+       /** If type is TYPE_CHANNEL and the user specified a status rank. */
+       char status;
+
+       /** The type of the target of the message. If this is TYPE_CHANNEL then dest
+        * is a Channel*, TYPE_USER then dest is a User*, and TYPE_SERVER then dest is
+        * a std::string* containing a server glob.
+        */
+       MessageTarget::TargetType type;
+
+       /** Initialises a new channel message target.
+        * @param channel The channel which is the target of the message.
+        * @param statuschar The lowest status rank that the message is being sent to.
+        */
+       MessageTarget(Channel* channel, char statuschar)
+               : dest(channel)
+               , status(statuschar)
+               , type(TYPE_CHANNEL)
+       {
+       }
+
+       /** Initialises a new user message target.
+        * @param user The user which is the target of the message.
+        */
+       MessageTarget(User* user)
+               : dest(user)
+               , status(0)
+               , type(TYPE_USER)
+       {
+       }
+
+       /** Initialises a new server message target.
+        * @param server The server glob which is the target of the message.
+        */
+       MessageTarget(std::string* server)
+               : dest(server)
+               , status(0)
+               , type(TYPE_SERVER)
+       {
+       }
+
+       /** Retrieves the target of this message. */
+       template<typename T>
+       T* Get() const
+       {
+               return static_cast<T*>(dest);
+       }
+};
index 1dab442d40ac6d993ae8c8787a89fc93c66e83a1..fe02838b2fc80aef7b47d98deab905818e718cef 100644 (file)
  */
 
 
-#ifndef MODE_H
-#define MODE_H
+#pragma once
 
 #include "ctables.h"
+#include "modechange.h"
 
 /**
  * Holds the values for different type of modes
@@ -46,17 +46,6 @@ enum ModeAction
        MODEACTION_ALLOW = 1 /* Allow the mode */
 };
 
-/**
- * Used to mask off the mode types in the mode handler
- * array. Used in a simple two instruction hashing function
- * "(modeletter - 65) OR mask"
- */
-enum ModeMasks
-{
-       MASK_USER = 128,        /* A user mode */
-       MASK_CHANNEL = 0        /* A channel mode */
-};
-
 /**
  * These fixed values can be used to proportionally compare module-defined prefixes to known values.
  * For example, if your module queries a Channel, and is told that user 'joebloggs' has the prefix
@@ -85,6 +74,10 @@ enum ParamSpec
        PARAM_ALWAYS
 };
 
+class PrefixMode;
+class ListModeBase;
+class ParamModeBase;
+
 /** Each mode is implemented by ONE ModeHandler class.
  * You must derive ModeHandler and add the child class to
  * the list of modes handled by the ircd, using
@@ -101,12 +94,23 @@ enum ParamSpec
  */
 class CoreExport ModeHandler : public ServiceProvider
 {
- protected:
-       /**
-        * The mode parameter translation type
+ public:
+       typedef size_t Id;
+
+       enum Class
+       {
+               MC_PREFIX,
+               MC_LIST,
+               MC_PARAM,
+               MC_OTHER
+       };
+
+ private:
+       /** The opaque id of this mode assigned by the mode parser
         */
-       TranslateType m_paramtype;
+       Id modeid;
 
+ protected:
        /** What kind of parameters does the mode take?
         */
        ParamSpec parameters_taken;
@@ -116,10 +120,6 @@ class CoreExport ModeHandler : public ServiceProvider
         */
        char mode;
 
-       /** Mode prefix, or 0
-        */
-       char prefix;
-
        /**
         * True if the mode requires oper status
         * to set.
@@ -144,10 +144,15 @@ class CoreExport ModeHandler : public ServiceProvider
         */
        ModeType m_type;
 
-       /** The prefix char needed on channel to use this mode,
-        * only checked for channel modes
+       /** The object type of this mode handler
         */
-       int levelrequired;
+       const Class type_id;
+
+       /** The prefix rank required to set this mode on channels. */
+       unsigned int ranktoset;
+
+       /** The prefix rank required to unset this mode on channels. */
+       unsigned int ranktounset;
 
  public:
        /**
@@ -159,56 +164,85 @@ class CoreExport ModeHandler : public ServiceProvider
         * @param modeletter The mode letter you wish to handle
         * @param params Parameters taken by the mode
         * @param type Type of the mode (MODETYPE_USER or MODETYPE_CHANNEL)
+        * @param mclass The object type of this mode handler, one of ModeHandler::Class
         */
-       ModeHandler(Module* me, const std::string& name, char modeletter, ParamSpec params, ModeType type);
-       virtual CullResult cull();
+       ModeHandler(Module* me, const std::string& name, char modeletter, ParamSpec params, ModeType type, Class mclass = MC_OTHER);
+       CullResult cull() CXX11_OVERRIDE;
        virtual ~ModeHandler();
+
+       /** Register this object in the ModeParser
+        */
+       void RegisterService() CXX11_OVERRIDE;
+
        /**
         * Returns true if the mode is a list mode
         */
-       bool IsListMode();
+       bool IsListMode() const { return list; }
+
        /**
-        * Mode prefix or 0. If this is defined, you should
-        * also implement GetPrefixRank() to return an integer
-        * value for this mode prefix.
+        * Check whether this mode is a prefix mode
+        * @return non-NULL if this mode is a prefix mode, NULL otherwise
         */
-       inline char GetPrefix() const { return prefix; }
+       PrefixMode* IsPrefixMode();
+
        /**
-        * Get the 'value' of this modes prefix.
-        * determines which to display when there are multiple.
-        * The mode with the highest value is ranked first. See the
-        * PrefixModeValue enum and Channel::GetPrefixValue() for
-        * more information.
+        * Check whether this mode is a prefix mode
+        * @return non-NULL if this mode is a prefix mode, NULL otherwise
         */
-       virtual unsigned int GetPrefixRank();
+       const PrefixMode* IsPrefixMode() const;
+
        /**
-        * Returns the mode's type
+        * Check whether this mode handler inherits from ListModeBase
+        * @return non-NULL if this mode handler inherits from ListModeBase, NULL otherwise
         */
-       inline ModeType GetModeType() const { return m_type; }
+       ListModeBase* IsListModeBase();
+
+       /**
+        * 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();
+
+       /**
+        * Check whether this mode handler inherits from ParamModeBase
+        * @return non-NULL if this mode handler inherits from ParamModeBase, NULL otherwise
+        */
+       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.
+        */
+       Id GetId() const { return modeid; }
 
        /** 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.
@@ -269,28 +303,138 @@ class CoreExport ModeHandler : public ServiceProvider
        virtual bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel* channel);
 
        /**
-        * When a MODETYPE_USER mode handler is being removed, the server will call this method for every user on the server.
-        * Your mode handler should remove its user mode from the user by sending the appropriate server modes using
-        * InspIRCd::SendMode(). The default implementation of this method can remove simple modes which have no parameters,
-        * and can be used when your mode is of this type, otherwise you must implement a more advanced version of it to remove
-        * your mode properly from each user.
+        * When a MODETYPE_USER mode handler is being removed, the core will call this method for every user on the server.
+        * The usermode will be removed using the appropiate server mode using InspIRCd::SendMode().
         * @param user The user which the server wants to remove your mode from
-        * @param stack The mode stack to add the mode change to
         */
-       virtual void RemoveMode(User* user, irc::modestacker* stack = NULL);
+       void RemoveMode(User* user);
 
        /**
         * When a MODETYPE_CHANNEL mode handler is being removed, the server will call this method for every channel on the server.
-        * Your mode handler should remove its user mode from the channel by sending the appropriate server modes using
-        * InspIRCd::SendMode(). The default implementation of this method can remove simple modes which have no parameters,
-        * and can be used when your mode is of this type, otherwise you must implement a more advanced version of it to remove
-        * your mode properly from each channel. Note that in the case of listmodes, you should remove the entire list of items.
+        * The mode handler has to populate the given modestacker with mode changes that remove the mode from the channel.
+        * The default implementation of this method can remove all kinds of channel modes except listmodes.
+        * In the case of listmodes, the entire list of items must be added to the modestacker (which is handled by ListModeBase,
+        * 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 = NULL);
+       virtual void RemoveMode(Channel* channel, Modes::ChangeList& changelist);
 
-       inline unsigned int GetLevelRequired() const { return levelrequired; }
+       /** Retrieves the level required to modify this mode.
+        * @param adding Whether the mode is being added or removed.
+        */
+       inline unsigned int GetLevelRequired(bool adding) const
+       {
+               return adding ? ranktoset : ranktounset;
+       }
+
+       friend class ModeParser;
+};
+
+/**
+ * Prefix modes are channel modes that grant a specific rank to members having prefix mode set.
+ * They require a parameter when setting and unsetting; the parameter is always a member of the channel.
+ * A prefix mode may be set on any number of members on a channel, but for a given member a given prefix
+ * mode is either set or not set, in other words members cannot have the same prefix mode set more than once.
+ *
+ * A rank of a member is defined as the rank given by the 'strongest' prefix mode that member has.
+ * Other parts of the IRCd use this rank to determine whether a channel action is allowable for a user or not.
+ * The rank of a prefix mode is constant, i.e. the same rank value is given to all users having that prefix mode set.
+ *
+ * Note that it is possible that the same action requires a different rank on a different channel;
+ * for example changing the topic on a channel having +t set requires a rank that is >= than the rank of a halfop,
+ * but there is no such restriction when +t isn't set.
+ */
+class CoreExport PrefixMode : public ModeHandler
+{
+ protected:
+       /** The prefix character granted by this mode. '@' for op, '+' for voice, etc.
+        * If 0, this mode does not have a visible prefix character.
+        */
+       char prefix;
+
+       /** The prefix rank of this mode, used to compare prefix
+        * modes
+        */
+       unsigned int prefixrank;
+
+       /** Whether a client with this prefix can remove it from themself. */
+       bool selfremove;
+
+ public:
+       /**
+        * Constructor
+        * @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, unsigned int Rank = 0, char PrefixChar = 0);
+
+       /**
+        * Called when a channel mode change access check for your mode occurs.
+        * @param source Contains the user setting the mode.
+        * @param channel contains the destination channel the modes are being set on.
+        * @param parameter The parameter for your mode. This is modifiable.
+        * @param adding This value is true when the mode is being set, or false when it is being unset.
+        * @return allow, deny, or passthru to check against the required level
+        */
+       ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding) CXX11_OVERRIDE;
+
+       /**
+        * Handles setting and unsetting the prefix mode.
+        * Finds the given member of the given channel, if it's not found an error message is sent to 'source'
+        * and MODEACTION_DENY is returned. Otherwise the mode change is attempted.
+        * @param source Source of the mode change, an error message is sent to this user if the target is not found
+        * @param dest Unused
+        * @param channel The channel the mode change is happening on
+        * @param param The nickname or uuid of the target user
+        * @param adding True when the mode is being set, false when it is being unset
+        * @return MODEACTION_ALLOW if the change happened, MODEACTION_DENY if no change happened
+        * The latter occurs either when the member cannot be found or when the member already has this prefix set
+        * (when setting) or doesn't have this prefix set (when unsetting).
+        */
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& param, bool adding) CXX11_OVERRIDE;
+
+       /**
+        * Updates the configuration of this prefix.
+        * @param rank The prefix rank of this mode.
+        * @param setrank The prefix rank required to set this mode on channels.
+        * @param unsetrank The prefix rank required to set this unmode on channels.
+        * @param selfrm Whether a client with this prefix can remove it from themself.
+        */
+       void Update(unsigned int rank, unsigned int setrank, unsigned int unsetrank, bool selfrm);
+
+       /**
+        * Removes this prefix mode from all users on the given channel
+        * @param channel The channel which the server wants to remove your mode from
+        * @param changelist Mode change list to populate with the removal of this mode
+        */
+       void RemoveMode(Channel* channel, Modes::ChangeList& changelist) CXX11_OVERRIDE;
+
+
+       /**
+       * Determines whether a user with this prefix mode can remove it.
+       */
+       bool CanSelfRemove() const { return selfremove; }
+
+       /**
+        * Mode prefix or 0. If this is defined, you should
+        * also implement GetPrefixRank() to return an integer
+        * value for this mode prefix.
+        */
+       char GetPrefix() const { return prefix; }
+
+       /**
+        * Get the 'value' of this modes prefix.
+        * determines which to display when there are multiple.
+        * The mode with the highest value is ranked first. See the
+        * PrefixModeValue enum and Channel::GetPrefixValue() for
+        * more information.
+        */
+       unsigned int GetPrefixRank() const { return prefixrank; }
 };
 
 /** A prebuilt mode handler which handles a simple user mode, e.g. no parameters, usable by any user, with no extra
@@ -303,8 +447,7 @@ class CoreExport SimpleUserModeHandler : public ModeHandler
  public:
        SimpleUserModeHandler(Module* Creator, const std::string& Name, char modeletter)
                : ModeHandler(Creator, Name, modeletter, PARAM_NONE, MODETYPE_USER) {}
-       virtual ~SimpleUserModeHandler() {}
-       virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE;
 };
 
 /** A prebuilt mode handler which handles a simple channel mode, e.g. no parameters, usable by any user, with no extra
@@ -317,18 +460,7 @@ class CoreExport SimpleChannelModeHandler : public ModeHandler
  public:
        SimpleChannelModeHandler(Module* Creator, const std::string& Name, char modeletter)
                : ModeHandler(Creator, Name, modeletter, PARAM_NONE, MODETYPE_CHANNEL) {}
-       virtual ~SimpleChannelModeHandler() {}
-       virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-};
-
-class CoreExport ParamChannelModeHandler : public ModeHandler
-{
- public:
-       ParamChannelModeHandler(Module* Creator, const std::string& Name, char modeletter)
-               : ModeHandler(Creator, Name, modeletter, PARAM_SETONLY, MODETYPE_CHANNEL) {}
-       virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-       /** Validate the parameter - you may change the value to normalize it. Return true if it is valid. */
-       virtual bool ParamValidate(std::string& parameter);
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE;
 };
 
 /**
@@ -339,11 +471,12 @@ class CoreExport ParamChannelModeHandler : public ModeHandler
  */
 class CoreExport ModeWatcher : public classbase
 {
- protected:
+ private:
        /**
-        * The mode letter this class is watching
+        * The mode name this class is watching
         */
-       char mode;
+       const std::string mode;
+
        /**
         * The mode type being watched (user or channel)
         */
@@ -354,22 +487,23 @@ class CoreExport ModeWatcher : public classbase
        /**
         * The constructor initializes the mode and the mode type
         */
-       ModeWatcher(Module* creator, char modeletter, ModeType type);
+       ModeWatcher(Module* creator, const std::string& modename, ModeType type);
        /**
         * The default destructor does nothing.
         */
        virtual ~ModeWatcher();
 
        /**
-        * Get the mode character being watched
-        * @return The mode character being watched
+        * Get the mode name being watched
+        * @return The mode name being watched
         */
-       char GetModeChar();
+       const std::string& GetModeName() const { return mode; }
+
        /**
         * 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.
@@ -380,11 +514,10 @@ class CoreExport ModeWatcher : public classbase
         * If you alter the parameter you are given, the mode handler will see your atered version
         * when it handles the mode.
         * @param adding True if the mode is being added and false if it is being removed
-        * @param type The mode type, either MODETYPE_USER or MODETYPE_CHANNEL
         * @return True to allow the mode change to go ahead, false to abort it. If you abort the
         * change, the mode handler (and ModeWatcher::AfterMode()) will never see the mode change.
         */
-       virtual bool BeforeMode(User* source, User* dest, Channel* channel, std::string &parameter, bool adding, ModeType type);
+       virtual bool BeforeMode(User* source, User* dest, Channel* channel, std::string& parameter, bool adding);
        /**
         * After the mode character has been processed by the ModeHandler, this method will be called.
         * @param source The sender of the mode
@@ -393,68 +526,120 @@ class CoreExport ModeWatcher : public classbase
         * @param parameter The parameter of the mode, if the mode is supposed to have a parameter.
         * You cannot alter the parameter here, as the mode handler has already processed it.
         * @param adding True if the mode is being added and false if it is being removed
-        * @param type The mode type, either MODETYPE_USER or MODETYPE_CHANNEL
         */
-       virtual void AfterMode(User* source, User* dest, Channel* channel, const std::string &parameter, bool adding, ModeType type);
+       virtual void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding);
 };
 
-typedef std::vector<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;
+
+       /** Type of the container that maps mode names to ModeHandlers
+        */
+       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;
+
        /** Mode handlers for each mode, to access a handler subtract
         * 65 from the ascii value of the mode letter.
         * The upper bit of the value indicates if its a usermode
         * or a channel mode, so we have 256 of them not 64.
         */
-       ModeHandler* modehandlers[256];
-       /** Mode watcher classes arranged in the same way as the
-        * mode handlers, except for instead of having 256 of them
-        * we have 256 lists of them.
+       ModeHandler* modehandlers[MODETYPE_LAST][128];
+
+       /** An array of mode handlers indexed by the mode id
         */
-       std::vector<ModeWatcher*> modewatchers[256];
-       /** Displays the current modes of a channel or user.
-        * Used by ModeParser::Process.
+       ModeHandler* modehandlersbyid[MODETYPE_LAST][MODEID_MAX];
+
+       /** A map of mode handlers keyed by their name
         */
-       void DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text);
-       /** Displays the value of a list mode
-        * Used by ModeParser::Process.
+       ModeHandlerMap modehandlersbyname[MODETYPE_LAST];
+
+       /** Lists of mode handlers by type
         */
-       void DisplayListModes(User* user, Channel* chan, std::string &mode_sequence);
+       struct
+       {
+               /** List of mode handlers that inherit from ListModeBase
+                */
+               std::vector<ListModeBase*> list;
 
-       /**
-        * Attempts to apply a mode change to a user or channel
+               /** List of mode handlers that inherit from PrefixMode
+                */
+               std::vector<PrefixMode*> prefix;
+       } mhlist;
+
+       /** Mode watcher classes
         */
-       ModeAction TryMode(User* user, User* targu, Channel* targc, bool adding, unsigned char mode, std::string &param, bool SkipACL);
+       ModeWatcherMap modewatchermap;
 
-       /** The string representing the last set of modes to be parsed.
-        * Use GetLastParse() to get this value, to be used for  display purposes.
+       /** Last processed mode change
         */
-       std::string LastParse;
-       std::vector<std::string> LastParseParams;
-       std::vector<TranslateType> LastParseTranslate;
+       Modes::ChangeList LastChangeList;
 
-       unsigned int sent[256];
+       /**
+        * Attempts to apply a mode change to a user or channel
+        */
+       ModeAction TryMode(User* user, User* targu, Channel* targc, Modes::Change& mcitem, bool SkipACL);
 
-       unsigned int seq;
+       /** Allocates an unused id for the given mode type, throws a ModuleException if out of ids.
+        * @param mt The type of the mode to allocate the id for
+        * @return The id
+        */
+       ModeHandler::Id AllocateModeId(ModeType mt);
 
  public:
+       typedef std::vector<ListModeBase*> ListModeList;
+       typedef std::vector<PrefixMode*> PrefixModeList;
+
+       typedef unsigned int ModeProcessFlag;
+       enum ModeProcessFlags
+       {
+               /** If only this flag is specified, the mode change will be global
+                * and parameter modes will have their parameters explicitly set
+                * (not merged). This is the default.
+                */
+               MODE_NONE = 0,
+
+               /** If this flag is set then the parameters of non-listmodes will be
+                * merged according to their conflict resolution rules.
+                * Does not affect user modes, channel modes without a parameter and
+                * listmodes.
+                */
+               MODE_MERGE = 1,
+
+               /** 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,
+
+               /** 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::ranktoset 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
+       };
 
-       /** The constructor initializes all the RFC basic modes by using ModeParserAddMode().
-        */
        ModeParser();
        ~ModeParser();
 
-       /** Used to check if user 'd' should be allowed to do operation 'MASK' on channel 'chan'.
-        * for example, should 'user A' be able to 'op' on 'channel B'.
-        */
-       User* SanityChecks(User *user,const char *dest,Channel *chan,int status);
+       static bool IsModeChar(char chr);
+
        /** Tidy a banmask. This makes a banmask 'acceptable' if fields are left out.
         * E.g.
         *
@@ -469,18 +654,15 @@ class CoreExport ModeParser
         * This method can be used on both IPV4 and IPV6 user masks.
         */
        static void CleanMask(std::string &mask);
-       /** Get the last string to be processed, as it was sent to the user or channel.
-        * Use this to display a string you just sent to be parsed, as the actual output
-        * may be different to what you sent after it has been 'cleaned up' by the parser.
-        * @return Last parsed string, as seen by users.
-        */
-       const std::string& GetLastParse();
-       const std::vector<std::string>& GetLastParseParams() { return LastParseParams; }
-       const std::vector<TranslateType>& GetLastParseTranslate() { return LastParseTranslate; }
+
+       /** Gets the last mode change to be processed. */
+       const Modes::ChangeList& GetLastChangeList() const { return LastChangeList; }
+
        /** Add a mode to the mode parser.
-        * @return True if the mode was successfully added.
+        * Throws a ModuleException if the mode cannot be added.
         */
-       bool AddMode(ModeHandler* mh);
+       void AddMode(ModeHandler* mh);
+
        /** Delete a mode from the mode parser.
         * When a mode is deleted, the mode handler will be called
         * for every user (if it is a user mode) or for every  channel
@@ -496,9 +678,9 @@ class CoreExport ModeParser
         * triggered. See the documentation of class ModeWatcher for more
         * information.
         * @param mw The ModeWatcher you want to add
-        * @return True if the ModeWatcher was added correctly
         */
-       bool AddModeWatcher(ModeWatcher* mw);
+       void AddModeWatcher(ModeWatcher* mw);
+
        /** Delete a mode watcher.
         * A mode watcher is triggered before and after a mode handler is
         * triggered. See the documentation of class ModeWatcher for more
@@ -507,15 +689,56 @@ 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.
-        * @param user The user setting or removing the modes. When the modes are set
-        * by a server, an 'uninitialized' User is used, where *user\::nick == NULL
-        * and *user->server == NULL.
-        * @param merge Should the mode parameters be merged?
-        */
-       void Process(const std::vector<std::string>& parameters, User *user, bool merge = false);
+
+       /** 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(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.
+        * @param mt Type of mode to search for, user or channel.
+        * @return A pointer to a ModeHandler class, or NULL of there isn't a handler for the given mode name.
+        */
+       ModeHandler* FindMode(const std::string& modename, ModeType mt);
 
        /** Find the mode handler for a given mode and type.
         * @param modeletter mode letter to search for
@@ -524,27 +747,18 @@ class CoreExport ModeParser
         */
        ModeHandler* FindMode(unsigned const char modeletter, ModeType mt);
 
+       /** Find the mode handler for the given prefix mode
+        * @param modeletter The mode letter to search for
+        * @return A pointer to the PrefixMode or NULL if the mode wasn't found or it isn't a prefix mode
+        */
+       PrefixMode* FindPrefixMode(unsigned char modeletter);
+
        /** Find a mode handler by its prefix.
         * If there is no mode handler with the given prefix, NULL will be returned.
         * @param pfxletter The prefix to find, e.g. '@'
         * @return The mode handler which handles this prefix, or NULL if there is none.
         */
-       ModeHandler* FindPrefix(unsigned const char pfxletter);
-
-       /** Returns a list of mode characters which are usermodes.
-        * This is used in the 004 numeric when users connect.
-        */
-       std::string UserModeList();
-
-       /** Returns a list of channel mode characters which are listmodes.
-        * This is used in the 004 numeric when users connect.
-        */
-       std::string ChannelModeList();
-
-       /** Returns a list of channel mode characters which take parameters.
-        * This is used in the 004 numeric when users connect.
-        */
-       std::string ParaModeList();
+       PrefixMode* FindPrefix(unsigned const char pfxletter);
 
        /** Generates a list of modes, comma seperated by type:
         *  1; Listmodes EXCEPT those with a prefix
@@ -552,14 +766,63 @@ class CoreExport ModeParser
         *  3; Modes that only take a param when adding
         *  4; Modes that dont take a param
         */
-       std::string GiveModeList(ModeMasks m);
-
-       static bool PrefixComparison(ModeHandler* one, ModeHandler* two);
+       std::string GiveModeList(ModeType mt);
 
        /** This returns the PREFIX=(ohv)@%+ section of the 005 numeric, or
         * just the "@%+" part if the parameter false
         */
        std::string BuildPrefixes(bool lettersAndModes = true);
+
+       /** Get a list of all mode handlers that inherit from ListModeBase
+        * @return A list containing ListModeBase modes
+        */
+       const ListModeList& GetListModes() const { return mhlist.list; }
+
+       /** Get a list of all prefix modes
+        * @return A list containing all prefix modes
+        */
+       const PrefixModeList& GetPrefixModes() const { return mhlist.prefix; }
+
+       /** Get a mode name -> ModeHandler* map containing all modes of the given type
+        * @param mt Type of modes to return, MODETYPE_USER or MODETYPE_CHANNEL
+        * @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);
 };
 
-#endif
+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 (file)
index 0000000..9ec105e
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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 change Mode change to add
+        */
+       void push(const Modes::Change& change)
+       {
+               items.push_back(change);
+       }
+
+       /** Insert multiple mode changes to the ChangeList
+        * @param first Iterator to the first change to insert
+        * @param last Iterator to the first change to not insert
+        */
+       void push(List::const_iterator first, List::const_iterator last)
+       {
+               items.insert(items.end(), first, last);
+       }
+
+       /** Add a new mode to be changed to this ChangeList
+        * @param mh Mode handler
+        * @param adding True if this mode is being set, false if removed
+        * @param param 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 mh Mode handler
+        * @param param 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 mh Mode handler
+        * @param param 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/modes/cmode_b.h b/include/modes/cmode_b.h
deleted file mode 100644 (file)
index afd5cd1..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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/>.
- */
-
-
-#include "mode.h"
-#include "channels.h"
-
-class InspIRCd;
-
-/** Channel mode +b
- */
-class ModeChannelBan : public ModeHandler
-{
- private:
-       BanItem b;
- public:
-       ModeChannelBan();
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-       std::string& AddBan(User *user,std::string& dest,Channel *chan,int status);
-       std::string& DelBan(User *user,std::string& dest,Channel *chan,int status);
-       void DisplayList(User* user, Channel* channel);
-       void DisplayEmptyList(User* user, Channel* channel);
-       void RemoveMode(User* user, irc::modestacker* stack = NULL);
-       void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
-};
-
diff --git a/include/modes/cmode_k.h b/include/modes/cmode_k.h
deleted file mode 100644 (file)
index 000667f..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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/>.
- */
-
-
-#include "mode.h"
-
-class InspIRCd;
-
-/** Channel mode +k
- */
-class ModeChannelKey : public ModeHandler
-{
- public:
-       ModeChannelKey();
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-       void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
-       void RemoveMode(User* user, irc::modestacker* stack = NULL);
-};
diff --git a/include/modes/cmode_l.h b/include/modes/cmode_l.h
deleted file mode 100644 (file)
index 3018a0d..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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/>.
- */
-
-
-#include "mode.h"
-
-/** Channel mode +l
- */
-class ModeChannelLimit : public ParamChannelModeHandler
-{
- public:
-       ModeChannelLimit();
-       bool ParamValidate(std::string& parameter);
-       bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel* channel);
-};
diff --git a/include/modes/cmode_o.h b/include/modes/cmode_o.h
deleted file mode 100644 (file)
index c5f1764..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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/>.
- */
-
-
-#include "mode.h"
-#include "channels.h"
-
-class InspIRCd;
-
-/** Channel mode +o
- */
-class ModeChannelOp : public ModeHandler
-{
- private:
- public:
-       ModeChannelOp();
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-       unsigned int GetPrefixRank();
-       void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
-       void RemoveMode(User* user, irc::modestacker* stack = NULL);
-};
-
diff --git a/include/modes/cmode_v.h b/include/modes/cmode_v.h
deleted file mode 100644 (file)
index ab037f3..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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/>.
- */
-
-
-#include "mode.h"
-#include "channels.h"
-
-class InspIRCd;
-
-/** Channel mode +v
- */
-class ModeChannelVoice : public ModeHandler
-{
- private:
- public:
-       ModeChannelVoice();
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-       unsigned int GetPrefixRank();
-       void RemoveMode(User* user, irc::modestacker* stack = NULL);
-       void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
-};
-
diff --git a/include/modes/simplemodes.h b/include/modes/simplemodes.h
deleted file mode 100644 (file)
index 661bba4..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   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/>.
- */
-
-#include "mode.h"
-
-/** Channel mode +i
- */
-class ModeChannelInviteOnly : public SimpleChannelModeHandler
-{
- public:
-       ModeChannelInviteOnly() : SimpleChannelModeHandler(NULL, "inviteonly", 'i')
-       {
-       }
-};
-
-/** 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 +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')
-       {
-       }
-};
-      
-/** User mode +i
- */
-class ModeUserInvisible : public SimpleUserModeHandler
-{
- public:
-       ModeUserInvisible() : SimpleUserModeHandler(NULL, "invisible", 'i')
-       {
-       }
-};
-
-/** User mode +w
- */
-class ModeUserWallops : public SimpleUserModeHandler
-{
- public:
-       ModeUserWallops() : SimpleUserModeHandler(NULL, "wallops", 'w')
-       {
-       }
-};
diff --git a/include/modes/umode_o.h b/include/modes/umode_o.h
deleted file mode 100644 (file)
index f9644a0..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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/>.
- */
-
-
-#include "mode.h"
-
-class InspIRCd;
-
-/** User mode +o
- */
-class ModeUserOperator : public ModeHandler
-{
- public:
-       ModeUserOperator();
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-};
diff --git a/include/modes/umode_s.h b/include/modes/umode_s.h
deleted file mode 100644 (file)
index 8ac8fa3..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *   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/>.
- */
-
-
-#include "mode.h"
-
-class InspIRCd;
-
-/** User mode +n
- */
-class ModeUserServerNoticeMask : public ModeHandler
-{
- public:
-       ModeUserServerNoticeMask();
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-       void OnParameterMissing(User* user, User* dest, Channel* channel);
-       std::string GetUserParameter(User* user);
-};
index b16dcc49aa626f56c8c04190b0a5b5e1aea45a37..cd4fdce05164cb687eb1976e663c549c400be51d 100644 (file)
@@ -23,8 +23,7 @@
  */
 
 
-#ifndef MODULES_H
-#define MODULES_H
+#pragma once
 
 #include "dynamic.h"
 #include "base.h"
 #include <sstream>
 #include "timer.h"
 #include "mode.h"
-#include "dns.h"
 
-/** Used to define a set of behavior bits for a module
- */
-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
-       VF_CORE = 16            // module is a core command, can be assumed loaded on all servers
-};
+/** Used to specify the behaviour of a module. */
+enum ModuleFlags
+{
+       /** The module has no special attributes. */
+       VF_NONE = 0,
 
-/** Used to represent an event type, for user, channel or server
- */
-enum TargetTypeFlags {
-       TYPE_USER = 1,
-       TYPE_CHANNEL,
-       TYPE_SERVER,
-       TYPE_OTHER
-};
+       /** The module is a coremod and can be assumed to be loaded on all servers. */
+       VF_CORE = 1,
 
-/** Used to represent wether a message was PRIVMSG or NOTICE
- */
-enum MessageType {
-       MSG_PRIVMSG = 0,
-       MSG_NOTICE = 1
+       /* The module is included with InspIRCd. */
+       VF_VENDOR = 2,
+
+       /** The module MUST be loaded on all servers on a network to link. */
+       VF_COMMON = 4,
+
+       /** The module SHOULD be loaded on all servers on a network for consistency. */
+       VF_OPTCOMMON = 8
 };
 
 #define MOD_RES_ALLOW (ModResult(1))
@@ -109,35 +99,34 @@ struct ModResult {
 /** InspIRCd major version.
  * 1.2 -> 102; 2.1 -> 201; 2.12 -> 212
  */
-#define INSPIRCD_VERSION_MAJ 200
+#define INSPIRCD_VERSION_MAJ 300
+
 /** InspIRCd API version.
  * If you change any API elements, increment this value. This counter should be
  * reset whenever the major version is changed. Modules can use these two values
  * and numerical comparisons in preprocessor macros if they wish to support
  * multiple versions of InspIRCd in one file.
  */
-#define INSPIRCD_VERSION_API 11
+#define INSPIRCD_VERSION_API 5
 
 /**
  * This #define allows us to call a method in all
  * loaded modules in a readable simple way, e.g.:
- * 'FOREACH_MOD(I_OnConnect,OnConnect(user));'
+ * 'FOREACH_MOD(OnConnect,(user));'
  */
 #define FOREACH_MOD(y,x) do { \
-       EventHandlerIter safei; \
-       for (EventHandlerIter _i = ServerInstance->Modules->EventHandlers[y].begin(); _i != ServerInstance->Modules->EventHandlers[y].end(); ) \
+       const Module::List& _handlers = ServerInstance->Modules->EventHandlers[I_ ## y]; \
+       for (Module::List::const_reverse_iterator _i = _handlers.rbegin(), _next; _i != _handlers.rend(); _i = _next) \
        { \
-               safei = _i; \
-               ++safei; \
+               _next = _i+1; \
                try \
                { \
-                       (*_i)->x ; \
+                       (*_i)->x ; \
                } \
                catch (CoreException& modexcept) \
                { \
-                       ServerInstance->Logs->Log("MODULE",DEFAULT,"Exception caught: %s",modexcept.GetReason()); \
+                       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + modexcept.GetReason()); \
                } \
-               _i = safei; \
        } \
 } while (0);
 
@@ -149,21 +138,19 @@ struct ModResult {
  */
 #define DO_EACH_HOOK(n,v,args) \
 do { \
-       EventHandlerIter iter_ ## n = ServerInstance->Modules->EventHandlers[I_ ## n].begin(); \
-       while (iter_ ## n != ServerInstance->Modules->EventHandlers[I_ ## n].end()) \
+       const Module::List& _handlers = ServerInstance->Modules->EventHandlers[I_ ## n]; \
+       for (Module::List::const_reverse_iterator _i = _handlers.rbegin(), _next; _i != _handlers.rend(); _i = _next) \
        { \
-               Module* mod_ ## n = *iter_ ## n; \
-               iter_ ## n ++; \
+               _next = _i+1; \
                try \
                { \
-                       v = (mod_ ## n)->n args;
+                       v = (*_i)->n args;
 
 #define WHILE_EACH_HOOK(n) \
                } \
                catch (CoreException& except_ ## n) \
                { \
-                       ServerInstance->Logs->Log("MODULE",DEFAULT,"Exception caught: %s", (except_ ## n).GetReason()); \
-                       (void) mod_ ## n; /* catch mismatched pairs */ \
+                       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + (except_ ## n).GetReason()); \
                } \
        } \
 } while(0)
@@ -208,71 +195,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 Request class is a unicast message directed at a given module.
- * When this class is properly instantiated it may be sent to a module
- * using the Send() method, which will call the given module's OnRequest
- * method with this class as its parameter.
- */
-class CoreExport Request : public classbase
-{
- public:
-       /** This should be a null-terminated string identifying the type of request,
-        * all modules should define this and use it to determine the nature of the
-        * request before they attempt to cast the Request in any way.
-        */
-       const char* const id;
-       /** 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 single destination of the Request
-        */
-       ModuleRef dest;
-
-       /** Create a new Request
-        * This is for the 'new' way of defining a subclass
-        * of Request and defining it in a common header,
-        * passing an object of your Request subclass through
-        * as a Request* and using the ID string to determine
-        * what to cast it back to and the other end.
-        */
-       Request(Module* src, Module* dst, const char* idstr);
-       /** Send the Request.
-        */
-       void Send();
-};
-
-
-/** 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
@@ -282,38 +204,6 @@ class CoreExport DataProvider : public ServiceProvider
                : ServiceProvider(Creator, Name, SERVICE_DATA) {}
 };
 
-class CoreExport dynamic_reference_base : public interfacebase
-{
- private:
-       std::string name;
- protected:
-       DataProvider* value;
- public:
-       ModuleRef creator;
-       dynamic_reference_base(Module* Creator, const std::string& Name);
-       ~dynamic_reference_base();
-       inline void ClearCache() { value = NULL; }
-       inline const std::string& GetProvider() { return name; }
-       void SetProvider(const std::string& newname);
-       void lookup();
-       operator bool();
-       static void reset_all();
-};
-
-template<typename T>
-class dynamic_reference : public dynamic_reference_base
-{
- public:
-       dynamic_reference(Module* Creator, const std::string& Name)
-               : dynamic_reference_base(Creator, Name) {}
-       inline T* operator->()
-       {
-               if (!value)
-                       lookup();
-               return static_cast<T*>(value);
-       }
-};
-
 /** Priority types which can be used by Module::Prioritize()
  */
 enum Priority { PRIORITY_FIRST, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER };
@@ -322,23 +212,23 @@ 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_OnRehash,
-       I_OnSendSnotice, I_OnUserPreJoin, I_OnUserPreKick, I_OnUserKick, I_OnOper, I_OnInfo, I_OnWhois,
-       I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserPreNick,
-       I_OnUserMessage, I_OnUserNotice, I_OnMode, I_OnGetServerDescription, I_OnSyncUser,
-       I_OnSyncChannel, I_OnDecodeMetaData, I_OnWallops, I_OnAcceptConnection, I_OnUserInit,
-       I_OnChangeHost, I_OnChangeName, I_OnAddLine, I_OnDelLine, I_OnExpireLine,
-       I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnRemoteKill, I_OnLoadModule,
+       I_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart,
+       I_OnSendSnotice, I_OnUserPreJoin, I_OnUserPreKick, I_OnUserKick, I_OnOper,
+       I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNick,
+       I_OnUserPostMessage, I_OnUserMessageBlocked, I_OnMode,
+       I_OnDecodeMetaData, I_OnAcceptConnection, I_OnUserInit, I_OnUserPostInit,
+       I_OnChangeHost, I_OnChangeRealName, I_OnAddLine, I_OnDelLine, I_OnExpireLine,
+       I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnLoadModule,
        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_OnPostDeoper,
-       I_OnPostTopicChange, I_OnEvent, I_OnGlobalOper, I_OnPostConnect, I_OnAddBan,
-       I_OnDelBan, 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_OnText, I_OnPassCompare, I_OnRunTestSuite, I_OnNamesListItem, I_OnNumeric, I_OnHookIO,
-       I_OnPreRehash, I_OnModuleRehash, I_OnSendWhoLine, I_OnChangeIdent, I_OnSetUserIP,
+       I_OnPreChangeHost, I_OnPreTopicChange,
+       I_OnPostTopicChange, I_OnPostConnect, I_OnPostDeoper,
+       I_OnPreChangeRealName, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete,
+       I_OnPostOper, I_OnPostCommand, I_OnPostJoin,
+       I_OnBuildNeighborList, I_OnGarbageCollect, I_OnSetConnectClass,
+       I_OnUserMessage, I_OnPassCompare, I_OnNumeric,
+       I_OnPreRehash, I_OnModuleRehash, I_OnChangeIdent, I_OnSetUserIP,
+       I_OnServiceAdd, I_OnServiceDel, I_OnUserWrite,
        I_END
 };
 
@@ -349,10 +239,19 @@ enum Implementation
  */
 class CoreExport Module : public classbase, public usecountbase
 {
+       /** Detach an event from this module
+        * @param i Event type to detach
+        */
+       void DetachEvent(Implementation i);
+
  public:
+       /** A list of modules. */
+       typedef std::vector<Module*> List;
+
        /** File that this module was loaded from
         */
        std::string ModuleSourceFile;
+
        /** Reference to the dlopen() value
         */
        DLLManager* ModuleDLLManager;
@@ -376,7 +275,7 @@ class CoreExport Module : public classbase, public usecountbase
        /** Clean up prior to destruction
         * If you override, you must call this AFTER your module's cleanup
         */
-       virtual CullResult cull();
+       CullResult cull() CXX11_OVERRIDE;
 
        /** Default destructor.
         * destroys a module class
@@ -387,6 +286,13 @@ class CoreExport Module : public classbase, public usecountbase
        {
        }
 
+       /** This method is called when you should reload module specific configuration:
+        * on boot, on a /REHASH and on module load.
+        * @param status The current status, can be inspected for more information;
+        * also used for reporting configuration errors and warnings.
+        */
+       virtual void ReadConfig(ConfigStatus& status);
+
        /** Returns the version number of a Module.
         * The method should return a Version object with its version information assigned via
         * Version::Version
@@ -477,14 +383,6 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual void OnModuleRehash(User* user, const std::string &parameter);
 
-       /** Called on rehash.
-        * This method is called after a rehash has completed. You should use it to reload any module
-        * configuration from the main configuration file.
-        * @param user The user that performed the rehash, if it was initiated by a user and that user
-        * is still connected.
-        */
-       virtual void OnRehash(User* user);
-
        /** Called whenever a snotice is about to be sent to a snomask.
         * snomask and type may both be modified; the message may not.
         * @param snomask The snomask the message is going to (e.g. 'A')
@@ -514,7 +412,7 @@ class CoreExport Module : public classbase, public usecountbase
         * @param keygiven The key given to join the channel, or an empty string if none was provided
         * @return 1 To prevent the join, 0 to allow it.
         */
-       virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven);
+       virtual ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven);
 
        /** Called whenever a user is about to be kicked.
         * Returning a value of 1 from this function stops the process immediately, causing no
@@ -560,26 +458,6 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual void OnPostDeoper(User* user);
 
-       /** Called whenever a user types /INFO.
-        * The User will contain the information of the user who typed the command. Modules may use this
-        * method to output their own credits in /INFO (which is the ircd's version of an about box).
-        * It is purposefully not possible to modify any info that has already been output, or halt the list.
-        * You must write a 371 numeric to the user, containing your info in the following format:
-        *
-        * &lt;nick&gt; :information here
-        *
-        * @param user The user issuing /INFO
-        */
-       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,
@@ -599,47 +477,21 @@ 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
-        * output to be sent to the user by the core. If you do this you must produce your own numerics,
-        * notices etc. This is useful for modules which may want to filter or redirect messages.
-        * target_type can be one of TYPE_USER or TYPE_CHANNEL. If the target_type value is a user,
-        * you must cast dest to a User* otherwise you must cast it to a Channel*, this is the details
-        * of where the message is destined to be sent.
-        * @param user The user sending the message
-        * @param dest The target of the message (Channel* or User*)
-        * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
-        * @param text Changeable text being sent by the user
-        * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
-        * @param exempt_list A list of users not to send to. For channel messages, this will usually contain just the sender.
-        * It will be ignored for private messages.
-        * @return 1 to deny the message, 0 to allow it
-        */
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list);
-
-       /** Called whenever a user is about to NOTICE A user or a channel, before any processing is done.
-        * Returning any nonzero value 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,
-        * notices etc. This is useful for modules which may want to filter or redirect messages.
-        * target_type can be one of TYPE_USER or TYPE_CHANNEL. If the target_type value is a user,
-        * you must cast dest to a User* otherwise you must cast it to a Channel*, this is the details
-        * of where the message is destined to be sent.
-        * You may alter the message text as you wish before relinquishing control to the next module
-        * in the chain, and if no other modules block the text this altered form of the text will be sent out
-        * to the user and possibly to other servers.
-        * @param user The user sending the message
-        * @param dest The target of the message (Channel* or User*)
-        * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
-        * @param text Changeable text being sent by the user
-        * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
-        * @param exempt_list A list of users not to send to. For channel notices, this will usually contain just the sender.
-        * It will be ignored for private notices.
-        * @return 1 to deny the NOTICE, 0 to allow it
-        */
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list);
+       /** Called before a user sends a message to a channel, a user, or a server glob mask.
+        * @param user The user sending the message.
+        * @param target The target of the message. This can either be a channel, a user, or a server
+        *               glob mask.
+        * @param details Details about the message such as the message text and type. See the
+        *                MessageDetails class for more information.
+        * @return MOD_RES_ALLOW to explicitly allow the message, MOD_RES_DENY to explicitly deny the
+        *         message, or MOD_RES_PASSTHRU to let another module handle the event.
+        */
+       virtual ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details);
 
        /** Called when sending a message to all "neighbors" of a given user -
         * that is, all users that share a common channel. This is used in
@@ -650,168 +502,65 @@ class CoreExport Module : public classbase, public usecountbase
         *
         * Set exceptions[user] = true to include, exceptions[user] = false to exclude
         */
-       virtual void OnBuildNeighborList(User* source, UserChanList &include_c, std::map<User*,bool> &exceptions);
+       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);
-
-       /** Called after any PRIVMSG sent from a user.
-        * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
-        * if target_type is TYPE_CHANNEL.
-        * @param user The user sending the message
-        * @param dest The target of the message
-        * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
-        * @param text the text being sent by the user
-        * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
-        * @param exempt_list A list of users to not send to.
-        */
-       virtual void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
-
-       /** Called after any NOTICE sent from a user.
-        * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
-        * if target_type is TYPE_CHANNEL.
-        * @param user The user sending the message
-        * @param dest The target of the message
-        * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
-        * @param text the text being sent by the user
-        * @param status The status being used, e.g. NOTICE @#chan has status== '@', 0 to send to everyone.
-        * @param exempt_list A list of users to not send to.
-        */
-       virtual void OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
-
-       /** Called immediately before any NOTICE or PRIVMSG sent from a user, local or remote.
-        * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
-        * if target_type is TYPE_CHANNEL.
-        * The difference between this event and OnUserPreNotice/OnUserPreMessage is that delivery is gauranteed,
-        * the message has already been vetted. In the case of the other two methods, a later module may stop your
-        * message. This also differs from OnUserMessage which occurs AFTER the message has been sent.
-        * @param user The user sending the message
-        * @param dest The target of the message
-        * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
-        * @param text the text being sent by the user
-        * @param status The status being used, e.g. NOTICE @#chan has status== '@', 0 to send to everyone.
-        * @param exempt_list A list of users not to send to. For channel messages, this will usually contain just the sender.
-        */
-       virtual void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list);
+       virtual ModResult OnUserPreNick(LocalUser* user, const std::string& newnick);
 
-       /** Called after every MODE command sent from a user
-        * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
-        * if target_type is TYPE_CHANNEL. The text variable contains the remainder of the
-        * mode string after the target, e.g. "+wsi" or "+ooo nick1 nick2 nick3".
-        * @param user The user sending the MODEs
-        * @param dest The target of the modes (User* or Channel*)
-        * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
-        * @param text The actual modes and their parameters if any
-        * @param translate The translation types of the mode parameters
-        */
-       virtual void OnMode(User* user, void* dest, int target_type, const std::vector<std::string> &text, const std::vector<TranslateType> &translate);
-
-       /** Allows modules to alter or create server descriptions
-        * Whenever a module requires a server description, for example for display in
-        * WHOIS, this function is called in all modules. You may change or define the
-        * description given in std::string &description. If you do, this description
-        * will be shown in the WHOIS fields.
-        * @param servername The servername being searched for
-        * @param description Alterable server description for this server
-        */
-       virtual void OnGetServerDescription(const std::string &servername,std::string &description);
-
-       /** 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
-        * the linking protocol. This currently is m_spanningtree.so. A pointer to this module
-        * is given in Module* proto, so that you may call its methods such as ProtoSendMode
-        * (see below). This function will be called for every user visible on your side
-        * of the burst, allowing you to for example set modes, etc. Do not use this call to
-        * synchronize data which you have stored using class Extensible -- There is a specialist
-        * function OnSyncUserMetaData and OnSyncChannelMetaData for this!
-        * @param user The user being syncronized
-        * @param proto A pointer to the module handling network protocol
-        * @param opaque An opaque pointer set by the protocol module, should not be modified!
-        */
-       virtual void OnSyncUser(User* user, Module* proto, void* opaque);
-
-       /** Allows modules to synchronize data which relates to channels during a netburst.
-        * When this function is called, it will be called from the module which implements
-        * the linking protocol. This currently is m_spanningtree.so. A pointer to this module
-        * is given in Module* proto, so that you may call its methods such as ProtoSendMode
-        * (see below). This function will be called for every user visible on your side
-        * of the burst, allowing you to for example set modes, etc.
-        *
-        * For a good example of how to use this function, please see src/modules/m_chanprotect.cpp
-        *
-        * @param chan The channel being syncronized
-        * @param proto A pointer to the module handling network protocol
-        * @param opaque An opaque pointer set by the protocol module, should not be modified!
+       /** Called immediately after a user sends a message to a channel, a user, or a server glob mask.
+        * @param user The user sending the message.
+        * @param target The target of the message. This can either be a channel, a user, or a server
+        *               glob mask.
+        * @param details Details about the message such as the message text and type. See the
+        *                MessageDetails class for more information.
         */
-       virtual void OnSyncChannel(Channel* chan, Module* proto, void* opaque);
+       virtual void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details);
 
-       /* Allows modules to syncronize metadata not related to users or channels, over the network during a netburst.
-        * Whenever the linking module wants to send out data, but doesnt know what the data
-        * represents (e.g. it is Extensible metadata, added to a User or Channel by a module) then
-        * this method is called. You should use the ProtoSendMetaData function after you've
-        * correctly decided how the data should be represented, to send the metadata on its way if
-        * if it belongs to your module.
-        * @param proto A pointer to the module handling network protocol
-        * @param opaque An opaque pointer set by the protocol module, should not be modified!
-        * @param displayable If this value is true, the data is going to be displayed to a user,
-        * and not sent across the network. Use this to determine wether or not to show sensitive data.
+       /** Called immediately before a user sends a message to a channel, a user, or a server glob mask.
+        * @param user The user sending the message.
+        * @param target The target of the message. This can either be a channel, a user, or a server
+        *               glob mask.
+        * @param details Details about the message such as the message text and type. See the
+        *                MessageDetails class for more information.
         */
-       virtual void OnSyncNetwork(Module* proto, void* opaque);
+       virtual void OnUserMessage(User* user, const MessageTarget& target, const MessageDetails& details);
+
+       /** Called when a message sent by a user to a channel, a user, or a server glob mask is blocked.
+        * @param user The user sending the message.
+        * @param target The target of the message. This can either be a channel, a user, or a server
+        *               glob mask.
+        * @param details Details about the message such as the message text and type. See the
+        *                MessageDetails class for more information.
+        */
+       virtual void OnUserMessageBlocked(User* user, const MessageTarget& target, const MessageDetails& details);
+
+       /** 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.
+        * 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 changelist The changed modes.
+        * @param processflags Flags passed to ModeParser::Process(), see ModeParser::ModeProcessFlags
+        * for the possible flags.
+        */
+       virtual void OnMode(User* user, User* usertarget, Channel* chantarget, const Modes::ChangeList& changelist, ModeParser::ModeProcessFlag processflags);
 
        /** Allows module data, sent via ProtoSendMetaData, to be decoded again by a receiving module.
         * Please see src/modules/m_swhois.cpp for a working example of how to use this method call.
         * @param target The Channel* or User* that data should be added to
         * @param extname The extension name which is being sent
-        * @param extdata The extension data, encoded at the other end by an identical module through OnSyncChannelMetaData or OnSyncUserMetaData
+        * @param extdata The extension data, encoded at the other end by an identical module
         */
        virtual void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata);
 
-       /** Implemented by modules which provide the ability to link servers.
-        * These modules will implement this method, which allows transparent sending of servermodes
-        * down the network link as a broadcast, without a module calling it having to know the format
-        * of the MODE command before the actual mode string.
-        *
-        * More documentation to follow soon. Please see src/modules/m_chanprotect.cpp for examples
-        * of how to use this function.
-        *
-        * @param opaque An opaque pointer set by the protocol module, should not be modified!
-        * @param target_type The type of item to decode data for, TYPE_USER or TYPE_CHANNEL
-        * @param target The Channel* or User* that modes should be sent for
-        * @param modeline The modes and parameters to be sent
-        * @param translate The translation types of the mode parameters
-        */
-       virtual void ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const std::vector<std::string> &modeline, const std::vector<TranslateType> &translate);
-
-       /** Implemented by modules which provide the ability to link servers.
-        * These modules will implement this method, which allows metadata (extra data added to
-        * user and channel records using class Extensible, Extensible::Extend, etc) to be sent
-        * to other servers on a netburst and decoded at the other end by the same module on a
-        * different server.
-        *
-        * More documentation to follow soon. Please see src/modules/m_swhois.cpp for example of
-        * how to use this function.
-        * @param opaque An opaque pointer set by the protocol module, should not be modified!
-        * @param target The Channel* or User* that metadata should be sent for
-        * @param extname The extension name to send metadata for
-        * @param extdata Encoded data for this extension name, which will be encoded at the oppsite end by an identical module using OnDecodeMetaData
-        */
-       virtual void ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata);
-
-       /** Called after every WALLOPS command.
-        * @param user The user sending the WALLOPS
-        * @param text The content of the WALLOPS message
-        */
-       virtual void OnWallops(User* user, const std::string &text);
-
        /** Called whenever a user's hostname is changed.
         * This event triggers after the host has been set.
         * @param user The user whos host is being changed
@@ -819,12 +568,12 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual void OnChangeHost(User* user, const std::string &newhost);
 
-       /** Called whenever a user's GECOS (realname) is changed.
+       /** Called whenever a user's real name is changed.
         * This event triggers after the name has been set.
-        * @param user The user who's GECOS is being changed
-        * @param gecos The new GECOS being set on the user
+        * @param user The user who's real name is being changed
+        * @param real The new real name being set on the user
         */
-       virtual void OnChangeName(User* user, const std::string &gecos);
+       virtual void OnChangeRealName(User* user, const std::string& real);
 
        /** Called whenever a user's IDENT is changed.
         * This event triggers after the name has been set.
@@ -853,16 +602,15 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual void OnExpireLine(XLine *line);
 
-       /** Called before your module is unloaded to clean up Extensibles.
-        * This method is called once for every user and channel on the network,
-        * so that when your module unloads it may clear up any remaining data
-        * in the form of Extensibles added using Extensible::Extend().
-        * If the target_type variable is TYPE_USER, then void* item refers to
-        * a User*, otherwise it refers to a Channel*.
-        * @param target_type The type of item being cleaned
-        * @param item A pointer to the item's class
+       /** Called before the module is unloaded to clean up extensibles.
+        * This method is called once for every channel, membership, and user.
+        * so that you can clear up any data relating to the specified extensible.
+        * @param type The type of extensible being cleaned up. If this is EXT_CHANNEL
+        *             then item is a Channel*, EXT_MEMBERSHIP then item is a Membership*,
+        *             and EXT_USER then item is a User*.
+        * @param item A pointer to the extensible which is being cleaned up.
         */
-       virtual void OnCleanup(int target_type, void* item);
+       virtual void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item);
 
        /** Called after any nickchange, local or remote. This can be used to track users after nickchanges
         * have been applied. Please note that although you can see remote nickchanges through this function, you should
@@ -875,7 +623,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
@@ -884,15 +632,15 @@ 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.
-        * @param output The 005 string to be modified if neccessary.
-        */
-       virtual void On005Numeric(std::string &output);
+       * @param tokens The 005 map to be modified if neccessary.
+       */
+       virtual void On005Numeric(std::map<std::string, std::string>& tokens);
 
        /** Called when a client is disconnected by KILL.
         * If a client is killed by a server, e.g. a nickname collision or protocol error,
@@ -909,14 +657,6 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual ModResult OnKill(User* source, User* dest, const std::string &reason);
 
-       /** Called when an oper wants to disconnect a remote user via KILL
-        * @param source The user sending the KILL
-        * @param dest The user being killed
-        * @param reason The kill reason
-        * @param operreason The oper kill reason
-        */
-       virtual void OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason);
-
        /** Called whenever a module is loaded.
         * mod will contain a pointer to the module, and string will contain its name,
         * for example m_widgets.so. This function is primary for dependency checking,
@@ -965,10 +705,9 @@ class CoreExport Module : public classbase, public usecountbase
         * @param user the user issuing the command
         * @param validated True if the command has passed all checks, e.g. it is recognised, has enough parameters, the user has permission to execute it, etc.
         * You should only change the parameter list and command string if validated == false (e.g. before the command lookup occurs).
-        * @param original_line The entire original line as passed to the parser from the user
         * @return 1 to block the command, 0 to allow
         */
-       virtual ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line);
+       virtual ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated);
 
        /** Called after any command has been executed.
         * This event occurs for all registered commands, wether they are registered in the core,
@@ -979,15 +718,24 @@ class CoreExport Module : public classbase, public usecountbase
         * @param parameters An array of array of characters containing the parameters for the command
         * @param user the user issuing the command
         * @param result The return code given by the command handler, one of CMD_SUCCESS or CMD_FAILURE
-        * @param original_line The entire original line as passed to the parser from the user
+        * @param loop Whether the command is being called from LoopCall or directly.
         */
-       virtual void OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line);
+       virtual void OnPostCommand(Command* command, const CommandBase::Params& parameters, LocalUser* user, CmdResult result, bool loop);
 
-       /** Called when a user is first connecting, prior to starting DNS lookups, checking initial
-        * connect class, or accepting any commands.
+       /** Called after a user object is initialised and added to the user list.
+        * When this is called the user has not had their I/O hooks checked or had their initial
+        * connect class assigned and may not yet have a serialiser. You probably want to use
+        * the OnUserPostInit or OnUserSetIP hooks instead of this one.
+        * @param user The connecting user.
         */
        virtual void OnUserInit(LocalUser* user);
 
+       /** Called after a user object has had their I/O hooks checked, their initial connection
+        * class assigned, and had a serialiser set.
+        * @param user The connecting user.
+        */
+       virtual void OnUserPostInit(LocalUser* user);
+
        /** Called to check if a user who is connecting can now be allowed to register
         * If any modules return false for this function, the user is held in the waiting
         * state until all modules return true. For example a module which implements ident
@@ -1025,15 +773,14 @@ class CoreExport Module : public classbase, public usecountbase
         * Return 1 from this function to block the mode character from being processed entirely.
         * @param user The user who is sending the mode
         * @param chan The channel the mode is being sent to (or NULL if a usermode)
-        * @param mode The mode character being set
+        * @param mh The mode handler for the mode being changed
         * @param param The parameter for the mode or an empty string
         * @param adding true of the mode is being added, false if it is being removed
-        * @param pcnt The parameter count for the mode (0 or 1)
         * @return ACR_DENY to deny the mode, ACR_DEFAULT to do standard mode checking, and ACR_ALLOW
         * to skip all permission checking. Please note that for remote mode changes, your return value
         * will be ignored!
         */
-       virtual ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string &param, bool adding, int pcnt);
+       virtual ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding);
 
        /** Called whenever a user joins a channel, to determine if key checks should go ahead or not.
         * This method will always be called for each join, wether or not the channel is actually +k, and
@@ -1082,32 +829,21 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual ModResult OnExtBanCheck(User* user, Channel* chan, char type);
 
-       /** 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.
-        * @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);
-
        /** Called whenever a change of a local users displayed host is attempted.
         * Return 1 to deny the host change, or 0 to allow it.
         * @param user The user whos host will be changed
         * @param newhost The new hostname
         * @return 1 to deny the host change, 0 to allow
         */
-       virtual ModResult OnChangeLocalUserHost(LocalUser* user, const std::string &newhost);
+       virtual ModResult OnPreChangeHost(LocalUser* user, const std::string &newhost);
 
-       /** Called whenever a change of a local users GECOS (fullname field) is attempted.
-        * return 1 to deny the name change, or 0 to allow it.
-        * @param user The user whos GECOS will be changed
-        * @param newhost The new GECOS
-        * @return 1 to deny the GECOS change, 0 to allow
+       /** Called whenever a change of a local users real name is attempted.
+        * return MOD_RES_DENY to deny the name change, or MOD_RES_ALLOW to allow it.
+        * @param user The user whos real name will be changed
+        * @param newhost The new real name.
+        * @return MOD_RES_DENY to deny the real name change, MOD_RES_ALLOW to allow
         */
-       virtual ModResult OnChangeLocalUserGECOS(LocalUser* user, const std::string &newhost);
+       virtual ModResult OnPreChangeRealName(LocalUser* user, const std::string &newhost);
 
        /** Called before a topic is changed.
         * Return 1 to deny the topic change, 0 to check details on the change, -1 to let it through with no checks
@@ -1127,18 +863,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 Request class is sent to your module by another module.
-        * The value of Request::id should be used to determine the type of request.
-        * @param request The Request class being received
-        */
-       virtual void OnRequest(Request& request);
-
        /** 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).
@@ -1151,14 +875,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.
@@ -1167,30 +883,6 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual void OnPostConnect(User* user);
 
-       /** Called whenever a ban is added to a channel's list.
-        * Return a non-zero value to 'eat' the mode change and prevent the ban from being added.
-        * @param source The user adding the ban
-        * @param channel The channel the ban is being added to
-        * @param banmask The ban mask being added
-        * @return 1 to block the ban, 0 to continue as normal
-        */
-       virtual ModResult OnAddBan(User* source, Channel* channel,const std::string &banmask);
-
-       /** Called whenever a ban is removed from a channel's list.
-        * Return a non-zero value to 'eat' the mode change and prevent the ban from being removed.
-        * @param source The user deleting the ban
-        * @param channel The channel the ban is being deleted from
-        * @param banmask The ban mask being deleted
-        * @return 1 to block the unban, 0 to continue as normal
-        */
-       virtual ModResult OnDelBan(User* source, Channel* channel,const std::string &banmask);
-
-       /** Called to install an I/O hook on an event handler
-        * @param user The socket to possibly install the I/O hook on
-        * @param via The port that the user connected on
-        */
-       virtual void OnHookIO(StreamSocket* user, ListenSocket* via);
-
        /** Called when a port accepts a connection
         * Return MOD_RES_ACCEPT if you have used the file descriptor.
         * @param fd The file descriptor returned from accept()
@@ -1200,71 +892,6 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual ModResult OnAcceptConnection(int fd, ListenSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
 
-       /** Called immediately after any 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.
-        * There are no return values from this call as all modules get an opportunity if required to
-        * process the connection.
-        * @param sock The socket in question
-        * @param client The client IP address and port
-        * @param server The server IP address and port
-        */
-       virtual void OnStreamSocketAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
-
-       /**
-        * 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
-        * @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);
-
-       /** Called immediately before any socket is closed. When this event is called, shutdown()
-        * has not yet been called on the socket.
-        * @param sock The socket in question
-        */
-       virtual void OnStreamSocketClose(StreamSocket* sock);
-
-       /** Called immediately upon connection of an outbound BufferedSocket which has been hooked
-        * by a module.
-        * @param sock The socket in question
-        */
-       virtual void OnStreamSocketConnect(StreamSocket* sock);
-
-       /**
-        * Called when the stream socket has data to read
-        * @param sock The socket that is ready
-        * @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);
-
-       /** Called whenever a user sets away or returns from being away.
-        * The away message is available as a parameter, but should not be modified.
-        * At this stage, it has already been copied into the user record.
-        * If awaymsg is empty, the user is returning from away.
-        * @param user The user setting away
-        * @param awaymsg The away message of the user, or empty if returning from away
-        * @return nonzero if the away message should be blocked - should ONLY be nonzero for LOCAL users (IS_LOCAL) (no output is returned by core)
-        */
-       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
@@ -1278,223 +905,46 @@ class CoreExport Module : public classbase, public usecountbase
         */
        virtual ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass);
 
-       /** Add test suite hooks here. These are used for testing functionality of a module
-        * via the --testsuite debugging parameter.
-        */
-       virtual void OnRunTestSuite();
-
-       /** Called for every item in a NAMES list, so that modules may reformat portions of it as they see fit.
-        * For example NAMESX, channel mode +u and +I, and UHNAMES. If the nick is set to an empty string by any
-        * module, then this will cause the nickname not to be displayed at all.
-        */
-       virtual void OnNamesListItem(User* issuer, Membership* item, std::string &prefixes, std::string &nick);
-
-       virtual ModResult OnNumeric(User* user, unsigned int numeric, const std::string &text);
-
-       /** 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 line The raw line to send; modifiable, if empty no line will be returned.
-        */
-       virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line);
+       virtual ModResult OnNumeric(User* user, const 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.
         * @param user The user whose IP is being set
         */
        virtual void OnSetUserIP(LocalUser* user);
-};
-
-
-#define CONF_NO_ERROR          0x000000
-#define CONF_NOT_A_NUMBER      0x000010
-#define CONF_INT_NEGATIVE      0x000080
-#define CONF_VALUE_NOT_FOUND   0x000100
-#define CONF_FILE_NOT_FOUND    0x000200
-
-
-/** Allows reading of values from configuration files
- * This class allows a module to read from either the main configuration file (inspircd.conf) or from
- * a module-specified configuration file. It may either be instantiated with one parameter or none.
- * Constructing the class using one parameter allows you to specify a path to your own configuration
- * file, otherwise, inspircd.conf is read.
- */
-class CoreExport ConfigReader : public interfacebase
-{
-  protected:
-       /** Error code
-        */
-       long error;
-
-  public:
-       /** Default constructor.
-        * This constructor initialises the ConfigReader class to read the inspircd.conf file
-        * as specified when running ./configure.
-        */
-       ConfigReader();
-       /** Default destructor.
-        * This method destroys the ConfigReader class.
-        */
-       ~ConfigReader();
-
-       /** Retrieves a value from the config file.
-        * This method retrieves a value from the config file. Where multiple copies of the tag
-        * exist in the config file, index indicates which of the values to retrieve.
-        */
-       std::string ReadValue(const std::string &tag, const std::string &name, int index, bool allow_linefeeds = false);
-       /** Retrieves a value from the config file.
-        * This method retrieves a value from the config file. Where multiple copies of the tag
-        * exist in the config file, index indicates which of the values to retrieve. If the
-        * tag is not found the default value is returned instead.
-        */
-       std::string ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds = false);
-
-       /** Retrieves a boolean value from the config file.
-        * This method retrieves a boolean value from the config file. Where multiple copies of the tag
-        * exist in the config file, index indicates which of the values to retrieve. The values "1", "yes"
-        * and "true" in the config file count as true to ReadFlag, and any other value counts as false.
-        */
-       bool ReadFlag(const std::string &tag, const std::string &name, int index);
-       /** Retrieves a boolean value from the config file.
-        * This method retrieves a boolean value from the config file. Where multiple copies of the tag
-        * exist in the config file, index indicates which of the values to retrieve. The values "1", "yes"
-        * and "true" in the config file count as true to ReadFlag, and any other value counts as false.
-        * If the tag is not found, the default value is used instead.
-        */
-       bool ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index);
-
-       /** Retrieves an integer value from the config file.
-        * This method retrieves an integer value from the config file. Where multiple copies of the tag
-        * exist in the config file, index indicates which of the values to retrieve. Any invalid integer
-        * values in the tag will cause the objects error value to be set, and any call to GetError() will
-        * return CONF_INVALID_NUMBER to be returned. need_positive is set if the number must be non-negative.
-        * If a negative number is placed into a tag which is specified positive, 0 will be returned and GetError()
-        * will return CONF_INT_NEGATIVE. Note that need_positive is not suitable to get an unsigned int - you
-        * should cast the result to achieve that effect.
-        */
-       int ReadInteger(const std::string &tag, const std::string &name, int index, bool need_positive);
-       /** Retrieves an integer value from the config file.
-        * This method retrieves an integer value from the config file. Where multiple copies of the tag
-        * exist in the config file, index indicates which of the values to retrieve. Any invalid integer
-        * values in the tag will cause the objects error value to be set, and any call to GetError() will
-        * return CONF_INVALID_NUMBER to be returned. needs_unsigned is set if the number must be unsigned.
-        * If a signed number is placed into a tag which is specified unsigned, 0 will be returned and GetError()
-        * will return CONF_NOT_UNSIGNED. If the tag is not found, the default value is used instead.
-        */
-       int ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool need_positive);
-
-       /** Returns the last error to occur.
-        * Valid errors can be found by looking in modules.h. Any nonzero value indicates an error condition.
-        * A call to GetError() resets the error flag back to 0.
-        */
-       long GetError();
-
-       /** Counts the number of times a given tag appears in the config file.
-        * This method counts the number of times a tag appears in a config file, for use where
-        * there are several tags of the same kind, e.g. with opers and connect types. It can be
-        * used with the index value of ConfigReader::ReadValue to loop through all copies of a
-        * multiple instance tag.
-        */
-       int Enumerate(const std::string &tag);
-};
 
-
-
-/** Caches a text file into memory and can be used to retrieve lines from it.
- * This class contains methods for read-only manipulation of a text file in memory.
- * Either use the constructor type with one parameter to load a file into memory
- * at construction, or use the LoadFile method to load a file.
- */
-class CoreExport FileReader : public classbase
-{
-       /** The file contents
-        */
-       std::vector<std::string> fc;
-
-       /** Content size in bytes
-        */
-       unsigned long contentsize;
-
-       /** Calculate content size in bytes
+       /** Called whenever a ServiceProvider is registered.
+        * @param service ServiceProvider being registered.
         */
-       void CalcSize();
+       virtual void OnServiceAdd(ServiceProvider& service);
 
- public:
-       /** Default constructor.
-        * This method does not load any file into memory, you must use the LoadFile method
-        * after constructing the class this way.
+       /** Called whenever a ServiceProvider is unregistered.
+        * @param service ServiceProvider being unregistered.
         */
-       FileReader();
+       virtual void OnServiceDel(ServiceProvider& service);
 
-       /** Secondary constructor.
-        * This method initialises the class with a file loaded into it ready for GetLine and
-        * and other methods to be called. If the file could not be loaded, FileReader::FileSize
-        * returns 0.
+       /** Called whenever a message is about to be written to a user.
+        * @param user The user who is having a message sent to them.
+        * @param msg The message which is being written to the user.
+        * @return MOD_RES_ALLOW to explicitly allow the message to be sent, MOD_RES_DENY to explicitly
+        * deny the message from being sent, or MOD_RES_PASSTHRU to let another module handle the event.
         */
-       FileReader(const std::string &filename);
-
-       /** Default destructor.
-        * This deletes the memory allocated to the file.
-        */
-       ~FileReader();
-
-       /** Used to load a file.
-        * This method loads a file into the class ready for GetLine and
-        * and other methods to be called. If the file could not be loaded, FileReader::FileSize
-        * returns 0.
-        */
-       void LoadFile(const std::string &filename);
-
-       /** Returns the whole content of the file as std::string
-        */
-       std::string Contents();
-
-       /** Returns the entire size of the file as std::string
-        */
-       unsigned long ContentSize();
-
-       /** Returns true if the file exists
-        * This function will return false if the file could not be opened.
-        */
-       bool Exists();
-
-       /** Retrieve one line from the file.
-        * This method retrieves one line from the text file. If an empty non-NULL string is returned,
-        * the index was out of bounds, or the line had no data on it.
-        */
-       std::string GetLine(int x);
-
-       /** Returns the size of the file in lines.
-        * This method returns the number of lines in the read file. If it is 0, no lines have been
-        * read into memory, either because the file is empty or it does not exist, or cannot be
-        * opened due to permission problems.
-        */
-       int FileSize();
+       virtual ModResult OnUserWrite(LocalUser* user, ClientProtocol::Message& msg);
 };
 
-/** A list of modules
- */
-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;
+
  private:
        /** Holds a string describing the last module error to occur
         */
        std::string LastModuleError;
 
-       /** Total number of modules loaded into the ircd
-        */
-       int ModCount;
-
        /** List of loaded modules and shared object/dll handles
         * keyed by module name
         */
@@ -1506,18 +956,49 @@ class CoreExport ModuleManager
                PRIO_STATE_LAST
        } prioritizationState;
 
-       /** Internal unload module hook */
-       bool CanUnload(Module*);
+       /** Loads all core modules (core_*)
+        */
+       void LoadCoreModules(std::map<std::string, ServiceList>& servicemap);
+
+       /** Calls the Prioritize() method in all loaded modules
+        * @return True if all went well, false if a dependency loop was detected
+        */
+       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;
 
        /** Event handler hooks.
         * This needs to be public to be used by FOREACH_MOD and friends.
         */
-       IntModuleList EventHandlers[I_END];
+       Module::List EventHandlers[I_END];
 
        /** List of data services keyed by name */
        std::multimap<std::string, ServiceProvider*> DataProviders;
 
+       /** A list of ServiceProviders waiting to be registered.
+        * Non-NULL when constructing a Module, NULL otherwise.
+        * When non-NULL ServiceProviders add themselves to this list on creation and the core
+        * automatically registers them (that is, call AddService()) after the Module is constructed,
+        * and before Module::init() is called.
+        * If a service is created after the construction of the Module (for example in init()) it
+        * has to be registered manually.
+        */
+       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();
@@ -1544,12 +1025,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.
@@ -1558,7 +1033,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().
@@ -1590,6 +1065,11 @@ class CoreExport ModuleManager
         */
        void DetachAll(Module* mod);
 
+       /** Attach all events to a module (used on module load)
+        * @param mod Module to attach to all events
+        */
+       void AttachAll(Module* mod);
+
        /** Returns text describing the last module error
         * @return The last error message to occur
         */
@@ -1609,25 +1089,18 @@ 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*);
 
-       /** Get the total number of currently loaded modules
-        * @return The number of loaded modules
+       /** 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.
         */
-       int GetCount()
-       {
-               return this->ModCount;
-       }
+       bool CanUnload(Module* mod);
 
        /** Find a module by name, and return a Module* to it.
         * This is preferred over iterating the module lists yourself.
@@ -1642,6 +1115,11 @@ class CoreExport ModuleManager
        /** Unregister a service provided by a module */
        void DelService(ServiceProvider&);
 
+       /** Register all services in a given ServiceList
+        * @param list The list containing the services to register
+        */
+       void AddServices(const ServiceList& list);
+
        inline void AddServices(ServiceProvider** list, int count)
        {
                for(int i=0; i < count; i++)
@@ -1658,13 +1136,21 @@ class CoreExport ModuleManager
                return static_cast<T*>(FindService(SERVICE_DATA, name));
        }
 
-       /** Return a list of all modules matching the given filter
-        * @param filter This int is a bitmask of flags set in Module::Flags,
-        * such as VF_VENDOR or VF_STATIC. If you wish to receive a list of
-        * all modules with no filtering, set this to 0.
-        * @return The list of module names
+       /** Get a map of all loaded modules keyed by their name
+        * @return A ModuleMap containing all loaded modules
         */
-       const std::vector<std::string> GetAllModuleNames(int filter);
+       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
@@ -1677,66 +1163,13 @@ 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
-
-struct AllCommandList {
-       typedef Command* (*fn)(Module*);
-       AllCommandList(fn cmd);
-};
-#define COMMAND_INIT(x) static Command* MK_ ## x(Module* m) { return new x(m); } \
-       static const AllCommandList PREP_ ## x(&MK_ ## x);
-
-struct AllModuleList {
-       typedef Module* (*fn)();
-       fn init;
-       std::string name;
-       AllModuleList(fn mod, const std::string& Name);
-};
-
-#define MODULE_INIT(x) static Module* MK_ ## x() { return new x; } \
-       static const AllModuleList PREP_ ## x(&MK_ ## x, MODNAMESTR);
-
-#define MODNAMESTR MODNAMESTR_FN_2(MODNAME)
-#define MODNAMESTR_FN_2(x) MODNAMESTR_FN_1(x)
-#define MODNAMESTR_FN_1(x) #x
-
-#else
-
 /** This definition is used as shorthand for the various classes
  * and functions needed to make a module loadable by the OS.
  * It defines the class factory and external init_module function.
  */
-#ifdef _WIN32
-
-#define MODULE_INIT(y) \
-       extern "C" DllExport Module * MODULE_INIT_SYM() \
-       { \
-               return new y; \
-       } \
-       BOOLEAN WINAPI DllMain(HINSTANCE hDllHandle, DWORD nReason, LPVOID Reserved) \
-       { \
-               switch ( nReason ) \
-               { \
-                       case DLL_PROCESS_ATTACH: \
-                       case DLL_PROCESS_DETACH: \
-                               break; \
-               } \
-               return TRUE; \
-       } \
-       extern "C" DllExport const char inspircd_src_version[] = VERSION " r" REVISION;
-
-#else
-
 #define MODULE_INIT(y) \
        extern "C" DllExport Module * MODULE_INIT_SYM() \
        { \
                return new y; \
        } \
-       extern "C" const char inspircd_src_version[] = VERSION " r" REVISION;
-#endif
-
-#define COMMAND_INIT(c) MODULE_INIT(CommandModule<c>)
-
-#endif
-
-#endif
+       extern "C" DllExport const char inspircd_src_version[] = INSPIRCD_VERSION;
diff --git a/include/modules/account.h b/include/modules/account.h
new file mode 100644 (file)
index 0000000..0368127
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   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/>.
+ */
+
+
+#pragma once
+
+#include <map>
+#include <string>
+
+#include "event.h"
+
+typedef StringExtItem AccountExtItem;
+
+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/away.h b/include/modules/away.h
new file mode 100644 (file)
index 0000000..f231f0a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2016-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/>.
+ */
+
+
+#pragma once
+
+#include "event.h"
+
+namespace Away
+{
+       class EventListener;
+       class EventProvider;
+}
+
+class Away::EventListener
+       : public Events::ModuleEventListener
+{
+ protected:
+       EventListener(Module* mod)
+               : ModuleEventListener(mod, "event/away")
+       {
+       }
+
+ public:
+       /** Called when a user wishes to mark themselves as away.
+        * @param user The user who is going away.
+        * @param message The away message that the user set.
+        * @return Either MOD_RES_ALLOW to allow the user to mark themself as away, MOD_RES_DENY to
+        *         disallow the user to mark themself as away, or MOD_RES_PASSTHRU to let another module
+        *         handle the event.
+        */
+       virtual ModResult OnUserPreAway(LocalUser* user, std::string& message)
+       {
+               return MOD_RES_PASSTHRU;
+       }
+
+       /** Called when a user wishes to mark themselves as back.
+        * @param user The user who is going away.
+        * @param message The away message that the user set.
+        * @return Either MOD_RES_ALLOW to allow the user to mark themself as back, MOD_RES_DENY to
+        *         disallow the user to mark themself as back, or MOD_RES_PASSTHRU to let another module
+        *         handle the event.
+        */
+       virtual ModResult OnUserPreBack(LocalUser* user)
+       {
+               return MOD_RES_PASSTHRU;
+       }
+
+       /** Called when a user has marked themself as away.
+        * @param user The user who has gone away.
+        */
+       virtual void OnUserAway(User* user) = 0;
+
+       /** Called when a user has returned from being away.
+        * @param user The user who has returned from being away.
+        */
+       virtual void OnUserBack(User* user) = 0;
+};
+
+class Away::EventProvider
+       : public Events::ModuleEventProvider
+{
+ public:
+       EventProvider(Module* mod)
+               : ModuleEventProvider(mod, "event/away")
+       {
+       }
+};
diff --git a/include/modules/callerid.h b/include/modules/callerid.h
new file mode 100644 (file)
index 0000000..ed03419
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2018 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
+
+namespace CallerID
+{
+       class APIBase;
+       class API;
+}
+
+class CallerID::APIBase : public DataProvider
+{
+ public:
+       APIBase(Module* parent)
+               : DataProvider(parent, "m_callerid_api")
+       {
+       }
+
+       /** Determines whether \p source is on the accept list of \p target.
+        * @param source The user to search for in the accept list.
+        * @param target The user who's accept list to search in.
+        * @return True if \p source is on \p target's accept list; otherwise, false.
+        */
+       virtual bool IsOnAcceptList(User* source, User* target) = 0;
+};
+
+class CallerID::API : public dynamic_reference<CallerID::APIBase>
+{
+ public:
+       API(Module* parent)
+               : dynamic_reference<CallerID::APIBase>(parent, "m_callerid_api")
+       {
+       }
+};
diff --git a/include/modules/cap.h b/include/modules/cap.h
new file mode 100644 (file)
index 0000000..6dcb9f3
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * 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 Cap
+{
+       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
+       {
+        public:
+               ExtItem(Module* mod);
+               std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE;
+               void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE;
+       };
+
+       class Capability;
+
+       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")
+               {
+               }
+
+               /** 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
+       {
+        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);
+               }
+
+               /** 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
+               {
+                       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 (activate)
+                                       manager->AddCap(this);
+                               else
+                                       manager->DelCap(this);
+                       }
+               }
+
+               /** 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
+               {
+                       return ((IsRegistered() && (extitem->get(user) & CAP_302_BIT)) ? CAP_302 : CAP_LEGACY);
+               }
+
+               /** 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)
+               {
+                       return true;
+               }
+
+               /** 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)
+               {
+                       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;
+               }
+       };
+
+       class MessageBase : public ClientProtocol::Message
+       {
+        public:
+               MessageBase(const std::string& subcmd)
+                       : ClientProtocol::Message("CAP", ServerInstance->Config->ServerName)
+               {
+                       PushParamPlaceholder();
+                       PushParam(subcmd);
+               }
+
+               void SetUser(LocalUser* user)
+               {
+                       if (user->registered & REG_NICK)
+                               ReplaceParamRef(0, user->nick);
+                       else
+                               ReplaceParam(0, "*");
+               }
+       };
+}
diff --git a/include/modules/ctctags.h b/include/modules/ctctags.h
new file mode 100644 (file)
index 0000000..d8798de
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2019 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
+
+#include "event.h"
+
+namespace CTCTags
+{
+       class EventListener;
+       class TagMessage;
+       class TagMessageDetails;
+}
+
+class CTCTags::TagMessage : public ClientProtocol::Message
+{
+ public:
+       TagMessage(User* source, const Channel* targetchan, const ClientProtocol::TagMap& Tags)
+               : ClientProtocol::Message("TAGMSG", source)
+       {
+               PushParamRef(targetchan->name);
+               AddTags(Tags);
+               SetSideEffect(true);
+       }
+
+       TagMessage(User* source, const User* targetuser, const ClientProtocol::TagMap& Tags)
+               : ClientProtocol::Message("TAGMSG", source)
+       {
+               if (targetuser->registered & REG_NICK)
+                       PushParamRef(targetuser->nick);
+               else
+                       PushParam("*");
+               AddTags(Tags);
+               SetSideEffect(true);
+       }
+
+       TagMessage(User* source, const char* targetstr, const ClientProtocol::TagMap& Tags)
+               : ClientProtocol::Message("TAGMSG", source)
+       {
+               PushParam(targetstr);
+               AddTags(Tags);
+               SetSideEffect(true);
+       }
+};
+
+class CTCTags::TagMessageDetails
+{
+ public:
+       /** Whether to echo the tags at all. */
+       bool echo;
+
+       /* Whether to send the original tags back to clients with echo-message support. */
+       bool echo_original;
+
+       /** The users who are exempted from receiving this message. */
+       CUList exemptions;
+
+       /** IRCv3 message tags sent to the server by the user. */
+       const ClientProtocol::TagMap tags_in;
+
+       /** IRCv3 message tags sent out to users who get this message. */
+       ClientProtocol::TagMap tags_out;
+
+       TagMessageDetails(const ClientProtocol::TagMap& tags)
+               : echo(true)
+               , echo_original(false)
+               , tags_in(tags)
+       {
+       }
+};
+
+class CTCTags::EventListener
+       : public Events::ModuleEventListener
+{
+ protected:
+       EventListener(Module* mod, unsigned int eventprio = DefaultPriority)
+               : ModuleEventListener(mod, "event/tagmsg", eventprio)
+       {
+       }
+
+ public:
+       /** Called before a user sends a tag message to a channel, a user, or a server glob mask.
+        * @param user The user sending the message.
+        * @param target The target of the message. This can either be a channel, a user, or a server
+        *               glob mask.
+        * @param details Details about the message such as the message tags or whether to echo. See the
+        *                TagMessageDetails class for more information.
+        * @return MOD_RES_ALLOW to explicitly allow the message, MOD_RES_DENY to explicitly deny the
+        *         message, or MOD_RES_PASSTHRU to let another module handle the event.
+        */
+       virtual ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, TagMessageDetails& details) { return MOD_RES_PASSTHRU; }
+       
+       /** Called immediately after a user sends a tag message to a channel, a user, or a server glob mask.
+        * @param user The user sending the message.
+        * @param target The target of the message. This can either be a channel, a user, or a server
+        *               glob mask.
+        * @param details Details about the message such as the message tags or whether to echo. See the
+        *                TagMessageDetails class for more information.
+        */
+       virtual void OnUserPostTagMessage(User* user, const MessageTarget& target, const TagMessageDetails& details) { }
+
+       /** Called immediately before a user sends a tag message to a channel, a user, or a server glob mask.
+        * @param user The user sending the message.
+        * @param target The target of the message. This can either be a channel, a user, or a server
+        *               glob mask.
+        * @param details Details about the message such as the message tags or whether to echo. See the
+        *                TagMessageDetails class for more information.
+        */
+       virtual void OnUserTagMessage(User* user, const MessageTarget& target, const TagMessageDetails& details) { }
+
+       /** Called when a tag message sent by a user to a channel, a user, or a server glob mask is blocked.
+        * @param user The user sending the message.
+        * @param target The target of the message. This can either be a channel, a user, or a server
+        *               glob mask.
+        * @param details Details about the message such as the message tags or whether to echo. See the
+        *                TagMessageDetails class for more information.
+        */
+       virtual void OnUserTagMessageBlocked(User* user, const MessageTarget& target, const TagMessageDetails& details) { }
+};
diff --git a/include/modules/dns.h b/include/modules/dns.h
new file mode 100644 (file)
index 0000000..3db6517
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Adam <Adam@anope.org>
+ *   Copyright (C) 2003-2013 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
+ * 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 DNS
+{
+       /** Valid query types
+        */
+       enum QueryType
+       {
+               /* Nothing */
+               QUERY_NONE,
+               /* A simple A lookup */
+               QUERY_A = 1,
+               /* A CNAME lookup */
+               QUERY_CNAME = 5,
+               /* Reverse DNS lookup */
+               QUERY_PTR = 12,
+               /* TXT */
+               QUERY_TXT = 16,
+               /* IPv6 AAAA lookup */
+               QUERY_AAAA = 28
+       };
+
+       /** Flags that can be AND'd into DNSPacket::flags to receive certain values
+        */
+       enum
+       {
+               QUERYFLAGS_QR = 0x8000,
+               QUERYFLAGS_OPCODE = 0x7800,
+               QUERYFLAGS_AA = 0x400,
+               QUERYFLAGS_TC = 0x200,
+               QUERYFLAGS_RD = 0x100,
+               QUERYFLAGS_RA = 0x80,
+               QUERYFLAGS_Z = 0x70,
+               QUERYFLAGS_RCODE = 0xF
+       };
+
+       enum Error
+       {
+               ERROR_NONE,
+               ERROR_UNKNOWN,
+               ERROR_UNLOADED,
+               ERROR_TIMEDOUT,
+               ERROR_MALFORMED,
+               ERROR_NOT_AN_ANSWER,
+               ERROR_NONSTANDARD_QUERY,
+               ERROR_FORMAT_ERROR,
+               ERROR_SERVER_FAILURE,
+               ERROR_DOMAIN_NOT_FOUND,
+               ERROR_NOT_IMPLEMENTED,
+               ERROR_REFUSED,
+               ERROR_NO_RECORDS,
+               ERROR_INVALIDTYPE
+       };
+
+       typedef uint16_t RequestId;
+
+       const int PORT = 53;
+
+       class Exception : public ModuleException
+       {
+        public:
+               Exception(const std::string& message) : ModuleException(message) { }
+       };
+
+       struct Question
+       {
+               std::string name;
+               QueryType type;
+
+               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
+               {
+                       size_t operator()(const Question& question) const
+                       {
+                               return irc::insensitive()(question.name);
+                       }
+               };
+       };
+
+       struct ResourceRecord : Question
+       {
+               unsigned int ttl;
+               std::string rdata;
+               time_t created;
+
+               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
+       {
+               Question question;
+               std::vector<ResourceRecord> answers;
+               Error error;
+               bool cached;
+
+               Query() : error(ERROR_NONE), cached(false) { }
+               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;
+       class Request;
+
+       /** DNS manager
+        */
+       class Manager : public DataProvider
+       {
+        public:
+               Manager(Module* mod) : DataProvider(mod, "DNS") { }
+
+               virtual void Process(Request* req) = 0;
+               virtual void RemoveRequest(Request* req) = 0;
+               virtual std::string GetErrorStr(Error) = 0;
+       };
+
+       /** A DNS query.
+        */
+       class Request : public Timer
+       {
+        protected:
+               Manager* const manager;
+        public:
+               Question question;
+               /* Use result cache if available */
+               bool use_cache;
+               /* Request 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->ConfValue("dns")->getDuration("timeout", 5, 1))
+                       , manager(mgr)
+                       , question(addr, qt)
+                       , use_cache(usecache)
+                       , id(0)
+                       , creator(mod)
+               {
+               }
+
+               virtual ~Request()
+               {
+                       manager->RemoveRequest(this);
+               }
+
+               /** Called when this request succeeds
+                * @param r The query sent back from the nameserver
+                */
+               virtual void OnLookupComplete(const Query* req) = 0;
+
+               /** Called when this request fails or times out.
+                * @param r The query sent back from the nameserver, check the error code.
+                */
+               virtual void OnError(const Query* req) { }
+
+               /** Used to time out the query, calls OnError and asks the TimerManager
+                * to delete this request
+                */
+               bool Tick(time_t now) CXX11_OVERRIDE
+               {
+                       Query rr(this->question);
+                       rr.error = ERROR_TIMEDOUT;
+                       this->OnError(&rr);
+                       delete this;
+                       return false;
+               }
+       };
+
+} // namespace DNS
diff --git a/include/modules/exemption.h b/include/modules/exemption.h
new file mode 100644 (file)
index 0000000..a33d67f
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2016-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/>.
+ */
+
+
+#pragma once
+
+#include "event.h"
+
+namespace CheckExemption
+{
+       class EventListener;
+       class EventProvider;
+
+       /** Helper function for calling the CheckExemption::EventListener::OnCheckExemption event.
+        * @param prov The CheckExemption::EventProvider which is calling the event.
+        * @param user The user to check exemption for.
+        * @param chan The channel to check exemption on.
+        * @param restriction The restriction to check for.
+        * @return Either MOD_RES_ALLOW if the exemption was confirmed, MOD_RES_DENY if the exemption was
+        *         denied or MOD_RES_PASSTHRU if no module handled the event.
+        */
+       inline ModResult Call(const CheckExemption::EventProvider& prov, User* user, Channel* chan, const std::string& restriction);
+}
+
+class CheckExemption::EventListener
+       : public Events::ModuleEventListener
+{
+ protected:
+       EventListener(Module* mod, unsigned int eventprio = DefaultPriority)
+               : ModuleEventListener(mod, "event/exemption", eventprio)
+       {
+       }
+
+ public:
+       /** Called when checking if a user is exempt from something.
+        * @param user The user to check exemption for.
+        * @param chan The channel to check exemption on.
+        * @param restriction The restriction to check for.
+        * @return Either MOD_RES_ALLOW to confirm an exemption, MOD_RES_DENY to deny an exemption,
+        *         or MOD_RES_PASSTHRU to let another module handle the event.
+        */
+       virtual ModResult OnCheckExemption(User* user, Channel* chan, const std::string& restriction) = 0;
+};
+
+class CheckExemption::EventProvider
+       : public Events::ModuleEventProvider
+{
+ public:
+       EventProvider(Module* mod)
+               : ModuleEventProvider(mod, "event/exemption")
+       {
+       }
+};
+
+inline ModResult CheckExemption::Call(const CheckExemption::EventProvider& prov, User* user, Channel* chan, const std::string& restriction)
+{
+       ModResult result;
+       FIRST_MOD_RESULT_CUSTOM(prov, CheckExemption::EventListener, OnCheckExemption, result, (user, chan, restriction));
+       return result;
+}
diff --git a/include/modules/geolocation.h b/include/modules/geolocation.h
new file mode 100644 (file)
index 0000000..911a963
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2019 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
+
+namespace Geolocation
+{
+       class APIBase;
+       class API;
+       class Location;
+}
+
+class Geolocation::APIBase : public DataProvider
+{
+ public:
+       APIBase(Module* parent)
+               : DataProvider(parent, "geolocationapi")
+       {
+       }
+
+       /** Looks up the location of the specified user.
+        * @param user The user to look up the location of.
+        * @return Either an instance of the Location class or NULL if no location could be found.
+        */
+       virtual Location* GetLocation(User* user) = 0;
+
+       /** Looks up the location of the specified IP address.
+        * @param sa The IP address to look up the location of.
+        * @return Either an instance of the Location class or NULL if no location could be found.
+        */
+       virtual Location* GetLocation(irc::sockets::sockaddrs& sa) = 0;
+};
+
+class Geolocation::API : public dynamic_reference<Geolocation::APIBase>
+{
+ public:
+       API(Module* parent)
+               : dynamic_reference<Geolocation::APIBase>(parent, "geolocationapi")
+       {
+       }
+};
+
+class Geolocation::Location : public usecountbase
+{
+private:
+       /** The two character country code for this location. */
+       std::string code;
+
+       /** The country name for this location. */
+       std::string name;
+
+ public:
+       Location(const std::string& Code, const std::string& Name)
+               : code(Code)
+               , name(Name)
+       {
+       }
+
+       /** Retrieves the two character country code for this location. */
+       std::string GetCode() const { return code; }
+
+       /** Retrieves the country name for this location. */
+       std::string GetName() const { return name; }
+};
diff --git a/include/modules/hash.h b/include/modules/hash.h
new file mode 100644 (file)
index 0000000..7d46ee7
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 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
+ * 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.h"
+
+class HashProvider : public DataProvider
+{
+ public:
+       const unsigned int out_size;
+       const unsigned int block_size;
+       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)
+       {
+       }
+
+       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 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 ? GenerateRaw(key) : key;
+               kbuf.resize(block_size);
+
+               for (size_t n = 0; n < block_size; n++)
+               {
+                       hmac1.push_back(static_cast<char>(kbuf[n] ^ 0x5C));
+                       hmac2.push_back(static_cast<char>(kbuf[n] ^ 0x36));
+               }
+               hmac2.append(msg);
+               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
new file mode 100644 (file)
index 0000000..e1921cd
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ *   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   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
+
+#include "base.h"
+#include "event.h"
+
+#include <string>
+#include <sstream>
+#include <map>
+
+class HTTPQueryParameters : public insp::flat_multimap<std::string, std::string>
+{
+ public:
+       bool get(const std::string& key, std::string& value) const
+       {
+               const_iterator it = find(key);
+               if (it == end())
+                       return false;
+
+               value = it->second;
+               return true;
+       }
+
+       std::string getString(const std::string& key, const std::string& def = "") const
+       {
+               std::string value;
+               if (!get(key, value))
+                       return def;
+
+               return value;
+       }
+
+       template <typename T>
+       T getNum(const std::string& key, T def = 0) const
+       {
+               std::string value;
+               if (!get(key, value))
+                       return def;
+
+               return ConvToNum<T>(value);
+       }
+
+       unsigned long getDuration(const std::string& key, unsigned long def = 0) const
+       {
+               unsigned long value;
+               if (!InspIRCd::Duration(getString(key, "0"), value))
+                       return def;
+
+               return value;
+       }
+
+       bool getBool(const std::string& key, bool def = false) const
+       {
+               return getNum<bool>(key, def);
+       }
+};
+
+struct HTTPRequestURI
+{
+       std::string path;
+       HTTPQueryParameters query_params;
+       std::string fragment;
+};
+
+/** A modifyable list of HTTP header fields
+ */
+class HTTPHeaders
+{
+ protected:
+       std::map<std::string,std::string> headers;
+ public:
+
+       /** Set the value of a header
+        * Sets the value of the named header. If the header is already present, it will be replaced
+        */
+       void SetHeader(const std::string &name, const std::string &data)
+       {
+               headers[name] = data;
+       }
+
+       /** Set the value of a header, only if it doesn't exist already
+        * Sets the value of the named header. If the header is already present, it will NOT be updated
+        */
+       void CreateHeader(const std::string &name, const std::string &data)
+       {
+               if (!IsSet(name))
+                       SetHeader(name, data);
+       }
+
+       /** Remove the named header
+        */
+       void RemoveHeader(const std::string &name)
+       {
+               headers.erase(name);
+       }
+
+       /** Remove all headers
+        */
+       void Clear()
+       {
+               headers.clear();
+       }
+
+       /** Get the value of a header
+        * @return The value of the header, or an empty string
+        */
+       std::string GetHeader(const std::string &name)
+       {
+               std::map<std::string,std::string>::iterator it = headers.find(name);
+               if (it == headers.end())
+                       return std::string();
+
+               return it->second;
+       }
+
+       /** Check if the given header is specified
+        * @return true if the header is specified
+        */
+       bool IsSet(const std::string &name)
+       {
+               std::map<std::string,std::string>::iterator it = headers.find(name);
+               return (it != headers.end());
+       }
+
+       /** Get all headers, formatted by the HTTP protocol
+        * @return Returns all headers, formatted according to the HTTP protocol. There is no request terminator at the end
+        */
+       std::string GetFormattedHeaders()
+       {
+               std::string re;
+
+               for (std::map<std::string,std::string>::iterator i = headers.begin(); i != headers.end(); i++)
+                       re += i->first + ": " + i->second + "\r\n";
+
+               return re;
+       }
+};
+
+class HttpServerSocket;
+
+/** This class represents a HTTP request.
+ */
+class HTTPRequest
+{
+ protected:
+       std::string type;
+       std::string ipaddr;
+       std::string postdata;
+       HTTPRequestURI parseduri;
+
+ public:
+
+       HTTPHeaders *headers;
+       int errorcode;
+
+       /** A socket pointer, which you must return in your HTTPDocument class
+        * if you reply to this request.
+        */
+       HttpServerSocket* sock;
+
+       /** Initialize HTTPRequest.
+        * This constructor is called by m_httpd.so to initialize the class.
+        * @param request_type The request type, e.g. GET, POST, HEAD
+        * @param hdr The headers sent with the request
+        * @param opaque An opaque pointer used internally by m_httpd, which you must pass back to the module in your reply.
+        * @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(const std::string& request_type, const HTTPRequestURI& Parseduri,
+               HTTPHeaders* hdr, HttpServerSocket* socket, const std::string &ip, const std::string &pdata)
+               : type(request_type)
+               , ipaddr(ip)
+               , postdata(pdata)
+               , parseduri(Parseduri)
+               , headers(hdr)
+               , sock(socket)
+       {
+       }
+
+       /** Get the post data (request content).
+        * All post data will be returned, including carriage returns and linefeeds.
+        * @return The postdata
+        */
+       std::string& GetPostData()
+       {
+               return postdata;
+       }
+
+       /** Get the request type.
+        * Any request type can be intercepted, even ones which are invalid in the HTTP/1.1 spec.
+        * @return The request type, e.g. GET, POST, HEAD
+        */
+       std::string& GetType()
+       {
+               return type;
+       }
+
+       HTTPRequestURI& GetParsedURI()
+       {
+               return parseduri;
+       }
+
+       std::string& GetPath()
+       {
+               return GetParsedURI().path;
+       }
+
+       /** Get IP address of requester.
+        * The requesting system's ip address will be returned.
+        * @return The IP address as a string
+        */
+       std::string& GetIP()
+       {
+               return ipaddr;
+       }
+};
+
+/** If you want to reply to HTTP requests, you must return a HTTPDocumentResponse to
+ * the httpd module via the HTTPdAPI.
+ * When you initialize this class you initialize it with all components required to
+ * form a valid HTTP response: the document data and a response code.
+ * You can add additional HTTP headers, if you want.
+ */
+class HTTPDocumentResponse
+{
+ public:
+       /** Module that generated this reply
+        */
+       Module* const module;
+
+       std::stringstream* document;
+       unsigned int responsecode;
+
+       /** Any extra headers to include with the defaults
+        */
+       HTTPHeaders headers;
+
+       HTTPRequest& src;
+
+       /** Initialize a HTTPDocumentResponse ready for sending to the httpd module.
+        * @param mod A pointer to the module who responded to the request
+        * @param req The request you obtained from the HTTPRequest at an earlier time
+        * @param doc A stringstream containing the document body
+        * @param response A valid HTTP/1.0 or HTTP/1.1 response code. The response text will be determined for you
+        * based upon the response code.
+        */
+       HTTPDocumentResponse(Module* mod, HTTPRequest& req, std::stringstream* doc, unsigned int response)
+               : module(mod), document(doc), responsecode(response), src(req)
+       {
+       }
+};
+
+class HTTPdAPIBase : public DataProvider
+{
+ public:
+       HTTPdAPIBase(Module* parent)
+               : DataProvider(parent, "m_httpd_api")
+       {
+       }
+
+       /** Answer an incoming HTTP request with the provided document
+        * @param response The response created by your module that will be sent to the client
+        */
+       virtual void SendResponse(HTTPDocumentResponse& response) = 0;
+};
+
+/** The API provided by the httpd module that allows other modules to respond to incoming
+ * HTTP requests
+ */
+class HTTPdAPI : public dynamic_reference<HTTPdAPIBase>
+{
+ public:
+       HTTPdAPI(Module* parent)
+               : dynamic_reference<HTTPdAPIBase>(parent, "m_httpd_api")
+       {
+       }
+};
+
+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 (file)
index 0000000..e53d520
--- /dev/null
@@ -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/include/modules/ircv3.h b/include/modules/ircv3.h
new file mode 100644 (file)
index 0000000..ce2b70d
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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/cap.h"
+
+namespace IRCv3
+{
+       class WriteNeighborsWithCap;
+       template <typename T>
+       class CapTag;
+}
+
+class IRCv3::WriteNeighborsWithCap : public User::ForEachNeighborHandler
+{
+       const Cap::Capability& cap;
+       ClientProtocol::Event& protoev;
+
+       void Execute(LocalUser* user) CXX11_OVERRIDE
+       {
+               if (cap.get(user))
+                       user->Send(protoev);
+       }
+
+ public:
+       WriteNeighborsWithCap(User* user, ClientProtocol::Event& ev, const Cap::Capability& capability, bool include_self = false)
+               : cap(capability)
+               , protoev(ev)
+       {
+               user->ForEachNeighbor(*this, include_self);
+       }
+};
+
+/** Base class for simple message tags.
+ * Message tags provided by classes derived from this class will be sent to clients that have negotiated
+ * a client capability, also managed by this class.
+ *
+ * Derived classes specify the name of the capability and the message tag and provide a public GetValue()
+ * method with the following signature: const std::string* GetValue(ClientProtocol::Message& msg).
+ * The returned value determines whether to attach the tag to the message. If it is NULL, the tag won't
+ * be attached. If it is non-NULL the tag will be attached with the value in the string. If the string is
+ * empty the tag is attached without a value.
+ *
+ * Providers inheriting from this class don't accept incoming tags by default.
+ *
+ * For more control, inherit from ClientProtocol::MessageTagProvider directly.
+ *
+ * Template parameter T is the derived class.
+ */
+template <typename T>
+class IRCv3::CapTag : public ClientProtocol::MessageTagProvider
+{
+       Cap::Capability cap;
+       const std::string tagname;
+
+       bool ShouldSendTag(LocalUser* user, const ClientProtocol::MessageTagData& tagdata) CXX11_OVERRIDE
+       {
+               return cap.get(user);
+       }
+
+       void OnPopulateTags(ClientProtocol::Message& msg) CXX11_OVERRIDE
+       {
+               T& tag = static_cast<T&>(*this);
+               const std::string* const val = tag.GetValue(msg);
+               if (val)
+                       msg.AddTag(tagname, this, *val);
+       }
+
+ public:
+       /** Constructor.
+        * @param mod Module that owns the tag.
+        * @param capname Name of the client capability.
+        * A client capability with this name will be created. It will be available to all clients and it won't
+        * have a value.
+        * See Cap::Capability for more info on client capabilities.
+        * @param Tagname Name of the message tag, to use in the protocol.
+        */
+       CapTag(Module* mod, const std::string& capname, const std::string& Tagname)
+               : ClientProtocol::MessageTagProvider(mod)
+               , cap(mod, capname)
+               , tagname(Tagname)
+       {
+       }
+};
diff --git a/include/modules/ircv3_batch.h b/include/modules/ircv3_batch.h
new file mode 100644 (file)
index 0000000..841554b
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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
+
+// For CapReference
+#include "modules/cap.h"
+
+namespace IRCv3
+{
+       namespace Batch
+       {
+               typedef uint64_t RefTag;
+               class Manager;
+               class ManagerImpl;
+               class Batch;
+               struct BatchInfo;
+               class API;
+               class CapReference;
+
+               static const unsigned int MAX_BATCHES = (sizeof(intptr_t) * 8) - 1;
+       }
+}
+
+/** Batch Manager.
+ * Implements batch starting and stopping. When it becomes unavailable (due to e.g. module unload)
+ * all running batches are stopped.
+ */
+class IRCv3::Batch::Manager : public DataProvider, public ClientProtocol::MessageTagProvider
+{
+ public:
+       /** Constructor.
+        * @param mod Module that owns the Manager.
+        */
+       Manager(Module* mod)
+               : DataProvider(mod, "batchapi")
+               , ClientProtocol::MessageTagProvider(mod)
+       {
+       }
+
+       /** Start a batch.
+        * Check Batch::IsRunning() to learn if the batch has been started.
+        * @param batch Batch to start.
+        */
+       virtual void Start(Batch& batch) = 0;
+
+       /** End a batch.
+        * @param batch Batch to end.
+        */
+       virtual void End(Batch& batch) = 0;
+};
+
+/** Represents a batch.
+ * Batches are used to group together physically separate client protocol messages that logically belong
+ * together for one reason or another. The type of a batch, if provided, indicates what kind of grouping
+ * it does.
+ *
+ * Batch objects have two states: running and stopped. If a batch is running, messages can be added to it.
+ * If a message has been added to a batch and that message is sent to a client that negotiated the batch
+ * capability then the client will receive a message tag attached to the message indicating the batch that
+ * the message is a part of. If a message M is part of a batch B and M is sent to a client that hasn't yet
+ * received any message from batch B it will get a batch start message for B before M. When a batch B is
+ * stopped, every client that received at least one message which was in batch B will receive an end of
+ * batch message for B.
+ * A message may only be part of a single batch at any given time.
+ */
+class IRCv3::Batch::Batch
+{
+       Manager* manager;
+       const std::string type;
+       RefTag reftag;
+       std::string reftagstr;
+       unsigned int bit;
+       BatchInfo* batchinfo;
+       ClientProtocol::Message* batchstartmsg;
+
+       void Setup(unsigned int b)
+       {
+               bit = b;
+               reftag = (1 << bit);
+               reftagstr = ConvToStr(reftag);
+       }
+
+       unsigned int GetId() const { return bit; }
+       intptr_t GetBit() const { return reftag; }
+
+ public:
+       /** Constructor.
+        * The batch is initially stopped. To start it, pass it to Manager::Start().
+        * @param Type Batch type string, used to indicate what kind of grouping the batch does. May be empty.
+        */
+       Batch(const std::string& Type)
+               : manager(NULL)
+               , type(Type)
+               , batchinfo(NULL)
+               , batchstartmsg(NULL)
+       {
+       }
+
+       /** Destructor.
+        * If the batch is running, it is ended.
+        */
+       ~Batch()
+       {
+               if (manager)
+                       manager->End(*this);
+       }
+
+       /** Add a message to the batch.
+        * If the batch isn't running then this method does nothing.
+        * @param msg Message to add to the batch. If it is already part of any batch, this method is a no-op.
+        */
+       void AddToBatch(ClientProtocol::Message& msg)
+       {
+               if (manager)
+                       msg.AddTag("batch", manager, reftagstr, this);
+       }
+
+       /** Get batch reference tag which is an opaque id for the batch and is used in the client protocol.
+        * Only running batches have a reference tag assigned.
+        * @return Reference tag as a string, only valid if the batch is running.
+        */
+       const std::string& GetRefTagStr() const { return reftagstr; }
+
+       /** Get batch type.
+        * @return Batch type string.
+        */
+       const std::string& GetType() const { return type; }
+
+       /** Check whether the batch is running.
+        * Batches can be started with Manager::Start() and stopped with Manager::End().
+        * @return True if the batch is running, false otherwise.
+        */
+       bool IsRunning() const { return (manager != NULL); }
+
+       /** Get the batch start client protocol message.
+        * The returned message object can be manipulated to add extra parameters or labels to the message. The first
+        * parameter of the message is the batch reference tag generated by the module providing batch support.
+        * If the batch type string was specified, it will be the second parameter of the message.
+        * May only be called if IsRunning() == true.
+        * @return Mutable batch start client protocol message.
+        */
+       ClientProtocol::Message& GetBatchStartMessage() { return *batchstartmsg; }
+
+       friend class ManagerImpl;
+};
+
+/** Batch API. Use this to access the Manager.
+ */
+class IRCv3::Batch::API : public dynamic_reference_nocheck<Manager>
+{
+ public:
+       API(Module* mod)
+               : dynamic_reference_nocheck<Manager>(mod, "batchapi")
+       {
+       }
+};
+
+/** Reference to the batch cap.
+ * Can be used to check whether a user has the batch client cap enabled.
+ */
+class IRCv3::Batch::CapReference : public Cap::Reference
+{
+ public:
+       CapReference(Module* mod)
+               : Cap::Reference(mod, "batch")
+       {
+       }
+};
diff --git a/include/modules/ircv3_servertime.h b/include/modules/ircv3_servertime.h
new file mode 100644 (file)
index 0000000..b917531
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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 IRCv3
+{
+       namespace ServerTime
+       {
+               class Manager;
+               class API;
+
+               /** Format a unix timestamp into the format used by server-time.
+                * @param t Time to format.
+                * @return Time in server-time format, as a string.
+                */
+               inline std::string FormatTime(time_t t)
+               {
+                       return InspIRCd::TimeString(t, "%Y-%m-%dT%H:%M:%S.000Z", true);
+               }
+       }
+}
+
+/** Implements manipulating the server time on messages.
+ * A timestamp can be attached to outgoing client protocol messages to indicate the time when the message
+ * was generated by us. If a message has server time attached then recipient clients who have negotiated
+ * the appropriate protocol extension will receive it.
+ */
+class IRCv3::ServerTime::Manager : public DataProvider
+{
+ protected:
+       ClientProtocol::MessageTagProvider* tagprov;
+
+ public:
+       /** Constructor.
+        * @param mod Module that owns the Manager.
+        */
+       Manager(Module* mod)
+               : DataProvider(mod, "servertimeapi")
+       {
+       }
+
+       /** Set the server time on a message.
+        * @param msg Message to set the time on. No-op if the message already has server time set.
+        * @param t Unix timestamp to set.
+        */
+       void Set(ClientProtocol::Message& msg, time_t t)
+       {
+               Set(msg, FormatTime(t));
+       }
+
+       /** Set the server time on a message.
+        * @param msg Message to set the time on. No-op if the message already has server time set.
+        * @param timestr Timestamp to set. Must be in server time format.
+        * The FormatTime() function can be used to convert unix timestamps into the required format.
+        */
+       void Set(ClientProtocol::Message& msg, const std::string& timestr)
+       {
+               msg.AddTag("time", tagprov, timestr);
+       }
+};
+
+/** Server time API. Use this to access the Manager.
+ */
+class IRCv3::ServerTime::API : public dynamic_reference_nocheck<Manager>
+{
+ public:
+       API(Module* mod)
+               : dynamic_reference_nocheck<Manager>(mod, "servertimeapi")
+       {
+       }
+};
diff --git a/include/modules/ldap.h b/include/modules/ldap.h
new file mode 100644 (file)
index 0000000..aeb3aa3
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   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
+ * 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
+
+typedef int LDAPQuery;
+
+class LDAPException : public ModuleException
+{
+ public:
+       LDAPException(const std::string& reason) : ModuleException(reason) { }
+
+       virtual ~LDAPException() throw() { }
+};
+
+struct LDAPModification
+{
+       enum LDAPOperation
+       {
+               LDAP_ADD,
+               LDAP_DEL,
+               LDAP_REPLACE
+       };
+
+       LDAPOperation op;
+       std::string name;
+       std::vector<std::string> values;
+};
+
+typedef std::vector<LDAPModification> LDAPMods;
+
+struct LDAPAttributes : public std::map<std::string, std::vector<std::string> >
+{
+       size_t size(const std::string& attr) const
+       {
+               const std::vector<std::string>& array = this->getArray(attr);
+               return array.size();
+       }
+
+       const std::vector<std::string> keys() const
+       {
+               std::vector<std::string> k;
+               for (const_iterator it = this->begin(), it_end = this->end(); it != it_end; ++it)
+                       k.push_back(it->first);
+               return k;
+       }
+
+       const std::string& get(const std::string& attr) const
+       {
+               const std::vector<std::string>& array = this->getArray(attr);
+               if (array.empty())
+                       throw LDAPException("Empty attribute " + attr + " in LDAPResult::get");
+               return array[0];
+       }
+
+       const std::vector<std::string>& getArray(const std::string& attr) const
+       {
+               const_iterator it = this->find(attr);
+               if (it == this->end())
+                       throw LDAPException("Unknown attribute " + attr + " in LDAPResult::getArray");
+               return it->second;
+       }
+};
+
+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;
+
+       QueryType type;
+       LDAPQuery id;
+
+       LDAPResult()
+               : type(QUERY_UNKNOWN), id(-1)
+       {
+       }
+
+       size_t size() const
+       {
+               return this->messages.size();
+       }
+
+       bool empty() const
+       {
+               return this->messages.empty();
+       }
+
+       const LDAPAttributes& get(size_t sz) const
+       {
+               if (sz >= this->messages.size())
+                       throw LDAPException("Index out of range");
+               return this->messages[sz];
+       }
+
+       const std::string& getError() const
+       {
+               return this->error;
+       }
+};
+
+class LDAPInterface
+{
+ public:
+       ModuleRef creator;
+
+       LDAPInterface(Module* m) : creator(m) { }
+       virtual ~LDAPInterface() { }
+
+       virtual void OnResult(const LDAPResult& r) = 0;
+       virtual void OnError(const LDAPResult& err) = 0;
+};
+
+class LDAPProvider : public DataProvider
+{
+ public:
+       LDAPProvider(Module* Creator, const std::string& Name)
+               : DataProvider(Creator, Name) { }
+
+       /** Attempt to bind to the LDAP server as a manager
+        * @param i The LDAPInterface the result is sent to
+        */
+       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
+        */
+       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
+        */
+       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
+        */
+       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
+        */
+       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
+        */
+       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
+        */
+       virtual void Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) = 0;
+};
diff --git a/include/modules/names.h b/include/modules/names.h
new file mode 100644 (file)
index 0000000..d3e1969
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2019 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
+
+#include "event.h"
+
+namespace Names
+{
+       class EventListener;
+}
+
+class Names::EventListener : public Events::ModuleEventListener
+{
+ public:
+       EventListener(Module* mod)
+               : ModuleEventListener(mod, "event/names")
+       {
+       }
+
+       /* Called for every item in a NAMES list.
+        * @param issuer The user who initiated the NAMES request.
+        * @param memb The channel membership of the user who is being considered for inclusion.
+        * @param prefixes The prefix character(s) to show in front of the user's nickname.
+        * @param nick The nickname of the user to show.
+        * @return Return MOD_RES_PASSTHRU to allow the member to be displayed, MOD_RES_DENY to cause them to be
+        * excluded from this NAMES list
+        */
+       virtual ModResult OnNamesListItem(LocalUser* issuer, Membership* memb, std::string& prefixes, std::string& nick) = 0;
+};
diff --git a/include/modules/regex.h b/include/modules/regex.h
new file mode 100644 (file)
index 0000000..5ef00cd
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@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/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+class Regex : public classbase
+{
+protected:
+       /** The uncompiled regex string. */
+       std::string regex_string;
+
+       // Constructor may as well be protected, as this class is abstract.
+       Regex(const std::string& rx) : regex_string(rx) { }
+
+public:
+
+       virtual ~Regex() { }
+
+       virtual bool Matches(const std::string& text) = 0;
+
+       const std::string& GetRegexString() const
+       {
+               return regex_string;
+       }
+};
+
+class RegexFactory : public DataProvider
+{
+ public:
+       RegexFactory(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) { }
+
+       virtual Regex* Create(const std::string& expr) = 0;
+};
+
+class RegexException : public ModuleException
+{
+ public:
+        RegexException(const std::string& regex, const std::string& error)
+                : ModuleException("Error in regex '" + regex + "': " + error) { }
+
+        RegexException(const std::string& regex, const std::string& error, int offset)
+                : ModuleException("Error in regex '" + regex + "' at offset " + ConvToStr(offset) + ": " + error) { }
+};
diff --git a/include/modules/reload.h b/include/modules/reload.h
new file mode 100644 (file)
index 0000000..dcdbc95
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..8a54cfd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 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
+ * 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"
+
+class SASLEventListener : public Events::ModuleEventListener
+{
+ public:
+       SASLEventListener(Module* mod)
+               : ModuleEventListener(mod, "event/sasl")
+       {
+       }
+
+       virtual void OnSASLAuth(const CommandBase::Params& params) = 0;
+};
diff --git a/include/modules/server.h b/include/modules/server.h
new file mode 100644 (file)
index 0000000..f9907ef
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+#include "event.h"
+
+class ServerEventListener : public Events::ModuleEventListener
+{
+ public:
+       ServerEventListener(Module* mod)
+               : ModuleEventListener(mod, "event/server")
+       {
+       }
+
+       /** Fired when a channel message is being broadcast across the network.
+        * @param channel The channel which is having a message sent to it.
+        * @param server The server which might have a message broadcast to it.
+        * @return Either MOD_RES_ALLOW to always send the message to the server, MOD_RES_DENY to never
+        *         send the message to the server or MOD_RES_PASSTHRU if no module handled the event.
+        */
+       virtual ModResult OnBroadcastMessage(Channel* channel, const Server* server) { return MOD_RES_PASSTHRU; }
+
+       /** 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) { }
+
+       /** Allows modules to synchronize user metadata during a netburst. This will
+        * be called for every user visible on your side of the burst.
+        * @param user The user being synchronized.
+        * @param server The target of the burst.
+        */
+       virtual void OnSyncUser(User* user, ProtocolServer& server) { }
+
+       /** Allows modules to synchronize channel metadata during a netburst. This will
+        * be called for every channel visible on your side of the burst.
+        * @param chan The channel being synchronized.
+        * @param server The target of the burst.
+        */
+       virtual void OnSyncChannel(Channel* chan, ProtocolServer& server) { }
+
+       /** Allows modules to synchronize network metadata during a netburst.
+        * @param server The target of the burst.
+        */
+       virtual void OnSyncNetwork(ProtocolServer& server) { }
+};
diff --git a/include/modules/shun.h b/include/modules/shun.h
new file mode 100644 (file)
index 0000000..1c35e3a
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2017 Dylan Frank <b00mx0r@aureus.pw>
+ *
+ * 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 "xline.h"
+
+/** Shun class
+ */
+class Shun : public XLine
+{
+  public:
+       /** Create a Shun.
+        * @param s_time The set time
+        * @param d The duration of the xline
+        * @param src The sender of the xline
+        * @param re The reason of the xline
+        * @param shunmask Mask to match
+        */
+       Shun(time_t s_time, unsigned long d, const std::string& src, const std::string& re, const std::string& shunmask)
+               : XLine(s_time, d, src, re, "SHUN")
+               , matchtext(shunmask)
+       {
+       }
+
+       bool Matches(User* u) CXX11_OVERRIDE
+       {
+               LocalUser* lu = IS_LOCAL(u);
+               if (lu && lu->exempt)
+                       return false;
+
+               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;
+       }
+
+       bool Matches(const std::string& str) CXX11_OVERRIDE
+       {
+               return (matchtext == str);
+       }
+
+       const std::string& Displayable() CXX11_OVERRIDE
+       {
+               return matchtext;
+       }
+
+  private:
+       /** Matching mask **/
+       std::string matchtext;
+};
diff --git a/include/modules/sql.h b/include/modules/sql.h
new file mode 100644 (file)
index 0000000..15e8260
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2017 Peter Powell <petpow@saberuk.com>
+ *   Copyright (C) 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
+ * 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 SQL
+{
+       class Error;
+       class Field;
+       class Provider;
+       class Query;
+       class Result;
+
+       /** A list of parameter replacement values. */
+       typedef std::vector<std::string> ParamList;
+
+       /** A map of parameter replacement values. */
+       typedef std::map<std::string, std::string> ParamMap;
+
+       /** A list of SQL fields from a specific row. */
+       typedef std::vector<Field> Row;
+
+       /** An enumeration of possible error codes. */
+       enum ErrorCode
+       {
+               /** No error has occurred. */
+               SUCCESS,
+
+               /** The database identifier is invalid. */
+               BAD_DBID,
+
+               /** The database connection has failed. */
+               BAD_CONN,
+
+               /** Executing the query failed. */
+               QSEND_FAIL,
+
+               /** Reading the response failed. */
+               QREPLY_FAIL
+       };
+
+       /** Populates a parameter map with information about a user.
+        * @param user The user to collect information from.
+        * @param userinfo The map to populate.
+        */
+       void PopulateUserInfo(User* user, ParamMap& userinfo);
+}
+
+/** Represents a single SQL field. */
+class SQL::Field
+{
+ private:
+       /** Whether this SQL field is NULL. */
+       bool null;
+
+       /** The underlying SQL value. */
+       std::string value;
+
+ public:
+       /** Creates a new NULL SQL field. */
+       Field()
+               : null(true)
+       {
+       }
+
+       /** Creates a new non-NULL SQL field.
+        * @param v The value of the field.
+        */
+       Field(const std::string& v)
+               : null(false)
+               , value(v)
+       {
+       }
+
+       /** Determines whether this SQL entry is NULL. */
+       inline bool IsNull() const { return null; }
+
+       /** Retrieves the underlying value. */
+       inline operator const std::string&() const { return value; }
+};
+
+/** Represents the result of an SQL query. */
+class SQL::Result : public classbase
+{
+ public:
+       /**
+        * Return the number of rows in the result.
+        *
+        * Note that if you have perfomed an INSERT or UPDATE query or other
+        * query which will not return rows, this will return the number of
+        * affected rows. In this case you SHOULD NEVER access any of the result
+        * set rows, as there aren't any!
+        * @returns Number of rows in the result set.
+        */
+       virtual int Rows() = 0;
+
+       /** Retrieves the next available row from the database.
+        * @param result A list to store the fields from this row in.
+        * @return True if a row could be retrieved; otherwise, false.
+        */
+       virtual bool GetRow(Row& result) = 0;
+
+       /** Retrieves a list of SQL columns in the result.
+        * @param result A reference to the vector to store column names in.
+        */
+       virtual void GetCols(std::vector<std::string>& result) = 0;
+
+       /**
+        * Check if there's a column with the specified name in the result
+        *
+        * @param the column name
+        * @param on success, this is the column index
+        * @returns true, or false if the column is not found
+        */
+       virtual bool HasColumn(const std::string& column, size_t& index) = 0;
+};
+
+/** SQL::Error holds the error state of a request.
+ * The error string varies from database software to database software
+ * and should be used to display informational error messages to users.
+ */
+class SQL::Error
+{
+ private:
+       /** The custom error message if one has been specified. */
+       const std::string message;
+
+ public:
+       /** The code which represents this error. */
+       const ErrorCode code;
+
+       /** Initialize an SQL::Error from an error code.
+        * @param c A code which represents this error.
+        */
+       Error(ErrorCode c)
+               : code(c)
+       {
+       }
+
+       /** Initialize an SQL::Error from an error code and a custom error message.
+        * @param c A code which represents this error.
+        * @param m A custom error message.
+        */
+       Error(ErrorCode c, const std::string m)
+               : message(m)
+               , code(c)
+       {
+       }
+
+       /** Retrieves the error message. */
+       const char* ToString() const
+       {
+               if (!message.empty())
+                       return message.c_str();
+
+               switch (code)
+               {
+                       case BAD_DBID:
+                               return "Invalid database identifier";
+                       case BAD_CONN:
+                               return "Invalid connection";
+                       case QSEND_FAIL:
+                               return "Sending query failed";
+                       case QREPLY_FAIL:
+                               return "Getting query result failed";
+                       default:
+                               return "Unknown error";
+               }
+       }
+};
+
+/**
+ * Object representing an SQL query. This should be allocated on the heap and
+ * passed to an SQL::Provider, which will free it when the query is complete or
+ * when the querying module is unloaded.
+ *
+ * You should store whatever information is needed to have the callbacks work in
+ * this object (UID of user, channel name, etc).
+ */
+class SQL::Query : public classbase
+{
+ protected:
+       /** Creates a new SQL query. */
+       Query(Module* Creator)
+               : creator(Creator)
+       {
+       }
+
+ public:
+       const ModuleRef creator;
+
+       /* Destroys this Query instance. */
+       virtual ~Query()
+       {
+       }
+
+       /** Called when an SQL error happens.
+        * @param error The error that occurred.
+        */
+       virtual void OnError(Error& error) = 0;
+
+       /** Called when a SQL result is received.
+        * @param result The result of the SQL query.
+        */
+       virtual void OnResult(Result& result) = 0;
+};
+
+/**
+ * Provider object for SQL servers
+ */
+class SQL::Provider : public DataProvider
+{
+ public:
+       Provider(Module* Creator, const std::string& Name)
+               : DataProvider(Creator, Name)
+       {
+       }
+
+       /** Submit an asynchronous SQL query.
+        * @param callback The result reporting point
+        * @param query The hardcoded query string. If you have parameters to substitute, see below.
+        */
+       virtual void Submit(Query* callback, const std::string& query) = 0;
+
+       /** Submit an asynchronous SQL query.
+        * @param callback The result reporting point
+        * @param format The simple parameterized query string ('?' parameters)
+        * @param p Parameters to fill in for the '?' entries
+        */
+       virtual void Submit(Query* callback, const std::string& format, const SQL::ParamList& p) = 0;
+
+       /** Submit an asynchronous SQL query.
+        * @param callback The result reporting point
+        * @param format The parameterized query string ('$name' parameters)
+        * @param p Parameters to fill in for the '$name' entries
+        */
+       virtual void Submit(Query* callback, const std::string& format, const ParamMap& p) = 0;
+};
+
+inline void SQL::PopulateUserInfo(User* user, ParamMap& userinfo)
+{
+       userinfo["nick"] = user->nick;
+       userinfo["host"] = user->GetRealHost();
+       userinfo["ip"] = user->GetIPString();
+       userinfo["real"] = user->GetRealName();
+       userinfo["ident"] = user->ident;
+       userinfo["server"] = user->server->GetName();
+       userinfo["uuid"] = user->uuid;
+}
diff --git a/include/modules/ssl.h b/include/modules/ssl.h
new file mode 100644 (file)
index 0000000..701c0d1
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   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
+
+#include <string>
+#include "iohook.h"
+
+/** ssl_cert is a class which abstracts SSL certificate
+ * and key information.
+ *
+ * Because gnutls and openssl represent key information in
+ * wildly different ways, this class allows it to be accessed
+ * in a unified manner. These classes are attached to ssl-
+ * connected local users using SSLCertExt
+ */
+class ssl_cert : public refcountbase
+{
+ public:
+       std::string dn;
+       std::string issuer;
+       std::string error;
+       std::string fingerprint;
+       bool trusted, invalid, unknownsigner, revoked;
+
+       ssl_cert() : trusted(false), invalid(true), unknownsigner(true), revoked(false) {}
+
+       /** Get certificate distinguished name
+        * @return Certificate DN
+        */
+       const std::string& GetDN()
+       {
+               return dn;
+       }
+
+       /** Get Certificate issuer
+        * @return Certificate issuer
+        */
+       const std::string& GetIssuer()
+       {
+               return issuer;
+       }
+
+       /** Get error string if an error has occured
+        * @return The error associated with this users certificate,
+        * or an empty string if there is no error.
+        */
+       const std::string& GetError()
+       {
+               return error;
+       }
+
+       /** Get key fingerprint.
+        * @return The key fingerprint as a hex string.
+        */
+       const std::string& GetFingerprint()
+       {
+               return fingerprint;
+       }
+
+       /** Get trust status
+        * @return True if this is a trusted certificate
+        * (the certificate chain validates)
+        */
+       bool IsTrusted()
+       {
+               return trusted;
+       }
+
+       /** Get validity status
+        * @return True if the certificate itself is
+        * correctly formed.
+        */
+       bool IsInvalid()
+       {
+               return invalid;
+       }
+
+       /** Get signer status
+        * @return True if the certificate appears to be
+        * self-signed.
+        */
+       bool IsUnknownSigner()
+       {
+               return unknownsigner;
+       }
+
+       /** Get revokation status.
+        * @return True if the certificate is revoked.
+        * Note that this only works properly for GnuTLS
+        * right now.
+        */
+       bool IsRevoked()
+       {
+               return revoked;
+       }
+
+       /** Get certificate usability
+       * @return True if the certificate is not expired nor revoked
+       */
+       bool IsUsable()
+       {
+               return !invalid && !revoked && error.empty();
+       }
+
+       /** Get CA trust status
+       * @return True if the certificate is issued by a CA
+       * and valid.
+       */
+       bool IsCAVerified()
+       {
+               return IsUsable() && trusted && !unknownsigner;
+       }
+
+       std::string GetMetaLine()
+       {
+               std::stringstream value;
+               bool hasError = !error.empty();
+               value << (IsInvalid() ? "v" : "V") << (IsTrusted() ? "T" : "t") << (IsRevoked() ? "R" : "r")
+                       << (IsUnknownSigner() ? "s" : "S") << (hasError ? "E" : "e") << " ";
+               if (hasError)
+                       value << GetError();
+               else
+                       value << GetFingerprint() << " " << GetDN() << " " << GetIssuer();
+               return value.str();
+       }
+};
+
+class SSLIOHook : public IOHook
+{
+ protected:
+       /** Peer SSL certificate, set by the SSL module
+        */
+       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)
+       {
+       }
+
+       /**
+        * Get the certificate sent by this peer
+        * @return The SSL certificate sent by the peer, NULL if no cert was sent
+        */
+       ssl_cert* GetCertificate() const
+       {
+               return certificate;
+       }
+
+       /**
+        * Get the fingerprint of the peer's certificate
+        * @return The fingerprint of the SSL client certificate sent by the peer,
+        * empty if no cert was sent
+        */
+       std::string GetFingerprint() const
+       {
+               ssl_cert* cert = GetCertificate();
+               if (cert && cert->IsUsable())
+                       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;
+
+
+       /** Retrieves the name of the SSL connection which is sent via SNI.
+        * @param out String that the server name will be appended to.
+        * returns True if the server name was retrieved; otherwise, false.
+        */
+       virtual bool GetServerName(std::string& out) const = 0;
+};
+
+/** Helper functions for obtaining SSL client certificates and key fingerprints
+ * from StreamSockets
+ */
+class SSLClientCert
+{
+ public:
+       /**
+        * Get the client certificate from a socket
+        * @param sock The socket to get the certificate from, the socket does not have to use SSL
+        * @return The SSL client certificate information, NULL if the peer is not using SSL
+        */
+       static ssl_cert* GetCertificate(StreamSocket* sock)
+       {
+               SSLIOHook* ssliohook = SSLIOHook::IsSSL(sock);
+               if (!ssliohook)
+                       return NULL;
+
+               return ssliohook->GetCertificate();
+       }
+
+       /**
+        * Get the fingerprint of a client certificate from a socket
+        * @param sock The socket to get the certificate fingerprint from, the
+        * socket does not have to use SSL
+        * @return The key fingerprint from the SSL certificate sent by the peer,
+        * empty if no cert was sent or the peer is not using SSL
+        */
+       static std::string GetFingerprint(StreamSocket* sock)
+       {
+               ssl_cert* cert = SSLClientCert::GetCertificate(sock);
+               if (cert)
+                       return cert->GetFingerprint();
+               return "";
+       }
+};
+
+class UserCertificateAPIBase : public DataProvider
+{
+ public:
+       UserCertificateAPIBase(Module* parent)
+               : DataProvider(parent, "m_sslinfo_api")
+       {
+       }
+
+       /** Get the SSL certificate of a user
+        * @param user The user whose certificate to get, user may be remote
+        * @return The SSL certificate of the user or NULL if the user is not using SSL
+        */
+       virtual ssl_cert* GetCertificate(User* user) = 0;
+
+       /** Set the SSL certificate of a user.
+        * @param user The user whose certificate to set.
+        * @param cert The SSL certificate to set for the user.
+        */
+       virtual void SetCertificate(User* user, ssl_cert* cert) = 0;
+
+       /** Get the key fingerprint from a user's certificate
+        * @param user The user whose key fingerprint to get, user may be remote
+        * @return The key fingerprint from the user's SSL certificate or an empty string
+        * if the user is not using SSL or did not provide a client certificate
+        */
+       std::string GetFingerprint(User* user)
+       {
+               ssl_cert* cert = GetCertificate(user);
+               if (cert)
+                       return cert->GetFingerprint();
+               return "";
+       }
+};
+
+/** API implemented by m_sslinfo that allows modules to retrive the SSL certificate
+ * information of local and remote users. It can also be used to find out whether a
+ * user is using SSL or not.
+ */
+class UserCertificateAPI : public dynamic_reference<UserCertificateAPIBase>
+{
+ public:
+       UserCertificateAPI(Module* parent)
+               : dynamic_reference<UserCertificateAPIBase>(parent, "m_sslinfo_api")
+       {
+       }
+};
diff --git a/include/modules/stats.h b/include/modules/stats.h
new file mode 100644 (file)
index 0000000..e69070c
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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 "event.h"
+
+namespace Stats
+{
+       class Context;
+       class EventListener;
+       class Row;
+}
+
+class Stats::EventListener : public Events::ModuleEventListener
+{
+ public:
+       EventListener(Module* mod)
+               : ModuleEventListener(mod, "event/stats")
+       {
+       }
+
+       /** Called when the STATS command is executed.
+        * @param stats Context of the /STATS request, contains requesting user, list of answer rows etc.
+        * @return MOD_RES_DENY if the stats request has been fulfilled. Otherwise, MOD_RES_PASSTHRU.
+        */
+       virtual ModResult OnStats(Stats::Context& stats) = 0;
+};
+
+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/webirc.h b/include/modules/webirc.h
new file mode 100644 (file)
index 0000000..4ca3b07
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2016-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/>.
+ */
+
+
+#pragma once
+
+#include "event.h"
+
+namespace WebIRC
+{
+       class EventListener;
+
+       typedef insp::flat_map<std::string, std::string, irc::insensitive_swo> FlagMap;
+}
+
+class WebIRC::EventListener
+       : public Events::ModuleEventListener
+{
+ protected:
+       EventListener(Module* mod)
+               : ModuleEventListener(mod, "event/webirc")
+       {
+       }
+
+ public:
+       virtual void OnWebIRCAuth(LocalUser* user, const FlagMap* flags) = 0;
+};
diff --git a/include/modules/who.h b/include/modules/who.h
new file mode 100644 (file)
index 0000000..983cece
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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 Who
+{
+       class EventListener;
+       class Request;
+}
+
+class Who::EventListener : public Events::ModuleEventListener
+{
+ public:
+       EventListener(Module* mod)
+               : ModuleEventListener(mod, "event/who")
+       {
+       }
+
+       /** Called when a result from WHO is about to be queued.
+        * @param request Details about the WHO request which caused this response.
+        * @param source The user who initiated this WHO request.
+        * @param user The user that this line of the WHO request is about.
+        * @param memb The channel membership of the user or NULL if not targeted at a channel.
+        * @param numeric The numeric which will be sent in response to the request.
+        * @return MOD_RES_ALLOW to explicitly allow the response, MOD_RES_DENY to explicitly deny the
+        *         response, or MOD_RES_PASSTHRU to let another module handle the event.
+        */
+       virtual ModResult OnWhoLine(const Request& request, LocalUser* source, User* user, Membership* memb, Numeric::Numeric& numeric) = 0;
+};
+
+class Who::Request
+{
+ public:
+       /** The flags for matching users to include. */
+       std::bitset<UCHAR_MAX> flags;
+
+       /** Whether we are matching using a wildcard or a flag. */
+       bool fuzzy_match;
+
+       /** The text to match against. */
+       std::string matchtext;
+
+       /** The WHO/WHOX responses we will send to the source. */
+       std::vector<Numeric::Numeric> results;
+
+       /** Whether the source requested a WHOX response. */
+       bool whox;
+
+       /** The fields to include in the WHOX response. */
+       std::bitset<UCHAR_MAX> whox_fields;
+
+       /** A user specified label for the WHOX response. */
+       std::string whox_querytype;
+
+       /** Get the index in the response parameters for the different data fields
+        *
+        * The fields 'r' (realname) and 'd' (hops) will always be missing in a non-WHOX
+        * query, because WHOX splits them to 2 fields, where old WHO has them as one.
+        *
+        * @param flag The field name to look for
+        * @param out The index will be stored in this value
+        * @return True if the field is available, false otherwise
+        */
+       virtual bool GetFieldIndex(char flag, size_t& out) const = 0;
+
+ protected:
+       Request()
+               : fuzzy_match(false)
+               , whox(false)
+       {
+       }
+};
diff --git a/include/modules/whois.h b/include/modules/whois.h
new file mode 100644 (file)
index 0000000..4f09d26
--- /dev/null
@@ -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 (file)
index 0000000..cc9f9cc
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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
+        */
+       CommandBase::Params 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 CommandBase::Params& GetParams() const { return params; }
+
+       /** Get the parameters of the numeric
+        * @return Parameters of the numeric as a vector of strings
+        */
+       CommandBase::Params& GetParams() { return params; }
+};
diff --git a/include/numericbuilder.h b/include/numericbuilder.h
new file mode 100644 (file)
index 0000000..0d55093
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * 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())
+       {
+       }
+};
+
+namespace Numerics
+{
+       class InvalidModeParameter;
+       class NoSuchChannel;
+       class NoSuchNick;
+}
+
+/* Builder for the ERR_INVALIDMODEPARAM numeric. */
+class Numerics::InvalidModeParameter : public Numeric::Numeric
+{
+ public:
+       InvalidModeParameter(Channel* chan, ModeHandler* mode, const std::string& parameter, const std::string& message = "")
+               : Numeric(ERR_INVALIDMODEPARAM)
+       {
+               push(chan->name);
+               push(mode->GetModeChar());
+               push(parameter);
+               push(message.empty() ? InspIRCd::Format("Invalid %s mode parameter", mode->name.c_str()) : message);
+       }
+
+       InvalidModeParameter(User* user, ModeHandler* mode, const std::string& parameter, const std::string& message = "")
+               : Numeric(ERR_INVALIDMODEPARAM)
+       {
+               push(user->registered & REG_NICK ? user->nick : "*");
+               push(mode->GetModeChar());
+               push(parameter);
+               push(message.empty() ? InspIRCd::Format("Invalid %s mode parameter", mode->name.c_str()) : message);
+       }
+};
+
+/** Builder for the ERR_NOSUCHCHANNEL numeric. */
+class Numerics::NoSuchChannel : public Numeric::Numeric
+{
+ public:
+       NoSuchChannel(const std::string& chan)
+               : Numeric(ERR_NOSUCHCHANNEL)
+       {
+               push(chan);
+               push("No such channel");
+       }
+};
+
+/** Builder for the ERR_NOSUCHNICK numeric. */
+class Numerics::NoSuchNick : public Numeric::Numeric
+{
+ public:
+       NoSuchNick(const std::string& nick)
+               : Numeric(ERR_NOSUCHNICK)
+       {
+               push(nick);
+               push("No such nick");
+       }
+};
index 4fce4cb6de53b746c9ef6a8e6798fdca00aa8a1b..8bde83e1a23d08d037b21b1f0d91992c43b744f9 100644 (file)
  */
 
 
-#ifndef NUMERICS_H
-#define NUMERICS_H
+#pragma once
 
 /*
- * This file is aimed providing a string that is easier to use than using the numeric
- * directly.
- *
  * Module authors, please note!
  *  While you are free to use any numerics on this list, like the rest of the core, you
  *  *should not* be editing it!
- *  You should just WriteNumeric(444, .. or whatever as you would before this file, OR:
- *  #define RPL_MYNUMERIC 444 & WriteNumeric(RPL_MYNUMERIC, ...
  *
  *  If you *do* have a suggestion for a numeric you genuinely believe would be useful,
  *  please speak to us. :)
  * 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.
-        */
-       RPL_WELCOME                                     =       1, // 2812, not 1459
-       RPL_YOURHOSTIS                                  =       2, // 2812, not 1459
-       RPL_SERVERCREATED                               =       3, // 2812, not 1459
-       RPL_SERVERVERSION                               =       4, // 2812, not 1459
-       RPL_ISUPPORT                                    =       5, // not RFC, extremely common though (defined as RPL_BOUNCE in 2812, widely ignored)
-
-       RPL_MAP                                                         =       6, // unrealircd
-       RPL_ENDMAP                                                      =       7, // unrealircd
-       RPL_SNOMASKIS                                   =       8, // unrealircd
-
-       RPL_YOURUUID                                    =       42, // taken from ircnet
-
-       RPL_UMODEIS                                     =       221,
-       RPL_RULES                                       =       232, // unrealircd
-       RPL_ADMINME                                     =       256,
-       RPL_ADMINLOC1                           =       257,
-       RPL_ADMINLOC2                           =       258,
-       RPL_ADMINEMAIL                          =       259,
-       RPL_MAPUSERS                            =       270, // insp-specific
-
-       RPL_SYNTAX                                      =       304,    // insp-specific
-
-       RPL_UNAWAY                                              =       305,
-       RPL_NOWAWAY                                             =       306,
-
-       RPL_RULESTART                                   =       308, // unrealircd
-       RPL_RULESEND                                    =       309, // unrealircd
-       RPL_CHANNELMODEIS                               =       324,
-       RPL_CHANNELCREATED                              =       329, // ???
-       RPL_NOTOPICSET                                  =       331,
-       RPL_TOPIC                                       =       332,
-       RPL_TOPICTIME                                   =       333, // not RFC, extremely common though
-
-       RPL_INVITING                                    =       341,
-       RPL_INVITELIST                                  =       346, // insp-specific (stolen from ircu)
-       RPL_ENDOFINVITELIST                             =       347, // insp-specific (stolen from ircu)
-       RPL_VERSION                                             =       351,
-       RPL_NAMREPLY                                    =       353,
-       RPL_ENDOFNAMES                                  =       366,
-
-       RPL_INFO                                        =       371,
-       RPL_ENDOFINFO                           =       374,
-       RPL_MOTD                                        =       372,
-       RPL_MOTDSTART                                   =       375,
-       RPL_ENDOFMOTD                                   =       376,
-
-       RPL_YOUAREOPER                                          =       381,
-       RPL_REHASHING                                           =       382,
-       RPL_TIME                                                        =       391,
-       RPL_YOURDISPLAYEDHOST                           =       396, // from charybdis/etc, common convention
+       RPL_ISUPPORT                    = 5, // not RFC, extremely common though (defined as RPL_BOUNCE in 2812, widely ignored)
+
+       RPL_SNOMASKIS                   = 8, // unrealircd
+
+       RPL_MAP                         = 15, // ircu
+       RPL_ENDMAP                      = 17, // ircu
+       RPL_MAPUSERS                    = 18, // insp-specific
+
+       RPL_UMODEIS                     = 221,
+
+       RPL_LUSERCLIENT                 = 251,
+       RPL_LUSEROP                     = 252,
+       RPL_LUSERUNKNOWN                = 253,
+       RPL_LUSERCHANNELS               = 254,
+       RPL_LUSERME                     = 255,
+
+       RPL_ADMINME                     = 256,
+       RPL_ADMINLOC1                   = 257,
+       RPL_ADMINLOC2                   = 258,
+       RPL_ADMINEMAIL                  = 259,
+
+       RPL_LOCALUSERS                  = 265,
+       RPL_GLOBALUSERS                 = 266,
+
+       RPL_AWAY                        = 301,
+       RPL_USERHOST                    = 302,
+       RPL_ISON                        = 303,
+
+       RPL_WHOISSERVER                 = 312,
+       RPL_ENDOFWHOIS                  = 318,
+
+       RPL_LISTSTART                   = 321,
+       RPL_LIST                        = 322,
+       RPL_LISTEND                     = 323,
+
+       RPL_CHANNELMODEIS               = 324,
+       RPL_CHANNELCREATED              = 329, // ???
+       RPL_NOTOPICSET                  = 331,
+       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_NAMREPLY                    = 353,
+       RPL_LINKS                       = 364,
+       RPL_ENDOFLINKS                  = 365,
+       RPL_ENDOFNAMES                  = 366,
+
+       RPL_INFO                        = 371,
+       RPL_ENDOFINFO                   = 374,
+       RPL_MOTD                        = 372,
+       RPL_MOTDSTART                   = 375,
+       RPL_ENDOFMOTD                   = 376,
+
+       RPL_YOUAREOPER                  = 381,
+       RPL_REHASHING                   = 382,
+       RPL_TIME                        = 391,
+       RPL_YOURDISPLAYEDHOST           = 396, // from charybdis/etc, common convention
 
        /*
         * Error range of numerics.
         */
-       ERR_NOSUCHNICK                                  =       401,
-       ERR_NOSUCHSERVER                                =       402,
-       ERR_NOSUCHCHANNEL                               =       403, // used to indicate an invalid channel name also, so don't rely on RFC text (don't do that anyway!)
-       ERR_CANNOTSENDTOCHAN                    =       404,
-       ERR_TOOMANYCHANNELS                             =       405,
-       ERR_INVALIDCAPSUBCOMMAND                =       410, // ratbox/charybdis(?)
-       ERR_UNKNOWNCOMMAND                              =       421,
-       ERR_NOMOTD                                      =       422,
-       ERR_NORULES                                     =       434, // unrealircd
-       ERR_USERNOTINCHANNEL                            =       441,
-       ERR_NOTONCHANNEL                                        =       442,
-       ERR_USERONCHANNEL                                       =       443,
-       ERR_CANTCHANGENICK                                      =       447, // unrealircd, probably
-       ERR_NOTREGISTERED                               =       451,
-       ERR_NEEDMOREPARAMS                              =       461,
-       ERR_ALREADYREGISTERED                   =       462,
+       ERR_NOSUCHNICK                  = 401,
+       ERR_NOSUCHSERVER                = 402,
+       ERR_NOSUCHCHANNEL               = 403, // used to indicate an invalid channel name also, so don't rely on RFC text (don't do that anyway!)
+       ERR_CANNOTSENDTOCHAN            = 404,
+       ERR_TOOMANYCHANNELS             = 405,
+       ERR_WASNOSUCHNICK               = 406,
+       ERR_NOTEXTTOSEND                = 412,
+       ERR_UNKNOWNCOMMAND              = 421,
+       ERR_NOMOTD                      = 422,
+       ERR_NONICKNAMEGIVEN             = 431,
+       ERR_ERRONEUSNICKNAME            = 432,
+       ERR_NICKNAMEINUSE               = 433,
+       ERR_USERNOTINCHANNEL            = 441,
+       ERR_NOTONCHANNEL                = 442,
+       ERR_USERONCHANNEL               = 443,
+       ERR_CANTCHANGENICK              = 447, // unrealircd, probably
+       ERR_NOTREGISTERED               = 451,
+       ERR_NEEDMOREPARAMS              = 461,
+       ERR_ALREADYREGISTERED           = 462,
+       ERR_YOUREBANNEDCREEP            = 465,
+       ERR_UNKNOWNMODE                 = 472,
 
        /*
         * A quick side-rant about the next group of numerics..
-        * There are clients out there that like to assume that just because they don't recieve a numeric
+        * There are clients out there that like to assume that just because they don't receive a numeric
         * they know, that they have joined the channel.
         *
         * If IRC was at all properly standardised, this may even be a semi-acceptable assumption to make,
@@ -126,36 +133,39 @@ enum Numerics
         * instead!
         *
         * tl;dr version:
-        *   DON'T MAKE YOUR CLIENT ASSUME YOU JOINED UNLESS YOU RECIEVE A JOIN WITH YOUR DAMN NICK ON IT.
+        *   DON'T MAKE YOUR CLIENT ASSUME YOU JOINED UNLESS YOU RECEIVE A JOIN WITH YOUR DAMN NICK ON IT.
         * Thanks.
         *
         *  -- A message from the IRC group for coder sanity, and w00t
         */
-       ERR_BADCHANNELKEY                               =       475,
-       ERR_INVITEONLYCHAN                              =       473,
-       ERR_CHANNELISFULL                               =       471,
-       ERR_BANNEDFROMCHAN                              =       474,
-
-       ERR_NOPRIVILEGES                                =       481, // rfc, beware though, we use this for other things opers may not do also
-       ERR_CHANOPRIVSNEEDED                            =       482, // rfc, beware though, we use this for other things like trying to kick a uline
-
-       ERR_ALLMUSTSSL                                  =       490, // unrealircd
-       ERR_NOCTCPALLOWED                               =       492, // XXX: bzzzz. 1459 defines this as ERR_NOSERVICEHOST, research it more and perhaps change this! (ERR_CANNOTSENDTOCHAN?)
-                                                                                       // wtf, we also use this for m_noinvite. UGLY!
-       ERR_DELAYREJOIN                                 =       495, // insp-specific, XXX: we should use 'resource temporarily unavailable' from ircnet/ratbox or whatever
-       ERR_UNKNOWNSNOMASK                              =       501, // insp-specific
-       ERR_USERSDONTMATCH                              =       502,
-       ERR_CANTJOINOPERSONLY                   =       520, // unrealircd, but crap to have so many numerics for cant join..
-       ERR_CANTSENDTOUSER                              =       531, // ???
-
-       RPL_COMMANDS                                            =       702, // insp-specific
-       RPL_COMMANDSEND                                         =       703, // 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
-       ERR_CANTLOADMODULE                              =       974, // insp-specific
-       RPL_LOADEDMODULE                                =       975 // insp-specific
-};
+       ERR_BADCHANNELKEY               = 475,
+       ERR_BADCHANMASK                 = 476,
+       ERR_INVITEONLYCHAN              = 473,
+       ERR_CHANNELISFULL               = 471,
+       ERR_BANNEDFROMCHAN              = 474,
+
+       ERR_BANLISTFULL                 = 478,
+
+       ERR_NOPRIVILEGES                = 481, // rfc, beware though, we use this for other things opers may not do also
+       ERR_CHANOPRIVSNEEDED            = 482, // rfc, beware though, we use this for other things like trying to kick a uline
 
-#endif
+       ERR_RESTRICTED                  = 484,
+
+       ERR_NOOPERHOST                  = 491,
+       ERR_UNKNOWNSNOMASK              = 501, // insp-specific
+       ERR_USERSDONTMATCH              = 502,
+       ERR_CANTSENDTOUSER              = 531, // ???
+
+       RPL_SYNTAX                      = 650, // insp-specific
+       ERR_INVALIDMODEPARAM            = 696, // insp-specific
+       ERR_LISTMODEALREADYSET          = 697, // insp-specific
+       ERR_LISTMODENOTSET              = 698, // insp-specific
+
+       RPL_OTHERUMODEIS                = 803, // insp-specific
+       RPL_OTHERSNOMASKIS              = 804, // insp-specific
+
+       ERR_CANTUNLOADMODULE            = 972, // insp-specific
+       RPL_UNLOADEDMODULE              = 973, // insp-specific
+       ERR_CANTLOADMODULE              = 974, // insp-specific
+       RPL_LOADEDMODULE                = 975 // insp-specific
+};
diff --git a/include/parammode.h b/include/parammode.h
new file mode 100644 (file)
index 0000000..3139811
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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
+
+class CoreExport ParamModeBase : public ModeHandler
+{
+ private:
+       virtual void OnUnsetInternal(User* source, Channel* chan) = 0;
+
+ public:
+       ParamModeBase(Module* Creator, const std::string& Name, char modeletter, ParamSpec ps)
+               : ModeHandler(Creator, Name, modeletter, ps, MODETYPE_CHANNEL, MC_PARAM) { }
+
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& param, bool adding) CXX11_OVERRIDE;
+
+       // Does nothing by default
+       virtual bool IsParameterSecret() { return false; }
+       virtual void OnUnset(User* source, Channel* chan) { }
+       virtual ModeAction OnSet(User* source, Channel* chan, std::string& param) = 0;
+       virtual void GetParameter(Channel* chan, std::string& out) = 0;
+};
+
+/** Defines a parameter mode
+ * T = Child class
+ * ExtItemT = Type of the extension item used to store the parameter
+ *
+ * When unsetting the mode, the extension is automatically unset.
+ */
+template <typename T, typename ExtItemT>
+class ParamMode : public ParamModeBase
+{
+ public:
+       ExtItemT ext;
+
+       /**
+        * @param Creator Module handling this mode
+        * @param Name The internal name of this mode
+        * @param modeletter The mode letter of this mode
+        * @param ps The parameter type of this mode, one of ParamSpec
+        */
+       ParamMode(Module* Creator, const std::string& Name, char modeletter, ParamSpec ps = PARAM_SETONLY)
+               : ParamModeBase(Creator, Name, modeletter, ps)
+               , ext("parammode_" + Name, ExtensionItem::EXT_CHANNEL, Creator)
+       {
+       }
+
+       void OnUnsetInternal(User* source, Channel* chan) CXX11_OVERRIDE
+       {
+               this->OnUnset(source, chan);
+               ext.unset(chan);
+       }
+
+       void GetParameter(Channel* chan, std::string& out) CXX11_OVERRIDE
+       {
+               T* mh = static_cast<T*>(this);
+               mh->SerializeParam(chan, ext.get(chan), out);
+       }
+};
index aabb5b022c7d444be244b08ffe40313f1a866c1e..f98f6b37d4d40a90e344471f9ae0854eacd0eec0 100644 (file)
  */
 
 
-#ifndef PROTOCOL_H
-#define PROTOCOL_H
+#pragma once
 
 #include "hashcomp.h"
 
 class User;
 
-typedef std::vector<std::string> parameterlist;
-
-class ProtoServer
+class ProtocolServer
 {
  public:
-       std::string servername;
-       std::string parentname;
-       std::string gecos;
-       unsigned int usercount;
-       unsigned int opercount;
-       unsigned int latencyms;
+       /** Send metadata related to this server to the target server
+        * @param key The 'key' of the data
+        * @param data The string representation of the data
+        */
+       virtual void SendMetaData(const std::string& key, const std::string& data) = 0;
 };
 
-typedef std::list<ProtoServer> ProtoServerList;
-
-class ProtocolInterface
+class CoreExport ProtocolInterface
 {
  public:
-       ProtocolInterface() { }
+       typedef ProtocolServer Server;
+
+       class ServerInfo
+       {
+        public:
+               std::string servername;
+               std::string parentname;
+               std::string description;
+               unsigned int usercount;
+               unsigned int opercount;
+               unsigned int latencyms;
+       };
+
+       typedef std::vector<ServerInfo> ServerList;
+
        virtual ~ProtocolInterface() { }
 
-       /** Send an ENCAP message to one or more linked servers.
+       /** Send an ENCAP message to all servers matching a wildcard string.
         * See the protocol documentation for the purpose of ENCAP.
-        * @param encap This is a list of string parameters, the first of which must be a server ID or glob matching servernames.
-        * The second must be a subcommand. All subsequent parameters are dependant on the subcommand.
+        * @param targetmask The target server mask (can contain wildcards)
+        * @param cmd The ENCAP subcommand
+        * @param params List of string parameters which are dependant on the subcommand
+        * @param source The source of the message (prefix), must be a local user or NULL which means use local server
+        * @return Always true if the target mask contains wildcards; otherwise true if the server name was found,
+        * and the message was sent, false if it was not found.
         * ENCAP (should) be used instead of creating new protocol messages for easier third party application support.
-        * @return True if the message was sent out (target exists)
         */
-       virtual bool SendEncapsulatedData(const parameterlist &encap) { return false; }
+       virtual bool SendEncapsulatedData(const std::string& targetmask, const std::string& cmd, const CommandBase::Params& params, User* source = NULL) { return false; }
 
-       /** Send metadata for an object to other linked servers.
-        * @param target The object to send metadata for.
+       /** Send an ENCAP message to all servers.
+        * See the protocol documentation for the purpose of ENCAP.
+        * @param cmd The ENCAP subcommand
+        * @param params List of string parameters which are dependant on the subcommand
+        * @param source The source of the message (prefix), must be a local user or a user behind 'omit'
+        * or NULL which is equivalent to the local server
+        * @param omit If non-NULL the message won't be sent in the direction of this server, useful for forwarding messages
+        */
+       virtual void BroadcastEncap(const std::string& cmd, const CommandBase::Params& params, User* source = NULL, User* omit = NULL) { }
+
+       /** Send metadata for a channel to other linked servers.
+        * @param chan The channel to send metadata for
         * @param key The 'key' of the data, e.g. "swhois" for swhois desc on a user
         * @param data The string representation of the data
         */
-       virtual void SendMetaData(Extensible* target, const std::string &key, const std::string &data) { }
+       virtual void SendMetaData(Channel* chan, 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.
+       /** Send metadata for a user to other linked servers.
+        * @param user The user to send metadata for
+        * @param key The 'key' of the data, e.g. "swhois" for swhois desc on a user
+        * @param data The string representation of the data
         */
-       virtual void SendTopic(Channel* channel, std::string &topic) { }
+       virtual void SendMetaData(User* user, const std::string& key, const std::string& data) { }
 
-       /** Send mode changes for an object.
-        * @param target The channel name or user to send mode changes for.
-        * @param modedata The mode changes to send.
-        * @param translate A list of translation types
+       /** Send metadata related to the server to other linked servers.
+        * @param key The 'key' of the data
+        * @param data The string representation of the data
         */
-       virtual void SendMode(const std::string &target, const parameterlist &modedata, const std::vector<TranslateType> &translate) { }
-
-       /** Convenience function, string wrapper around the above.
-         */
-       virtual void SendModeStr(const std::string &target, const std::string &modeline)
-       {
-               irc::spacesepstream x(modeline);
-               parameterlist n;
-               std::vector<TranslateType> types;
-               std::string v;
-               while (x.GetToken(v))
-               {
-                       n.push_back(v);
-                       types.push_back(TR_TEXT);
-               }
-               SendMode(target, n, types);
-       }
+       virtual void SendMetaData(const std::string& key, const std::string& data) { }
 
        /** 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(const std::string &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) { }
+       virtual void SendSNONotice(char snomask, const std::string& text) { }
 
        /** Send a message to a channel.
         * @param target The channel to message.
-        * @param status The status character (e.g. %) required to recieve.
+        * @param status The status character (e.g. %) required to receive.
         * @param text The message to send.
+        * @param type The message type (MSG_PRIVMSG or MSG_NOTICE)
         */
-       virtual void SendChannelPrivmsg(Channel* target, char status, const std::string &text) { }
+       virtual void SendMessage(Channel* target, char status, const std::string& text, MessageType type = MSG_PRIVMSG) { }
 
-       /** Send a notice to a channel.
-        * @param target The channel to message.
-        * @param status The status character (e.g. %) required to recieve.
+       /** Send a message to a user.
+        * @param target The user to message.
         * @param text The message to send.
+        * @param type The message type (MSG_PRIVMSG or MSG_NOTICE)
         */
-       virtual void SendChannelNotice(Channel* target, char status, const std::string &text) { }
+       virtual void SendMessage(User* target, const std::string& text, MessageType type = MSG_PRIVMSG) { }
 
-       /** Send a message to a user.
-        * @param target The user to message.
+       /** Send a notice to a channel.
+        * @param target The channel to message.
+        * @param status The status character (e.g. %) required to receive.
         * @param text The message to send.
         */
-       virtual void SendUserPrivmsg(User* target, const std::string &text) { }
+       void SendChannelNotice(Channel* target, char status, const std::string &text)
+       {
+               SendMessage(target, status, text, MSG_NOTICE);
+       }
 
        /** Send a notice to a user.
         * @param target The user to message.
         * @param text The message to send.
         */
-       virtual void SendUserNotice(User* target, const std::string &text) { }
+       void SendUserNotice(User* target, const std::string &text)
+       {
+               SendMessage(target, text, MSG_NOTICE);
+       }
 
        /** Fill a list of servers and information about them.
         * @param sl The list of servers to fill.
         * XXX: document me properly, this is shit.
         */
-       virtual void GetServerList(ProtoServerList &sl) { }
+       virtual void GetServerList(ServerList& sl) { }
 };
-
-#endif
-
diff --git a/include/server.h b/include/server.h
new file mode 100644 (file)
index 0000000..d73c967
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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
+
+class CoreExport Server : public classbase
+{
+ protected:
+       /** The name of this server
+        */
+       const std::string name;
+
+       /** The description of this server.
+        * This can be updated by the protocol module (for remote servers) or by a rehash (for the local server).
+        */
+       std::string description;
+
+       /** True if this server is ulined
+        */
+       bool uline;
+
+       /** True if this server is a silent uline, i.e. silent="true" in the uline block
+        */
+       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) { }
+
+       /**
+        * Returns the name of this server
+        * @return The name of this server, for example "irc.inspircd.org".
+        */
+       const std::string& GetName() const { return name; }
+
+       /** Returns the description of this server
+        * @return The description of this server
+        */
+       const std::string& GetDesc() const { return description; }
+
+       /**
+        * Checks whether this server is ulined
+        * @return True if this server is ulined, false otherwise.
+        */
+       bool IsULine() const { return uline; }
+
+       /**
+        * Checks whether this server is a silent uline
+        * Silent uline servers introduce, quit and oper up users without a snotice being generated.
+        * @return True if this server is a silent uline, false otherwise.
+        */
+       bool IsSilentULine() const { return silentuline; }
+};
index 85ad26f71392a7e068f35a43b7e3564cc17770cd..817371613c1833a4af4cff65affa0330123b8352 100644 (file)
  */
 
 
-#ifndef SNOMASKS_H
-#define SNOMASKS_H
+#pragma once
 
+class SnomaskManager;
 class Snomask
 {
- public:
+       /** Description of this snomask, e.g.: OPER, ANNOUNCEMENT, XLINE
+        */
        std::string Description;
+
+       /** Information about the last sent message,
+        * used for sending "last message repeated X times" messages
+        */
        std::string LastMessage;
-       int Count;
-       bool LastBlocked;
        char LastLetter;
+       unsigned int Count;
 
+       /** Log and send a message to all opers who have the given snomask set
+        * @param letter The target users of this message
+        * @param desc The description of this snomask, will be prepended to the message
+        * @param msg The message to send
+        */
+       static void Send(char letter, const std::string& desc, const std::string& msg);
+
+ public:
        /** Create a new Snomask
         */
-       Snomask() : Count(0), LastBlocked(false), LastLetter(0)
-       {
-       }
+       Snomask();
 
        /** Sends a message to all opers with this snomask.
+        * @param message The message to send
+        * @param letter The snomask character to send the message to.
         */
-       void SendMessage(const std::string &message, char letter);
+       void SendMessage(const std::stringmessage, char letter);
 
        /** Sends out the (last message repeated N times) message
         */
        void Flush();
+
+       /** Returns the description of this snomask
+        * @param letter The letter of this snomask. If uppercase, the description of the remote
+        * variant of this snomask will be returned (i.e.: "REMOTE" will be prepended to the description).
+        * @return The description of this snomask
+        */
+       std::string GetDescription(char letter) const;
+
+       friend class SnomaskManager;
 };
 
 /** Snomask manager handles routing of SNOMASK (usermode +s) messages to opers.
  * 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>
 {
- public:
        Snomask masks[26];
 
+ public:
        /** Create a new SnomaskManager
         */
        SnomaskManager();
@@ -95,7 +116,6 @@ class CoreExport SnomaskManager
         */
        void WriteGlobalSno(char letter, const char* text, ...) CUSTOM_PRINTF(3, 4);
 
-
        /** Called once per 5 seconds from the mainloop, this flushes any cached
         * snotices. The way the caching works is as follows:
         * Calls to WriteToSnoMask write to a cache, if the call is the same as it was
@@ -105,6 +125,12 @@ class CoreExport SnomaskManager
         * is not particularly significant, in order to keep notices going out.
         */
        void FlushSnotices();
-};
 
-#endif
+       /** Check whether a given character is an enabled (initialized) snomask.
+        * Valid snomask chars are lower- or uppercase letters and have a description.
+        * Snomasks are initialized with EnableSnomask().
+        * @param ch The character to check
+        * @return True if the given char is allowed to be set via +s.
+        */
+       bool IsSnomaskUsable(char ch) const;
+};
index 5f67051246d3dbe2017f5cab375632c58b69e016..e527bc7f54aa89c86b53253e6a38f4056f80882a 100644 (file)
@@ -22,8 +22,7 @@
  */
 
 
-#ifndef INSPIRCD_SOCKET_H
-#define INSPIRCD_SOCKET_H
+#pragma once
 
 #ifndef _WIN32
 
@@ -33,6 +32,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/un.h>
 #include <netinet/in.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -60,8 +60,11 @@ namespace irc
                        struct sockaddr sa;
                        struct sockaddr_in in4;
                        struct sockaddr_in6 in6;
+                       struct sockaddr_un un;
+                       /** Return the family of the socket (e.g. AF_INET). */
+                       int family() const;
                        /** Return the size of the structure for syscall passing */
-                       int sa_size() const;
+                       socklen_t sa_size() const;
                        /** Return port number or -1 if invalid */
                        int port() const;
                        /** Return IP only */
@@ -85,7 +88,7 @@ namespace irc
                        /** Construct a CIDR mask from the string. Will normalize (127.0.0.1/8 => 127.0.0.0/8). */
                        cidr_mask(const std::string& mask);
                        /** Construct a CIDR mask of a given length from the given address */
-                       cidr_mask(const irc::sockets::sockaddrs& addr, int len);
+                       cidr_mask(const irc::sockets::sockaddrs& addr, unsigned char len);
                        /** Equality of bits, type, and length */
                        bool operator==(const cidr_mask& other) const;
                        /** Ordering defined for maps */
@@ -110,9 +113,6 @@ namespace irc
                 */
                CoreExport bool MatchCIDR(const std::string &address, const std::string &cidr_mask, bool match_with_username);
 
-               /** Return the size of the structure for syscall passing */
-               inline int sa_size(const irc::sockets::sockaddrs& sa) { return sa.sa_size(); }
-
                /** Convert an address-port pair into a binary sockaddr
                 * @param addr The IP address, IPv4 or IPv6
                 * @param port The port, 0 for unspecified
@@ -121,23 +121,18 @@ namespace irc
                 */
                CoreExport bool aptosa(const std::string& addr, int port, irc::sockets::sockaddrs& sa);
 
-               /** Convert a binary sockaddr to an address-port pair
-                * @param sa The structure to convert
-                * @param addr the IP address
-                * @param port the port
-                * @return true if the conversion was successful, false if unknown address family
-                */
-               CoreExport bool satoap(const irc::sockets::sockaddrs& sa, std::string& addr, int &port);
-
-               /** Convert a binary sockaddr to a user-readable string.
-                * This means IPv6 addresses are written as [::1]:6667, and *:6668 is used for 0.0.0.0:6668
-                * @param sa The structure to convert
-                * @return The string; "<unknown>" if not a valid address
+               /** Convert a UNIX socket path to a binary sockaddr.
+                * @param path The path to the UNIX socket.
+                * @param sa The structure to place the result in. Will be zeroed prior to conversion.
+                * @return True if the conversion was successful; otherwise, false.
                 */
-               inline std::string satouser(const irc::sockets::sockaddrs& sa) { return sa.str(); }
+               CoreExport bool untosa(const std::string& path, irc::sockets::sockaddrs& sa);
        }
 }
 
+/** A list of failed port bindings, used for informational purposes on startup */
+typedef std::vector<std::pair<irc::sockets::sockaddrs, int> > FailedPortList;
+
 #include "socketengine.h"
 /** This class handles incoming connections on client ports.
  * It will create a new User for every valid connection
@@ -147,24 +142,37 @@ class CoreExport ListenSocket : public EventHandler
 {
  public:
        reference<ConfigTag> bind_tag;
-       std::string bind_addr;
-       int bind_port;
-       /** Human-readable bind description */
-       std::string bind_desc;
+       const irc::sockets::sockaddrs bind_sa;
+
+       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.
+        */
+       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();
-};
-
-#endif
+       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.
+        */
+       void ResetIOHookProvider();
+};
index 37b7d637350b833e46d38f13d932f253008f36d6..01afb8f91cf1a44bb9cf1119e3b8f9fcbd5bc995 100644 (file)
  */
 
 
-#ifndef SOCKETENGINE_H
-#define SOCKETENGINE_H
+#pragma once
 
 #include <vector>
 #include <string>
 #include <map>
-#include "inspircd_config.h"
+#include "config.h"
 #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
@@ -94,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
@@ -108,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,
 
@@ -116,28 +106,28 @@ 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
         * send() syscall, or to ensure that writes are blocking when attempting
         * to use FD_WANT_FAST_WRITE.
         */
        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, 
+       FD_WRITE_WILL_BLOCK = 0x8000,
 
        /** Mask for trial read/trial write */
        FD_TRIAL_NOTE_MASK = 0x5000
@@ -146,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.
@@ -166,6 +153,9 @@ class CoreExport EventHandler : public classbase
  private:
        /** Private state maintained by socket engine */
        int event_mask;
+
+       void SetEventMask(int mask) { event_mask = mask; }
+
  protected:
        /** File descriptor.
         * All events which can be handled must have a file descriptor.  This
@@ -197,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 OnEventHandlerRead() = 0;
+
+       /** Called by the socket engine in case of a write event.
+        * The default implementation does nothing.
         */
-       virtual void HandleEvent(EventType et, int errornum = 0) = 0;
+       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;
 };
@@ -217,47 +211,102 @@ class CoreExport EventHandler : public classbase
  * its private members and internal behaviour
  * should be treated as blackboxed, and vary
  * from system to system and upon the config
- * settings chosen by the server admin. The current
- * version supports select, epoll and kqueue.
- * The configure script will enable a socket engine
- * based upon what OS is detected, and will derive
- * a class from SocketEngine based upon what it finds.
- * The derived classes file will also implement a
- * classfactory, SocketEngineFactory, which will
- * create a derived instance of SocketEngine using
- * polymorphism so that the core and modules do not
- * have to be aware of which SocketEngine derived
- * class they are using.
+ * settings chosen by the server admin.
  */
 class CoreExport SocketEngine
 {
- protected:
-       /** Current number of descriptors in the engine
-        */
-       int CurrentSetSize;
+ public:
+       /** Socket engine statistics: count of various events, bandwidth usage
+        */
+       class Statistics
+       {
+               mutable size_t indata;
+               mutable size_t outdata;
+               mutable time_t lastempty;
+
+               /** Reset the byte counters and lastempty if there wasn't a reset in this second.
+                */
+               void CheckFlush() const;
+
+        public:
+               /** Constructor, initializes member vars except indata and outdata because those are set to 0
+                * in CheckFlush() the first time Update() or GetBandwidth() is called.
+                */
+               Statistics() : lastempty(0), TotalEvents(0), ReadEvents(0), WriteEvents(0), ErrorEvents(0) { }
+
+               /** 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 UpdateWriteCounters(int len_out);
+
+               /** Get data transfer statistics.
+                * @param kbitpersec_in Filled with incoming traffic in this second in kbit/s.
+                * @param kbitpersec_out Filled with outgoing traffic in this second in kbit/s.
+                * @param kbitpersec_total Filled with total traffic in this second in kbit/s.
+                */
+               void CoreExport GetBandwidth(float& kbitpersec_in, float& kbitpersec_out, float& kbitpersec_total) const;
+
+               unsigned long TotalEvents;
+               unsigned long ReadEvents;
+               unsigned long WriteEvents;
+               unsigned long ErrorEvents;
+       };
+
+ private:
        /** Reference table, contains all current handlers
-        */
-       EventHandler** ref;
+        **/
+       static std::vector<EventHandler*> ref;
+
+       /** Current number of descriptors in the engine. */
+       static size_t CurrentSetSize;
+
+       /** The maximum number of descriptors in the engine. */
+       static size_t MaxSetSize;
+
        /** List of handlers that want a trial read/write
         */
-       std::set<int> trials;
+       static std::set<int> trials;
 
-       int MAX_DESCRIPTORS;
+       /** Socket engine statistics: count of various events, bandwidth usage
+        */
+       static Statistics stats;
 
-       size_t indata;
-       size_t outdata;
-       time_t lastempty;
+       /** Look up the fd limit using rlimit. */
+       static void LookupMaxFds();
 
-       void UpdateStats(size_t len_in, size_t len_out);
+       /** Terminates the program when the socket engine fails to initialize. */
+       static void InitError();
 
-       virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask) = 0;
-       void SetEventMask(EventHandler* eh, int value);
-public:
+       static void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
+
+       /** Add an event handler to the base socket engine. AddFd(EventHandler*, int) should call this.
+        */
+       static bool AddFdRef(EventHandler* eh);
+
+       static void DelFdRef(EventHandler* eh);
 
-       unsigned long TotalEvents;
-       unsigned long ReadEvents;
-       unsigned long WriteEvents;
-       unsigned long ErrorEvents;
+       template <typename T>
+       static void ResizeDouble(std::vector<T>& vect)
+       {
+               if (SocketEngine::CurrentSetSize > vect.size())
+                       vect.resize(vect.size() * 2);
+       }
+
+public:
+#ifndef _WIN32
+       typedef iovec IOVector;
+#else
+       typedef WindowsIOVec IOVector;
+#endif
 
        /** Constructor.
         * The constructor transparently initializes
@@ -266,23 +315,25 @@ public:
         * failure (for example, you try and enable
         * epoll on a 2.4 linux kernel) then this
         * function may bail back to the shell.
+        * @return void, but it is acceptable for this function to bail back to
+        * the shell or operating system on fatal error.
         */
-       SocketEngine();
+       static void Init();
 
        /** Destructor.
         * The destructor transparently tidies up
         * any resources used by the socket engine.
         */
-       virtual ~SocketEngine();
+       static void Deinit();
 
        /** 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
         */
-       virtual bool AddFd(EventHandler* eh, int event_mask) = 0;
+       static bool AddFd(EventHandler* eh, int event_mask);
 
        /** If you call this function and pass it an
         * event handler, that event handler will
@@ -295,17 +346,19 @@ public:
         * @param eh The event handler to change
         * @param event_mask The changes to make to the wait state
         */
-       void ChangeEventMask(EventHandler* eh, int event_mask);
+       static void ChangeEventMask(EventHandler* eh, int event_mask);
 
-       /** Returns the highest file descriptor you may store in the socket engine
-        * @return The maximum fd value
+       /** Returns the number of file descriptors reported by the system this program may use
+        * when it was started.
+        * @return If non-zero the number of file descriptors that the system reported that we
+        * may use.
         */
-       inline int GetMaxFds() const { return MAX_DESCRIPTORS; }
+       static size_t GetMaxFds() { return MaxSetSize; }
 
        /** Returns the number of file descriptors being queried
         * @return The set size
         */
-       inline int GetUsedFds() const { return CurrentSetSize; }
+       static size_t GetUsedFds() { return CurrentSetSize; }
 
        /** Delete an event handler from the engine.
         * This function call deletes an EventHandler
@@ -315,47 +368,40 @@ public:
         * required you must do this yourself.
         * @param eh The event handler object to remove
         */
-       virtual void DelFd(EventHandler* eh) = 0;
+       static void DelFd(EventHandler* eh);
 
        /** Returns true if a file descriptor exists in
         * the socket engine's list.
         * @param fd The event handler to look for
         * @return True if this fd has an event handler
         */
-       virtual bool HasFd(int fd);
+       static bool HasFd(int fd);
 
        /** Returns the EventHandler attached to a specific fd.
         * If the fd isnt in the socketengine, returns NULL.
         * @param fd The event handler to look for
         * @return A pointer to the event handler, or NULL
         */
-       virtual EventHandler* GetRef(int fd);
+       static EventHandler* GetRef(int fd);
 
        /** Waits for events and dispatches them to handlers.  Please note that
         * 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.
         */
-       virtual int DispatchEvents() = 0;
+       static int DispatchEvents();
 
        /** Dispatch trial reads and writes. This causes the actual socket I/O
         * to happen when writes have been pre-buffered.
         */
-       virtual void DispatchTrialWrites();
-
-       /** Returns the socket engines name.  This returns the name of the
-        * engine for use in /VERSION responses.
-        * @return The socket engine name
-        */
-       virtual std::string GetName() = 0;
+       static void DispatchTrialWrites();
 
        /** Returns true if the file descriptors in the given event handler are
         * within sensible ranges which can be handled by the socket engine.
         */
-       virtual bool BoundsCheckFd(EventHandler* eh);
+       static bool BoundsCheckFd(EventHandler* eh);
 
        /** Abstraction for BSD sockets accept(2).
         * This function should emulate its namesake system call exactly.
@@ -364,21 +410,20 @@ public:
         * @param addrlen The size of the sockaddr parameter.
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen);
+       static int Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen);
 
-       /** Abstraction for BSD sockets close(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.
-        * @return This method should return exactly the same values as the system call it emulates.
+       /** Close the underlying fd of an event handler, remove it from the socket engine and set the fd to -1.
+        * @param eh The EventHandler to close.
+        * @return 0 on success, a negative value on error
         */
-       int Close(EventHandler* fd);
+       static int Close(EventHandler* eh);
 
        /** Abstraction for BSD sockets close(2).
         * This function should emulate its namesake system call exactly.
         * This function should emulate its namesake system call exactly.
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int Close(int fd);
+       static int Close(int fd);
 
        /** Abstraction for BSD sockets send(2).
         * This function should emulate its namesake system call exactly.
@@ -388,7 +433,28 @@ public:
         * @param flags A flag value that controls the sending of the data.
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int Send(EventHandler* fd, const void *buf, size_t len, int flags);
+       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.
@@ -398,7 +464,7 @@ public:
         * @param flags A flag value that controls the reception of the data.
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int Recv(EventHandler* fd, void *buf, size_t len, int flags);
+       static int Recv(EventHandler* fd, void *buf, size_t len, int flags);
 
        /** Abstraction for BSD sockets recvfrom(2).
         * This function should emulate its namesake system call exactly.
@@ -410,7 +476,7 @@ public:
         * @param fromlen The size of the from parameter.
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, sockaddr *from, socklen_t *fromlen);
+       static int RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, sockaddr *from, socklen_t *fromlen);
 
        /** Abstraction for BSD sockets sendto(2).
         * This function should emulate its namesake system call exactly.
@@ -418,32 +484,30 @@ public:
         * @param buf The buffer in which the data that is sent is stored.
         * @param len The size of the buffer.
         * @param flags A flag value that controls the sending of the data.
-        * @param to The remote IP address and port.    
-        * @param tolen The size of the to parameter.
+        * @param address The remote IP address and port.
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int SendTo(EventHandler* fd, const void *buf, size_t len, int flags, const sockaddr *to, socklen_t tolen);
+       static int SendTo(EventHandler* fd, const void* buf, size_t len, int flags, const irc::sockets::sockaddrs& address);
 
        /** Abstraction for BSD sockets connect(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.
-        * @param serv_addr The server IP address and port.
-        * @param addrlen The size of the sockaddr parameter.
+        * @param address The server IP address and port.
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int Connect(EventHandler* fd, const sockaddr *serv_addr, socklen_t addrlen);
+       static int Connect(EventHandler* fd, const irc::sockets::sockaddrs& address);
 
        /** Make a file descriptor blocking.
         * @param fd a file descriptor to set to blocking mode
         * @return 0 on success, -1 on failure, errno is set appropriately.
         */
-       int Blocking(int fd);
+       static int Blocking(int fd);
 
        /** Make a file descriptor nonblocking.
         * @param fd A file descriptor to set to nonblocking mode
         * @return 0 on success, -1 on failure, errno is set appropriately.
         */
-       int NonBlocking(int fd);
+       static int NonBlocking(int fd);
 
        /** Abstraction for BSD sockets shutdown(2).
         * This function should emulate its namesake system call exactly.
@@ -451,29 +515,29 @@ public:
         * @param how What part of the socket to shut down
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int Shutdown(EventHandler* fd, int how);
+       static int Shutdown(EventHandler* fd, int how);
 
        /** Abstraction for BSD sockets shutdown(2).
         * This function should emulate its namesake system call exactly.
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int Shutdown(int fd, int how);
+       static int Shutdown(int fd, int how);
 
        /** Abstraction for BSD sockets bind(2).
         * This function should emulate its namesake system call exactly.
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int Bind(int fd, const irc::sockets::sockaddrs& addr);
+       static int Bind(int fd, const irc::sockets::sockaddrs& addr);
 
        /** Abstraction for BSD sockets listen(2).
         * This function should emulate its namesake system call exactly.
         * @return This method should return exactly the same values as the system call it emulates.
         */
-       int Listen(int sockfd, int backlog);
+       static int Listen(int sockfd, int backlog);
 
        /** Set SO_REUSEADDR and SO_LINGER on this file descriptor
         */
-       void SetReuse(int sockfd);
+       static void SetReuse(int sockfd);
 
        /** This function is called immediately after fork().
         * Some socket engines (notably kqueue) cannot have their
@@ -484,11 +548,11 @@ public:
         * @return void, but it is acceptable for this function to bail back to
         * the shell or operating system on fatal error.
         */
-       virtual void RecoverFromFork();
+       static void RecoverFromFork();
 
-       /** Get data transfer statistics, kilobits per second in and out and total.
+       /** Get data transfer and event statistics
         */
-       void GetStats(float &kbitpersec_in, float &kbitpersec_out, float &kbitpersec_total);
+       static const Statistics& GetStats() { return stats; }
 
        /** Should we ignore the error in errno?
         * Checks EAGAIN and WSAEWOULDBLOCK
@@ -516,8 +580,3 @@ inline bool SocketEngine::IgnoreError()
 
        return false;
 }
-
-SocketEngine* CreateSocketEngine();
-
-#endif
-
diff --git a/include/stdalgo.h b/include/stdalgo.h
new file mode 100644 (file)
index 0000000..d69f50b
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * 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 stdalgo
+{
+       namespace vector
+       {
+               /**
+                * Erase a single element from a vector by overwriting it with a copy of the last element,
+                * which is then removed. This, in contrast to vector::erase(), does not result in all
+                * elements after the erased element being moved.
+                * @param vect Vector to remove the element from
+                * @param it Iterator to the element to remove
+                * @return Nothing, but all iterators, references and pointers to the erased element and the
+                * last element are invalidated
+                */
+               template <typename T>
+               inline void swaperase(typename std::vector<T>& vect, const typename std::vector<T>::iterator& it)
+               {
+                       *it = vect.back();
+                       vect.pop_back();
+               }
+
+               /**
+                * Find and if exists, erase a single element from a vector by overwriting it with a
+                * copy of the last element, which is then removed. This, in contrast to vector::erase(),
+                * does not result in all elements after the erased element being moved.
+                * If the given value occurs multiple times, the one with the lowest index is removed.
+                * Individual elements are compared to the given value using operator==().
+                * @param vect Vector 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 if it wasn't found.
+                * If true, all iterators, references and pointers pointing to either the first element that
+                * is equal to val or to the last element are invalidated.
+                */
+               template <typename T>
+               inline bool swaperase(typename std::vector<T>& vect, const T& val)
+               {
+                       const typename std::vector<T>::iterator it = std::find(vect.begin(), vect.end(), val);
+                       if (it != vect.end())
+                       {
+                               swaperase(vect, it);
+                               return true;
+                       }
+                       return false;
+               }
+       }
+
+       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)));
+               }
+
+               /** Joins the contents of a vector to a string.
+                * @param sequence Zero or more items to join.
+                * @param separator The character to place between the items, defaults to ' ' (space).
+                * @return The joined string.
+                */
+               template<typename Collection>
+               inline std::string join(const Collection& sequence, char separator = ' ')
+               {
+                       std::string joined;
+                       if (sequence.empty())
+                               return joined;
+
+                       for (typename Collection::const_iterator iter = sequence.begin(); iter != sequence.end(); ++iter)
+                               joined.append(ConvToStr(*iter)).push_back(separator);
+
+                       joined.erase(joined.end() - 1);
+                       return joined;
+               }
+
+               /** 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
+        */
+       template <typename T>
+       struct defaultdeleter
+       {
+               void operator()(T* o)
+               {
+                       delete o;
+               }
+       };
+
+       /**
+        * Deleter that adds the item to the cull list, that is, queues it for
+        * deletion at the end of the current mainloop iteration
+        */
+       struct culldeleter
+       {
+               void operator()(classbase* item);
+       };
+
+       /**
+        * Deletes all elements in a container using operator delete
+        * @param cont The container containing the elements to delete
+        */
+       template <template<typename, typename> class Cont, typename T, typename Alloc>
+       inline void delete_all(const Cont<T*, Alloc>& cont)
+       {
+               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());
+       }
+
+       namespace string
+       {
+               /**
+                * Escape a string
+                * @param str String to escape
+                * @param out Output, must not be the same string as str
+                */
+               template <char from, char to, char esc>
+               inline void escape(const std::string& str, std::string& out)
+               {
+                       for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
+                       {
+                               char c = *i;
+                               if (c == esc)
+                                       out.append(2, esc);
+                               else
+                               {
+                                       if (c == from)
+                                       {
+                                               out.push_back(esc);
+                                               c = to;
+                                       }
+                                       out.push_back(c);
+                               }
+                       }
+               }
+
+               /**
+                * Escape a string using the backslash character as the escape character
+                * @param str String to escape
+                * @param out Output, must not be the same string as str
+                */
+               template <char from, char to>
+               inline void escape(const std::string& str, std::string& out)
+               {
+                       escape<from, to, '\\'>(str, out);
+               }
+
+               /**
+                * Unescape a string
+                * @param str String to unescape
+                * @param out Output, must not be the same string as str
+                * @return True if the string was unescaped, false if an invalid escape sequence is present in the input in which case out will contain a partially unescaped string
+                */
+               template<char from, char to, char esc>
+               inline bool unescape(const std::string& str, std::string& out)
+               {
+                       for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
+                       {
+                               char c = *i;
+                               if (c == '\\')
+                               {
+                                       ++i;
+                                       if (i == str.end())
+                                               return false;
+
+                                       char nextc = *i;
+                                       if (nextc == esc)
+                                               c = esc;
+                                       else if (nextc != to)
+                                               return false; // Invalid escape sequence
+                                       else
+                                               c = from;
+                               }
+                               out.push_back(c);
+                       }
+                       return true;
+               }
+
+               /**
+                * Unescape a string using the backslash character as the escape character
+                * @param str String to unescape
+                * @param out Output, must not be the same string as str
+                * @return True if the string was unescaped, false if an invalid escape sequence is present in the input in which case out will contain a partially unescaped string
+                */
+               template <char from, char to>
+               inline bool unescape(const std::string& str, std::string& out)
+               {
+                       return unescape<from, to, '\\'>(str, out);
+               }
+       }
+}
diff --git a/include/testsuite.h b/include/testsuite.h
deleted file mode 100644 (file)
index f91e508..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *
- * 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/>.
- */
-
-
-#ifndef TESTSUITE_H
-#define TESTSUITE_H
-
-class TestSuite
-{
-       bool RealGenerateUIDTests();
- public:
-       TestSuite();
-       ~TestSuite();
-
-       bool DoThreadTests();
-       bool DoWildTests();
-       bool DoCommaSepStreamTests();
-       bool DoSpaceSepStreamTests();
-       bool DoGenerateUIDTests();
-};
-
-#endif
index 4bf5a48f38399a94d367989f405f9bff213f5936..964b8d796d0a5c97c36751c68d59c7aa2f90eb62 100644 (file)
  */
 
 
-#ifndef THREADENGINE_H
-#define THREADENGINE_H
+#pragma once
 
 #include <vector>
 #include <string>
 #include <map>
-#include "inspircd_config.h"
+#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!
@@ -39,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?)
@@ -48,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.
         */
@@ -109,7 +108,7 @@ class CoreExport QueuedThread : public Thread
                queue.Wakeup();
                queue.Unlock();
        }
-       virtual void SetExitFlag()
+       void SetExitFlag() CXX11_OVERRIDE
        {
                queue.Lock();
                Thread::SetExitFlag();
@@ -158,7 +157,7 @@ class CoreExport SocketThread : public Thread
                queue.Wakeup();
                queue.Unlock();
        }
-       virtual void SetExitFlag()
+       void SetExitFlag() CXX11_OVERRIDE
        {
                queue.Lock();
                Thread::SetExitFlag();
@@ -172,6 +171,3 @@ class CoreExport SocketThread : public Thread
         */
        virtual void OnNotify() = 0;
 };
-
-#endif
-
index 5168ed238cb53dbb5f4862af37efa151448bece9..ca33542607839ea168c48cb750af928552cca35b 100644 (file)
@@ -18,8 +18,7 @@
  */
 
 
-#ifndef THREADENGINE_PTHREAD_H
-#define THREADENGINE_PTHREAD_H
+#pragma once
 
 #include <pthread.h>
 #include "typedefs.h"
 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
@@ -54,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
@@ -80,7 +74,7 @@ class CoreExport ThreadData
  */
 class CoreExport Mutex
 {
- private:
+ protected:
        pthread_mutex_t putex;
  public:
        /** Constructor.
@@ -109,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);
@@ -143,7 +124,7 @@ class ThreadQueueData
 
        void Wait()
        {
-               pthread_cond_wait(&cond, &mutex);
+               pthread_cond_wait(&cond, &putex);
        }
 };
 
@@ -153,6 +134,3 @@ class ThreadSignalData
  public:
        ThreadSignalSocket* sock;
 };
-
-
-#endif
index f068ac70785ff35a3152bc65535b103f854fcc6f..aac7b8b974ca3d49efcc20673db7266bc96c5372 100644 (file)
  */
 
 
-#ifndef THREADENGINE_WIN32_H
-#define THREADENGINE_WIN32_H
+#pragma once
 
-#include "inspircd_config.h"
+#include "config.h"
 #include "base.h"
 
 class Thread;
@@ -39,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);
 
@@ -54,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
@@ -101,9 +99,8 @@ class CoreExport Mutex
        }
 };
 
-class ThreadQueueData
+class ThreadQueueData : public Mutex
 {
-       CRITICAL_SECTION mutex;
        HANDLE event;
  public:
        ThreadQueueData()
@@ -111,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()
@@ -137,9 +122,9 @@ class ThreadQueueData
 
        void Wait()
        {
-               LeaveCriticalSection(&mutex);
+               Unlock();
                WaitForSingleObject(event, INFINITE);
-               EnterCriticalSection(&mutex);
+               Lock();
        }
 };
 
@@ -152,6 +137,3 @@ class ThreadSignalData
                connFD = -1;
        }
 };
-
-#endif
-
index 9bb7128b85ac7f6283ec51a0e6986fc7464a6a1d..a116d456c4e7a78cc499ba53862c9e8e76ebc76b 100644 (file)
@@ -19,8 +19,9 @@
  */
 
 
-#ifndef INSPIRCD_TIMER_H
-#define INSPIRCD_TIMER_H
+#pragma once
+
+class Module;
 
 /** Timer class for one-second resolution timers
  * Timer provides a facility which allows module
  * resolution. To use Timer, inherit a class from
  * Timer, then insert your inherited class into the
  * queue using Server::AddTimer(). The Tick() method of
- * your object (which you should override) will be called
+ * your object (which you have to override) will be called
  * at the given time.
  */
 class CoreExport Timer
 {
- private:
        /** The triggering time
         */
        time_t trigger;
+
        /** Number of seconds between triggers
         */
-       long secs;
+       unsigned int secs;
+
        /** True if this is a repeating timer
         */
        bool repeat;
+
  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(long 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, does nothing.
+       /** Default destructor, removes the timer from the timer manager
         */
-       virtual ~Timer() { }
+       virtual ~Timer();
 
        /** Retrieve the current triggering time
         */
-       virtual time_t GetTimer()
+       time_t GetTrigger() const
        {
                return trigger;
        }
 
        /** Sets the trigger timeout to a new value
+        * This does not update the bookkeeping in TimerManager, use SetInterval()
+        * to change the interval between ticks while keeping TimerManager updated
         */
-       virtual void SetTimer(time_t t)
+       void SetTrigger(time_t nexttrigger)
        {
-               trigger = t;
+               trigger = nexttrigger;
        }
 
+       /** Sets the interval between two ticks.
+        */
+       void SetInterval(unsigned int interval);
+
        /** Called when the timer ticks.
         * You should override this method with some useful code to
         * handle the tick event.
+        * @param TIME The current time.
+        * @return True if the Timer object is still valid, false if it was destructed.
         */
-       virtual void Tick(time_t TIME) = 0;
+       virtual bool Tick(time_t TIME) = 0;
 
        /** Returns true if this timer is set to repeat
         */
-       bool GetRepeat()
+       bool GetRepeat() const
        {
                return repeat;
        }
@@ -91,7 +96,7 @@ class CoreExport Timer
        /** Returns the interval (number of seconds between ticks)
         * of this timer object.
         */
-       long GetSecs()
+       unsigned int GetInterval() const
        {
                return secs;
        }
@@ -99,12 +104,6 @@ class CoreExport Timer
        /** Cancels the repeat state of a repeating timer.
         * If you call this method, then the next time your
         * timer ticks, it will be removed immediately after.
-        * You should use this method call to remove a recurring
-        * timer if you wish to do so within the timer's Tick
-        * event, as calling TimerManager::DelTimer() from within
-        * the Timer::Tick() method is dangerous and may
-        * cause a segmentation fault. Calling CancelRepeat()
-        * is safe in this case.
         */
        void CancelRepeat()
        {
@@ -112,24 +111,19 @@ class CoreExport Timer
        }
 };
 
-
 /** 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
 {
- protected:
+       typedef std::multimap<time_t, Timer*> TimerMap;
+
        /** A list of all pending timers
         */
-       std::vector<Timer *> Timers;
+       TimerMap Timers;
 
  public:
-       /** Constructor
-        */
-       TimerManager();
-       ~TimerManager();
-
        /** Tick all pending Timers
         * @param TIME the current system time
         */
@@ -140,15 +134,8 @@ class CoreExport TimerManager
         */
        void AddTimer(Timer *T);
 
-       /** Delete an Timer
-        * @param T an Timer derived class to delete
+       /** Remove a Timer
+        * @param T an Timer derived class to remove
         */
        void DelTimer(Timer* T);
-
-       /** Compares two timers
-        */
-       static bool TimerComparison( Timer *one,  Timer*two);
 };
-
-#endif
-
diff --git a/include/token_list.h b/include/token_list.h
new file mode 100644 (file)
index 0000000..ffa3b89
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+#include "compat.h"
+
+class CoreExport TokenList
+{
+ private:
+       /** Whether this list includes all tokens by default. */
+       bool permissive;
+
+       /** Either the tokens to exclude if in permissive mode or the tokens to include if in strict mode. */
+       insp::flat_set<std::string, irc::insensitive_swo> tokens;
+
+ public:
+       /** Adds a space-delimited list of tokens to the token list.
+        * @param tokenlist The list of space-delimited tokens to add.
+        */
+       void AddList(const std::string& tokenlist);
+
+       /** Adds a single token to the token list.
+        * @param token The token to add.
+        */
+       void Add(const std::string& token);
+
+       /** Removes all tokens from the token list. */
+       void Clear();
+
+       /** Determines whether the specified token exists in the token list.
+        * @param token The token to search for.
+        */
+       bool Contains(const std::string& token) const;
+
+       /** Removes the specified token from the token list.
+        * @param token The token to remove.
+        */
+       void Remove(const std::string& token);
+
+       /** Retrieves a string which represents the contents of this token list. */
+       std::string ToString() const;
+
+       /** Determines whether the specified token list contains the same tokens as this instance.
+        * @param other The tokenlist to compare against.
+        * @return True if the token lists are equal; otherwise, false.
+        */
+       bool operator==(const TokenList& other) const;
+};
index 03593d40f45e616998a9509d37fad8b400d8c280..20fc596be444ac29c0665da90af031d0019e0676 100644 (file)
  */
 
 
-#ifndef TYPEDEFS_H
-#define TYPEDEFS_H
+#pragma once
 
 class BanCacheManager;
-class BanItem;
 class BufferedSocket;
 class Channel;
 class Command;
-class ConfigReader;
+class ConfigStatus;
 class ConfigTag;
-class DNSHeader;
-class DNSRequest;
 class Extensible;
 class FakeUser;
 class InspIRCd;
 class Invitation;
-class InviteBase;
+class IOHookProvider;
 class LocalUser;
 class Membership;
 class Module;
 class OperInfo;
+class ProtocolServer;
 class RemoteUser;
+class Server;
 class ServerConfig;
 class ServerLimits;
 class Thread;
 class User;
-class UserResolver;
 class XLine;
 class XLineManager;
 class XLineFactory;
 struct ConnectClass;
 struct ModResult;
-struct ResourceRecord;
+
+namespace ClientProtocol
+{
+       class Event;
+       class EventProvider;
+       class Message;
+       class MessageTagProvider;
+       class Serializer;
+
+       typedef std::vector<Message*> MessageList;
+       typedef std::vector<std::string> ParamList;
+       typedef std::string SerializedMessage;
+
+       struct MessageTagData
+       {
+               MessageTagProvider* tagprov;
+               std::string value;
+               void* provdata;
+
+               MessageTagData(MessageTagProvider* prov, const std::string& val, void* data = NULL);
+       };
+
+       /** Map of message tag values and providers keyed by their name.
+        * Sorted in descending order to ensure tag names beginning with symbols (such as '+') come later when iterating
+        * the container than tags with a normal name.
+        */
+       typedef insp::flat_map<std::string, MessageTagData, std::greater<std::string> > TagMap;
+}
 
 #include "hashcomp.h"
 #include "base.h"
 
-#ifdef HASHMAP_DEPRECATED
-       typedef nspace::hash_map<std::string, User*, nspace::insensitive, irc::StrHashComp> user_hash;
-       typedef nspace::hash_map<std::string, Channel*, nspace::insensitive, irc::StrHashComp> chan_hash;
-#else
-       typedef nspace::hash_map<std::string, User*, nspace::hash<std::string>, irc::StrHashComp> user_hash;
-       typedef nspace::hash_map<std::string, Channel*, nspace::hash<std::string>, irc::StrHashComp> chan_hash;
-#endif
-
-/** A list holding local users, this is the type of UserManager::local_users
- */
-typedef std::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 channels to which a user has been invited and has not yet joined, and the time at which they'll expire.
- */
-typedef std::vector<Invitation*> InviteList;
+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;
 
-/** Holds a complete list of all allow and deny tags from the configuration file (connection classes)
+/** List of channels to consider when building the neighbor list of a user
  */
-typedef std::vector<reference<ConnectClass> > ClassVector;
-
-/** Typedef for the list of user-channel records for a user
- */
-typedef std::set<Channel*> UserChanList;
-
-/** Shorthand for an iterator into a UserChanList
- */
-typedef UserChanList::iterator UCListIter;
-
-/** Holds a complete ban list
- */
-typedef std::vector<BanItem> BanList;
-
-/** A list of custom modes parameters on a channel
- */
-typedef std::map<char,std::string> CustomModeList;
+typedef std::vector<Membership*> IncludeChanList;
 
 /** A cached text file stored with its contents as lines
  */
 typedef std::vector<std::string> file_cache;
 
-/** A configuration key and value pair
+/** A mapping of configuration keys to their assigned values.
  */
-typedef std::pair<std::string, std::string> KeyVal;
+typedef insp::flat_map<std::string, std::string, irc::insensitive_swo> ConfigItems;
 
 /** The entire configuration
  */
-typedef std::multimap<std::string, reference<ConfigTag> > ConfigDataHash;
+typedef std::multimap<std::string, reference<ConfigTag>, irc::insensitive_swo> ConfigDataHash;
 
 /** Iterator of ConfigDataHash */
 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
- */
-#ifdef HASHMAP_DEPRECATED
-       typedef nspace::hash_map<std::string, Command*, nspace::insensitive, irc::StrHashComp> Commandtable;
-#else
-       typedef nspace::hash_map<std::string, Command*, nspace::hash<std::string>, irc::StrHashComp> Commandtable;
-#endif
-
-/** 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;
 
-/** A set of strings.
- */
-typedef std::vector<std::string> string_list;
-
 /** Contains an ident and host split into two strings
  */
 typedef std::pair<std::string, std::string> IdentHostPair;
@@ -150,7 +120,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
  */
@@ -164,6 +134,7 @@ typedef XLineContainer::iterator ContainerIter;
  */
 typedef XLineLookup::iterator LookupIter;
 
-
-#endif
-
+namespace Stats
+{
+       class Context;
+}
index 17061bdee972a72f296459bfad966e93f735ccd2..d24a63e94be16320d36885000cf17bffe49c5040 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#pragma once
 
-/**
- * This is the maximum length of a UUID (unique user identifier).
- * This length is set in compliance with TS6 protocol, and really should not be changed. Ever.
- * It allows for a lot of clients as-is. -- w00t.
- */
-#define UUID_LENGTH 10
+class CoreExport UIDGenerator
+{
+ private:
+       /** Holds the current UID. Used to generate the next one.
+        */
+       std::string current_uid;
+
+       /** Increments the current UID by one.
+        */
+       void IncrementUID(unsigned int pos);
+
+ public:
+       /**
+       * This is the maximum length of a UUID (unique user identifier).
+       * This length is set in compliance with TS6 protocol, and really should not be changed. Ever.
+       * It allows for a lot of clients as-is. -- w00t.
+       */
+       static const unsigned int UUID_LENGTH = 9;
+
+       /** Initializes this UID generator with the given SID
+        * @param sid SID that conforms to InspIRCd::IsSID()
+        */
+       void init(const std::string& sid);
 
+       /** Returns the next available UID for this server.
+        */
+       std::string GetUID();
 
+       /** Generates a pseudorandom SID based on a servername and a description
+        * Guaranteed to return the same if invoked with the same parameters
+        * @param servername The server name to use as seed
+        * @param serverdesc The server description to use as seed
+        * @return A valid SID
+        */
+       static std::string GenerateSID(const std::string& servername, const std::string& serverdesc);
+};
index 2a9d6b47b2b9ca54c893ba75a07bbe296a74f9e9..c013e59aeab343463bfef2ac374021471fd639de 100644 (file)
  */
 
 
-#ifndef USERMANAGER_H
-#define USERMANAGER_H
+#pragma once
 
 #include <list>
 
-/** A list of ip addresses cross referenced against clone counts */
-typedef std::map<irc::sockets::cidr_mask, unsigned int> clonemap;
-
-class CoreExport UserManager
+class CoreExport UserManager : public fakederef<UserManager>
 {
+ public:
+       struct CloneCounts
+       {
+               unsigned int global;
+               unsigned int local;
+               CloneCounts() : global(0), local(0) { }
+       };
+
+       /** Container that maps IP addresses to clone counts
+        */
+       typedef std::map<irc::sockets::cidr_mask, CloneCounts> CloneMap;
+
+       /** Sequence container in which each element is a User*
+        */
+       typedef std::vector<User*> OperList;
+
+       /** A list holding local users
+       */
+       typedef insp::intrusive_list<LocalUser> LocalList;
+
  private:
-       /** Map of local ip addresses for clone counting
+       /** Map of IP addresses for clone counting
+        */
+       CloneMap clonemap;
+
+       /** A CloneCounts that contains zero for both local and global
+        */
+       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.
         */
-       clonemap local_clones;
+       already_sent_t already_sent_id;
+
  public:
+       /** Constructor, initializes variables
+        */
        UserManager();
 
-       ~UserManager()
-       {
-               for (user_hash::iterator i = clientlist->begin();i != clientlist->end();i++)
-               {
-                       delete i->second;
-               }
-               clientlist->clear();
-               delete clientlist;
-               delete uuidlist;
-       }
-
-       /** Client list, a hash_map containing all clients, local and remote
+       /** Destructor, destroys all users in clientlist
         */
-       user_hash* clientlist;
+       ~UserManager();
 
-       /** Client list stored by UUID. Contains all clients, and is updated
-        * automatically by the constructor and destructor of User.
+       /** Nickname string -> User* map. Contains all users, including unregistered ones.
         */
-       user_hash* uuidlist;
+       user_hash clientlist;
 
-       /** Local client list, a list containing only local clients
+       /** UUID -> User* map. Contains all users, including unregistered ones.
         */
-       LocalUserList local_users;
+       user_hash uuidlist;
 
        /** Oper list, a vector containing all local and remote opered users
         */
-       std::list<User*> all_opers;
+       OperList all_opers;
 
        /** Number of unregistered users online right now.
         * (Unregistered means before USER/NICK/dns)
         */
        unsigned int unregistered_count;
 
-       /** Number of elements in local_users
-        */
-       unsigned int local_count;
-
-       /** Map of global ip addresses for clone counting
-        * XXX - this should be private, but m_clones depends on it currently.
+       /** 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.
         */
-       clonemap global_clones;
+       void DoBackgroundUserStuff();
 
-       /** 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
-        * @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 char* operreason = "");
-
-       /** Add a user to the local clone map
-        * @param user The user to add
+        * @param operreason The quit reason to show to opers, can be NULL if same as quitreason
         */
-       void AddLocalClone(User *user);
+       void QuitUser(User* user, const std::string& quitreason, const std::string* operreason = NULL);
 
-       /** Add a user to the global clone map
+       /** Add a user to the clone map
         * @param user The user to add
         */
-       void AddGlobalClone(User *user);
+       void AddClone(User* user);
 
        /** Remove all clone counts from the user, you should
         * use this if you change the user's IP address
@@ -112,53 +125,57 @@ class CoreExport UserManager
         */
        void RemoveCloneCounts(User *user);
 
-       /** Rebuild clone counts
+       /** Rebuild clone counts. Required when \<cidr> settings change.
         */
        void RehashCloneCounts();
 
-       /** Return the number of global clones of this user
-        * @param user The user to get a count for
-        * @return The global clone count of this user
+       /** 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
+        * must assume that it becomes invalid as soon as you call any function other than
+        * your own.
         */
-       unsigned long GlobalCloneCount(User *user);
+       const CloneCounts& GetCloneCounts(User* user) const;
 
-       /** Return the number of local clones of this user
-        * @param user The user to get a count for
-        * @return The local clone count of this user
+       /** Return a map containg IP addresses and their clone counts
+        * @return The clone count map
         */
-       unsigned long LocalCloneCount(User *user);
+       const CloneMap& GetCloneMap() const { return clonemap; }
 
-       /** Return a count of users, unknown and known connections
-        * @return The number of users
+       /** Return a count of all global users, unknown and known connections
+        * @return The number of users on the network, including local unregistered users
         */
-       unsigned int UserCount();
+       unsigned int UserCount() const { return this->clientlist.size(); }
 
-       /** Return a count of fully registered connections only
-        * @return The number of registered users
+       /** Return a count of fully registered connections on the network
+        * @return The number of registered users on the network
         */
-       unsigned int RegisteredUserCount();
+       unsigned int RegisteredUserCount() { return this->clientlist.size() - this->UnregisteredUserCount(); }
 
-       /** Return a count of opered (umode +o) users only
-        * @return The number of opers
+       /** Return a count of opered (umode +o) users on the network
+        * @return The number of opers on the network
         */
-       unsigned int OperCount();
+       unsigned int OperCount() const { return this->all_opers.size(); }
 
-       /** Return a count of unregistered (before NICK/USER) users only
-        * @return The number of unregistered (unknown) connections
+       /** Return a count of local unregistered (before NICK/USER) users
+        * @return The number of local unregistered (unknown) connections
         */
-       unsigned int UnregisteredUserCount();
+       unsigned int UnregisteredUserCount() const { return this->unregistered_count; }
 
-       /** Return a count of local users on this server only
-        * @return The number of local users
+       /** Return a count of local registered users
+        * @return The number of registered local users
         */
-       unsigned int LocalUserCount();
-
-
+       unsigned int LocalUserCount() const { return (this->local_users.size() - this->UnregisteredUserCount()); }
 
+       /** Get a hash map containing all users, keyed by their nickname
+        * @return A hash map mapping nicknames to User pointers
+        */
+       user_hash& GetUsers() { return clientlist; }
 
-       /** Number of users with a certain mode set on them
+       /** Get a list containing all local users
+        * @return A const list of local users
         */
-       int ModeCount(const char mode);
+       const LocalList& GetLocalUsers() const { return local_users; }
 
        /** Send a server notice to all local users
         * @param text The text format string to send
@@ -166,11 +183,8 @@ class CoreExport UserManager
         */
        void ServerNoticeAll(const char* text, ...) CUSTOM_PRINTF(2, 3);
 
-       /** Send a server message (PRIVMSG) to all local users
-        * @param text The text format string to send
-        * @param ... The format arguments
+       /** Retrieves the next already sent id, guaranteed to be not equal to any user's already_sent field
+        * @return Next already_sent id
         */
-       void ServerPrivmsgAll(const char* text, ...) CUSTOM_PRINTF(2, 3);
+       already_sent_t NextAlreadySentId();
 };
-
-#endif
index 88abfbcd1c6dc8c682fa3a2597f7c2e15891f148..dc7da40fe2a7e2adb817fcba8894f4b185ffa3d7 100644 (file)
  */
 
 
-#ifndef USERS_H
-#define USERS_H
+#pragma once
 
 #include "socket.h"
 #include "inspsocket.h"
-#include "dns.h"
 #include "mode.h"
 #include "membership.h"
 
@@ -42,19 +40,6 @@ enum ClassTypes {
        CC_NAMED = 2
 };
 
-/** RFC1459 channel modes
- */
-enum UserModes {
-       /** +s: Server notice mask */
-       UM_SNOMASK = 's' - 65,
-       /** +w: WALLOPS */
-       UM_WALLOPS = 'w' - 65,
-       /** +i: Invisible */
-       UM_INVISIBLE = 'i' - 65,
-       /** +o: Operator */
-       UM_OPERATOR = 'o' - 65
-};
-
 /** Registration state of a user, e.g.
  * have they sent USER, NICK, PASS yet?
  */
@@ -146,6 +131,15 @@ struct CoreExport ConnectClass : public refcountbase
         */
        unsigned long limit;
 
+       /** If set to true, no user DNS lookups are to be performed
+        */
+       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);
@@ -250,12 +244,40 @@ class CoreExport User : public Extensible
         */
        std::string cachedip;
 
+       /** If set then the hostname which is displayed to users. */
+       std::string displayhost;
+
+       /** The real hostname of this user. */
+       std::string realhost;
+
+       /** The real name of this user. */
+       std::string realname;
+
+       /** The user's mode list.
+        * Much love to the STL for giving us an easy to use bitset, saving us RAM.
+        * if (modes[modeid]) is set, then the mode is set.
+        * For example, to work out if mode +i is set, we check the field
+        * User::modes[invisiblemode->modeid] == true.
+        */
+       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;
+       };
 
-       /** Hostname of connection.
-        * This should be valid as per RFC1035.
+       /** List of Memberships for this user
         */
-       std::string host;
+       typedef insp::intrusive_list<Membership> ChanList;
 
        /** Time that the object was instantiated (used for TS calculation etc)
        */
@@ -267,10 +289,6 @@ class CoreExport User : public Extensible
         */
        time_t signon;
 
-       /** Time that the connection last sent a message, used to calculate idle time
-        */
-       time_t idle_lastmsg;
-
        /** Client address that the user is connected from.
         * Do not modify this value directly, use SetClientIP() to change it.
         * Port is not valid for remote users.
@@ -293,27 +311,6 @@ class CoreExport User : public Extensible
         */
        std::string ident;
 
-       /** The host displayed to non-opers (used for cloaking etc).
-        * This usually matches the value of User::host.
-        */
-       std::string dhost;
-
-       /** The users full name (GECOS).
-        */
-       std::string fullname;
-
-       /** The user's mode list.
-        * NOT a null terminated string.
-        * Also NOT an array.
-        * Much love to the STL for giving us an easy to use bitset, saving us RAM.
-        * if (modes[modeletter-65]) is set, then the mode is
-        * set, for example, to work out if mode +s is set, we  check the field
-        * User::modes['s'-65] != 0.
-        * The following RFC characters o, w, s, i have constants defined via an
-        * enum, such as UM_SERVERNOTICE and UM_OPETATOR.
-        */
-       std::bitset<64> modes;
-
        /** What snomasks are set on this user.
         * This functions the same as the above modes.
         */
@@ -321,11 +318,11 @@ class CoreExport User : public Extensible
 
        /** Channels this user is on
         */
-       UserChanList chans;
+       ChanList chans;
 
        /** The server the user is connected to.
         */
-       const std::string server;
+       Server* server;
 
        /** The user's away message.
         * If this string is empty, the user is not marked as away.
@@ -333,7 +330,7 @@ class CoreExport User : public Extensible
        std::string awaymsg;
 
        /** Time the user last went away.
-        * This is ONLY RELIABLE if user IS_AWAY()!
+        * This is ONLY RELIABLE if user IsAway()!
         */
        time_t awaytime;
 
@@ -347,16 +344,6 @@ class CoreExport User : public Extensible
         */
        unsigned int registered:3;
 
-       /** True when DNS lookups are completed.
-        * The UserResolver classes res_forward and res_reverse will
-        * set this value once they complete.
-        */
-       unsigned int dns_done:1;
-
-       /** Whether or not to send an snotice about this user's quitting
-        */
-       unsigned int quietquit:1;
-
        /** If this is set to true, then all socket operations for the user
         * are dropped into the bit-bucket.
         * This value is set by QuitUser, and is not needed seperately from that call.
@@ -364,27 +351,27 @@ class CoreExport User : public Extensible
         */
        unsigned int quitting:1;
 
-       /** Recursion fix: user is out of SendQ and will be quit as soon as possible.
-        * This can't be handled normally because QuitUser itself calls Write on other
-        * users, which could trigger their SendQ to overrun.
-        */
-       unsigned int quitting_sendq:1;
+       /** What type of user is this? */
+       const UserType usertype:2;
 
-       /** This is true if the user matched an exception (E:Line). It is used to save time on ban checks.
+       /** Get client IP string from sockaddr, using static internal buffer
+        * @return The IP string
         */
-       unsigned int exempt:1;
+       const std::string& GetIPString();
 
-       /** has the user responded to their previous ping?
+       /** Retrieves this user's hostname.
+        * @param uncloak If true then return the real host; otherwise, the display host.
         */
-       unsigned int lastping:1;
+       const std::string& GetHost(bool uncloak) const;
 
-       /** What type of user is this? */
-       const unsigned int usertype:2;
+       /** Retrieves this user's displayed hostname. */
+       const std::string& GetDisplayedHost() const;
 
-       /** Get client IP string from sockaddr, using static internal buffer
-        * @return The IP string
-        */
-       const char* GetIPString();
+       /** Retrieves this user's real hostname. */
+       const std::string& GetRealHost() const;
+
+       /** Retrieves this user's real name. */
+       const std::string& GetRealName() const;
 
        /** Get CIDR mask, using default range, for this user
         */
@@ -393,20 +380,14 @@ class CoreExport User : public Extensible
        /** Sets the client IP for this user
         * @return true if the conversion was successful
         */
-       virtual bool SetClientIP(const char* sip, bool recheck_eline = true);
+       virtual bool SetClientIP(const std::string& address);
 
-       virtual void SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline = true);
+       virtual void SetClientIP(const irc::sockets::sockaddrs& sa);
 
        /** Constructor
         * @throw CoreException if the UID allocated to the user already exists
         */
-       User(const std::string &uid, const std::string& srv, int objtype);
-
-       /** Check if the user matches a G or K line, and disconnect them if they do.
-        * @param doZline True if ZLines should be checked (if IP has changed since initial connect)
-        * Returns true if the user matched a ban, false else.
-        */
-       bool CheckLines(bool doZline = false);
+       User(const std::string& uid, Server* srv, UserType objtype);
 
        /** Returns the full displayed host of the user
         * This member function returns the hostname of the user as seen by other users
@@ -428,18 +409,17 @@ class CoreExport User : public Extensible
         */
        void InvalidateCache();
 
-       /** Create a displayable mode string for this users snomasks
-        * @return The notice mask character sequence
+       /** Returns whether this user is currently away or not. If true,
+        * further information can be found in User::awaymsg and User::awaytime
+        * @return True if the user is away, false otherwise
         */
-       const char* FormatNoticeMasks();
+       bool IsAway() const { return (!awaymsg.empty()); }
 
-       /** Process a snomask modifier string, e.g. +abc-de
-        * @param sm A sequence of notice mask characters
-        * @return The cleaned mode sequence which can be output,
-        * e.g. in the above example if masks c and e are not
-        * valid, this function will return +ab-d
+       /** Returns whether this user is an oper or not. If true,
+        * oper information can be obtained from User::oper
+        * @return True if the user is an oper, false otherwise
         */
-       std::string ProcessNoticeMasks(const char *sm);
+       bool IsOper() const { return oper; }
 
        /** Returns true if a notice mask is set
         * @param sm A notice mask character to check
@@ -447,28 +427,28 @@ class CoreExport User : public Extensible
         */
        bool IsNoticeMaskSet(unsigned char sm);
 
-       /** Changed a specific notice mask value
-        * @param sm The server notice mask to change
-        * @param value An on/off value for this mask
+       /** 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.
         */
-       void SetNoticeMask(unsigned char sm, bool value);
-
-       /** Create a displayable mode string for this users umodes
-        * @param showparameters The mode string
-        */
-       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(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
+        * @param mh The user mode
         * @param value On or off setting of the mode
         */
-       void SetMode(unsigned char m, bool value);
+       void SetMode(ModeHandler* mh, bool value);
+       void SetMode(ModeHandler& mh, bool value) { SetMode(&mh, value); }
 
        /** 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
@@ -476,32 +456,24 @@ class CoreExport User : public Extensible
         * @param command A command (should be all CAPS)
         * @return True if this user can execute the command
         */
-       virtual bool HasPermission(const std::string &command);
+       virtual bool HasCommandPermission(const std::string& command);
 
        /** Returns true if a user has a given permission.
         * This is used to check whether or not users may perform certain actions which admins may not wish to give to
         * all operators, yet are not commands. An example might be oper override, mass messaging (/notice $*), etc.
         *
         * @param privstr The priv to chec, e.g. "users/override/topic". These are loaded free-form from the config file.
-        * @param noisy If set to true, the user is notified that they do not have the specified permission where applicable. If false, no notification is sent.
         * @return True if this user has the permission in question.
         */
-       virtual bool HasPrivPermission(const std::string &privstr, bool noisy = false);
+       virtual bool HasPrivPermission(const std::string& privstr);
 
        /** 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);
-
-       /** Creates a wildcard host.
-        * Takes a buffer to use and fills the given buffer with the host in the format *!*\@hostname
-        * @return The wildcarded hostname in *!*\@host form
-        */
-       char* MakeWildHost();
+       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
@@ -515,122 +487,145 @@ class CoreExport User : public Extensible
         */
        const std::string& MakeHostIP();
 
-       /** Add the user to WHOWAS system
-        */
-       void AddToWhoWas();
-
        /** Oper up the user using the given opertype.
         * This will also give the +o usermode.
         */
        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.
-        */
-       inline bool ForceNickChange(const char* newnick) { return ChangeNick(newnick, true); }
-
        /** Oper down.
         * This will clear the +o usermode and unset the user's oper type
         */
        void UnOper();
 
-       /** Write text to this user, appending CR/LF. Works on local users only.
-        * @param text A std::string to send to the user
+       /** Sends a server notice to this user.
+        * @param text The contents of the message to send.
         */
-       virtual void Write(const std::string &text);
+       void WriteNotice(const std::string& text);
 
-       /** Write text to this user, appending CR/LF.
-        * Works on local users only.
-        * @param text The format string for text to send to the user
-        * @param ... POD-type format arguments
+       /** Send a NOTICE message from the local server to the user.
+        * @param text Text to send
         */
-       virtual void Write(const char *text, ...) CUSTOM_PRINTF(2, 3);
+       virtual void WriteRemoteNotice(const std::string& text);
 
-       /** Write text to this user, appending CR/LF and prepending :server.name
-        * Works on local users only.
-        * @param text A std::string to send to the user
-        */
-       void WriteServ(const std::string& text);
-
-       /** Write text to this user, appending CR/LF and prepending :server.name
-        * Works on local users only.
-        * @param text The format string for text to send to the user
-        * @param ... POD-type format arguments
-        */
-       void WriteServ(const char* text, ...) CUSTOM_PRINTF(2, 3);
+       virtual void WriteRemoteNumeric(const Numeric::Numeric& numeric);
 
-       void WriteNumeric(unsigned int numeric, const char* text, ...) CUSTOM_PRINTF(3, 4);
+       template <typename T1>
+       void WriteRemoteNumeric(unsigned int numeric, T1 p1)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               WriteRemoteNumeric(n);
+       }
 
-       void WriteNumeric(unsigned int numeric, const std::string &text);
+       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);
+       }
 
-       /** 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
-        * @param text A std::string to send to the user
-        */
-       void WriteFrom(User *user, const std::string &text);
+       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);
+       }
 
-       /** 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
-        * @param text The format string for text to send to the user
-        * @param ... POD-type format arguments
-        */
-       void WriteFrom(User *user, const char* text, ...) CUSTOM_PRINTF(3, 4);
+       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);
+       }
 
-       /** Write text to the user provided in the first parameter, appending CR/LF, and prepending THIS user's :nick!user\@host.
-        * @param dest The user to route the message to
-        * @param data A std::string to send to the user
-        */
-       void WriteTo(User *dest, const std::string &data);
+       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);
+       }
 
-       /** Write text to the user provided in the first parameter, appending CR/LF, and prepending THIS user's :nick!user\@host.
-        * @param dest The user to route the message to
-        * @param data The format string for text to send to the user
-        * @param ... POD-type format arguments
-        */
-       void WriteTo(User *dest, const char *data, ...) CUSTOM_PRINTF(3, 4);
+       void WriteNumeric(const Numeric::Numeric& numeric);
 
-       /** Write to all users that can see this user (including this user in the list if include_self is true), appending CR/LF
-        * @param line A std::string to send to the users
-        * @param include_self Should the message be sent back to the author?
-        */
-       void WriteCommonRaw(const std::string &line, bool include_self = true);
+       template <typename T1>
+       void WriteNumeric(unsigned int numeric, T1 p1)
+       {
+               Numeric::Numeric n(numeric);
+               n.push(p1);
+               WriteNumeric(n);
+       }
 
-       /** Write to all users that can see this user (including this user in the list), appending CR/LF
-        * @param text The format string for text to send to the users
-        * @param ... POD-type format arguments
-        */
-       void WriteCommon(const char* text, ...) CUSTOM_PRINTF(2, 3);
+       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);
+       }
 
-       /** Write to all users that can see this user (not including this user in the list), appending CR/LF
-        * @param text The format string for text to send to the users
-        * @param ... POD-type format arguments
-        */
-       void WriteCommonExcept(const char* text, ...) CUSTOM_PRINTF(2, 3);
+       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);
+       }
 
-       /** 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
-        */
-       void WriteCommonQuit(const std::string &normal_text, const std::string &oper_text);
+       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);
+       }
 
-       /** 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);
+       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 to the user, routing the line if the user is remote.
+       /** Write to all users that can see this user (including this user in the list if include_self is true), appending CR/LF
+        * @param protoev Protocol event to send, may contain any number of messages.
+        * @param include_self Should the message be sent back to the author?
         */
-       virtual void SendText(const std::string& line) = 0;
+       void WriteCommonRaw(ClientProtocol::Event& protoev, bool include_self = true);
 
-       /** Write to the user, routing the line if the user is remote.
+       /** 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 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
@@ -638,96 +633,66 @@ class CoreExport User : public Extensible
         */
        bool SharesChannelWith(User *other);
 
-       /** Send fake quit/join messages for host or ident cycle.
-        * Run this after the item in question has changed.
-        * You should not need to use this function, call ChangeDisplayedHost instead
-        *
-        * @param quitline The entire QUIT line, including the source using the old value
+       /** Change the displayed hostname of this user.
+        * @param host The new displayed hostname of this user.
+        * @return True if the hostname was changed successfully; otherwise, false.
         */
-       void DoHostCycle(const std::string &quitline);
+       bool ChangeDisplayedHost(const std::string& host);
 
-       /** Change the displayed host of a user.
-        * ALWAYS use this function, rather than writing User::dhost directly,
-        * as this triggers module events allowing the change to be syncronized to
-        * remote servers. This will also emulate a QUIT and rejoin (where configured)
-        * before setting their host field.
-        * @param host The new hostname to set
-        * @return True if the change succeeded, false if it didn't
+       /** Change the real hostname of this user.
+        * @param host The new real hostname of this user.
+        * @param resetdisplay Whether to reset the display host to this value.
         */
-       bool ChangeDisplayedHost(const char* host);
+       void ChangeRealHost(const std::string& host, bool resetdisplay);
 
        /** Change the ident (username) of a user.
         * ALWAYS use this function, rather than writing User::ident directly,
-        * as this correctly causes the user to seem to quit (where configured)
-        * before setting their ident field.
+        * as this triggers module events allowing the change to be syncronized to
+        * remote servers.
         * @param newident The new ident to set
         * @return True if the change succeeded, false if it didn't
         */
-       bool ChangeIdent(const char* newident);
+       bool ChangeIdent(const std::string& newident);
 
        /** Change a users realname field.
-        * ALWAYS use this function, rather than writing User::fullname directly,
-        * as this triggers module events allowing the change to be syncronized to
-        * remote servers.
-        * @param gecos The user's new realname
+        * @param real The user's new real name
         * @return True if the change succeeded, false if otherwise
         */
-       bool ChangeName(const char* gecos);
+       bool ChangeRealName(const std::string& real);
 
        /** 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.
+        * @param newts The time at which this nick change happened.
         * @return True if the change succeeded
         */
-       bool ChangeNick(const std::string& newnick, bool force = false);
-
-       /** Send a command to all local users from this user
-        * The command given must be able to send text with the
-        * first parameter as a servermask (e.g. $*), so basically
-        * you should use PRIVMSG or NOTICE.
-        * @param command the command to send
-        * @param text The text format string to send
-        * @param ... Format arguments
-        */
-       void SendAll(const char* command, const char* text, ...) CUSTOM_PRINTF(3, 4);
-
-       /** Compile a channel list for this user.  Used internally by WHOIS
-        * @param source The user to prepare the channel list for
-        * @param spy Whether to return the spy channel list rather than the normal one
-        * @return This user's channel list
-        */
-       std::string ChannelList(User* source, bool spy);
-
-       /** Split the channel list in cl which came from dest, and spool it to this user
-        * Used internally by WHOIS
-        * @param dest The user the original channel list came from
-        * @param cl The  channel list as a string obtained from User::ChannelList()
-        */
-       void SplitChanList(User* dest, const std::string &cl);
+       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!
         */
        void PurgeEmptyChannels();
 
-       /** Get the connect class which this user belongs to. NULL for remote users.
-        * @return A pointer to this user's connect class.
-        */
-       virtual ConnectClass* GetClass();
-
        /** Default destructor
         */
        virtual ~User();
-       virtual CullResult cull();
+       CullResult cull() CXX11_OVERRIDE;
 };
 
 class CoreExport UserIOHandler : public StreamSocket
 {
+ private:
+        size_t checked_until;
  public:
        LocalUser* const user;
-       UserIOHandler(LocalUser* me) : user(me) {}
-       void OnDataReady();
-       void OnError(BufferedSocketError error);
+       UserIOHandler(LocalUser* me)
+               : StreamSocket(StreamSocket::SS_USER)
+               , checked_until(0)
+               , user(me)
+       {
+       }
+       void OnDataReady() CXX11_OVERRIDE;
+       bool OnSetEndPoint(const irc::sockets::sockaddrs& local, const irc::sockets::sockaddrs& remote) CXX11_OVERRIDE;
+       void OnError(BufferedSocketError error) CXX11_OVERRIDE;
 
        /** Adds to the user's write buffer.
         * You may add any amount of text up to this users sendq value, if you exceed the
@@ -739,17 +704,33 @@ class CoreExport UserIOHandler : public StreamSocket
 
 typedef unsigned int already_sent_t;
 
-class CoreExport LocalUser : public User, public InviteBase
+class CoreExport LocalUser : public User, public insp::intrusive_list_node<LocalUser>
 {
+       /** Add a serialized message to the send queue of the user.
+        * @param serialized Bytes to add.
+        */
+       void Write(const ClientProtocol::SerializedMessage& serialized);
+
+       /** Send a protocol event to the user, consisting of one or more messages.
+        * @param protoev Event to send, may contain any number of messages.
+        * @param msglist Message list used temporarily internally to pass to hooks and store messages
+        * before Write().
+        */
+       void Send(ClientProtocol::Event& protoev, ClientProtocol::MessageList& msglist);
+
+       /** Message list, can be passed to the two parameter Send().
+        */
+       static ClientProtocol::MessageList sendmsglist;
+
  public:
        LocalUser(int fd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
-       CullResult cull();
+       CullResult cull() CXX11_OVERRIDE;
 
        UserIOHandler eh;
 
-       /** Position in UserManager::local_users
+       /** Serializer to use when communicating with the user
         */
-       LocalUserList::iterator localuseriter;
+       ClientProtocol::Serializer* serializer;
 
        /** Stats counter for bytes inbound
         */
@@ -777,45 +758,55 @@ class CoreExport LocalUser : public User, public InviteBase
         */
        reference<ConnectClass> MyClass;
 
-       ConnectClass* GetClass();
+       /** Get the connect class which this user belongs to.
+        * @return A pointer to this user's connect class.
+        */
+       ConnectClass* GetClass() const { return MyClass; }
 
        /** Call this method to find the matching \<connect> for a user, and to check them against it.
         */
-       void CheckClass();
+       void CheckClass(bool clone_count = true);
 
        /** Server address and port that this user is connected to.
         */
        irc::sockets::sockaddrs server_sa;
 
-       /**
-        * @return The port number of this user.
+       /** Recursion fix: user is out of SendQ and will be quit as soon as possible.
+        * This can't be handled normally because QuitUser itself calls Write on other
+        * users, which could trigger their SendQ to overrun.
         */
-       int GetServerPort();
+       unsigned int quitting_sendq:1;
 
-       /** Used by PING checking code
+       /** has the user responded to their previous ping?
         */
-       time_t nping;
+       unsigned int lastping:1;
+
+       /** This is true if the user matched an exception (E-line). It is used to save time on ban checks.
+        */
+       unsigned int exempt:1;
+
+       /** The time at which this user should be pinged next. */
+       time_t nextping;
+
+       /** Time that the connection last sent a message, used to calculate idle time
+        */
+       time_t idle_lastmsg;
 
        /** This value contains how far into the penalty threshold the user is.
         * This is used either to enable fake lag or for excess flood quits
         */
        unsigned int CommandFloodPenalty;
 
-       static already_sent_t already_sent_id;
        already_sent_t already_sent;
 
-       /** Stored reverse lookup from res_forward. Should not be used after resolution.
-        */
-       std::string stored_host;
-
-       /** Starts a DNS lookup of the user's IP.
-        * This will cause two UserResolver classes to be instantiated.
-        * When complete, these objects set User::dns_done to true.
+       /** Check if the user matches a G- or K-line, and disconnect them if they do.
+        * @param doZline True if Z-lines should be checked (if IP has changed since initial connect)
+        * Returns true if the user matched a ban, false else.
         */
-       void StartDNSLookup();
+       bool CheckLines(bool doZline = false);
 
        /** Use this method to fully connect a user.
-        * This will send the message of the day, check G/K/E lines, etc.
+        * This will send the message of the day, check G/K/E-lines, etc.
         */
        void FullConnect();
 
@@ -825,39 +816,15 @@ class CoreExport LocalUser : public User, public InviteBase
         */
        void SetClass(const std::string &explicit_name = "");
 
-       bool SetClientIP(const char* sip, bool recheck_eline = true);
-
-       void SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline = true);
+       bool SetClientIP(const std::string& address) CXX11_OVERRIDE;
 
-       void SendText(const std::string& line);
-       void Write(const std::string& text);
-       void Write(const char*, ...) CUSTOM_PRINTF(2, 3);
+       void SetClientIP(const irc::sockets::sockaddrs& sa) CXX11_OVERRIDE;
 
-       /** 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 channel A channel name to look up
-        * @return True if the user is invited to the given channel
-        */
-       bool IsInvited(const irc::string &channel);
-
-       /** Adds a channel to a users invite list (invites them to a channel)
-        * @param channel A channel name to add
-        * @param timeout When the invite should expire (0 == never)
-        */
-       void InviteTo(const irc::string &channel, time_t timeout);
-
-       /** 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 channel The channel to remove the invite to
-        */
-       void RemoveInvite(const irc::string &channel);
-
-       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
@@ -865,49 +832,68 @@ class CoreExport LocalUser : public User, public InviteBase
         * @param command A command (should be all CAPS)
         * @return True if this user can execute the command
         */
-       bool HasPermission(const std::string &command);
+       bool HasCommandPermission(const std::string& command) CXX11_OVERRIDE;
 
        /** Returns true if a user has a given permission.
         * This is used to check whether or not users may perform certain actions which admins may not wish to give to
         * all operators, yet are not commands. An example might be oper override, mass messaging (/notice $*), etc.
         *
         * @param privstr The priv to chec, e.g. "users/override/topic". These are loaded free-form from the config file.
-        * @param noisy If set to true, the user is notified that they do not have the specified permission where applicable. If false, no notification is sent.
         * @return True if this user has the permission in question.
         */
-       bool HasPrivPermission(const std::string &privstr, bool noisy = false);
+       bool HasPrivPermission(const std::string& privstr) CXX11_OVERRIDE;
 
        /** 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 CXX11_OVERRIDE;
+
+       /** 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();
+
+       /** Send a protocol event to the user, consisting of one or more messages.
+        * @param protoev Event to send, may contain any number of messages.
+        */
+       void Send(ClientProtocol::Event& protoev);
+
+       /** Send a single message to the user.
+        * @param protoevprov Protocol event provider.
+        * @param msg Message to send.
+        */
+       void Send(ClientProtocol::EventProvider& protoevprov, ClientProtocol::Message& msg);
 };
 
-class CoreExport RemoteUser : public User
+class RemoteUser : public User
 {
  public:
-       RemoteUser(const std::string& uid, const std::string& srv) : User(uid, srv, USERTYPE_REMOTE)
+       RemoteUser(const std::string& uid, Server* srv) : User(uid, srv, USERTYPE_REMOTE)
        {
        }
-       virtual void SendText(const std::string& line);
 };
 
 class CoreExport FakeUser : public User
 {
  public:
-       FakeUser(const std::string &uid, const std::string& srv) : User(uid, srv, USERTYPE_SERVER)
+       FakeUser(const std::string& uid, Server* srv) : User(uid, srv, USERTYPE_SERVER)
        {
-               nick = srv;
+               nick = srv->GetName();
        }
 
-       virtual CullResult cull();
-       virtual void SendText(const std::string& line);
-       virtual const std::string& GetFullHost();
-       virtual const std::string& GetFullRealHost();
+       FakeUser(const std::string& uid, const std::string& sname, const std::string& sdesc)
+               : User(uid, new Server(sname, sdesc), USERTYPE_SERVER)
+       {
+               nick = sname;
+       }
+
+       CullResult cull() CXX11_OVERRIDE;
+       const std::string& GetFullHost() CXX11_OVERRIDE;
+       const std::string& GetFullRealHost() CXX11_OVERRIDE;
 };
 
 /* Faster than dynamic_cast */
@@ -926,42 +912,21 @@ inline FakeUser* IS_SERVER(User* u)
 {
        return u->usertype == USERTYPE_SERVER ? static_cast<FakeUser*>(u) : NULL;
 }
-/** Is an oper */
-#define IS_OPER(x) (x->oper)
-/** Is away */
-#define IS_AWAY(x) (!x->awaymsg.empty())
 
-/** Derived from Resolver, and performs user forward/reverse lookups.
- */
-class CoreExport UserResolver : public Resolver
+inline bool User::IsModeSet(const ModeHandler* mh) const
 {
- private:
-       /** UUID we are looking up */
-       std::string uuid;
-       /** True if the lookup is forward, false if is a reverse lookup
-        */
-       bool fwd;
- public:
-       /** Create a resolver.
-        * @param user The user to begin lookup on
-        * @param to_resolve The IP or host to resolve
-        * @param qt The query type
-        * @param cache Modified by the constructor if the result was cached
-        */
-       UserResolver(LocalUser* user, std::string to_resolve, QueryType qt, bool &cache);
-
-       /** Called on successful lookup
-        * @param result Result string
-        * @param ttl Time to live for result
-        * @param cached True if the result was found in the cache
-        */
-       void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
+       return ((mh->GetId() != ModeParser::MODEID_MAX) && (modes[mh->GetId()]));
+}
 
-       /** Called on failed lookup
-        * @param e Error code
-        * @param errormessage Error message string
-        */
-       void OnError(ResolverError e, const std::string &errormessage);
-};
+inline bool User::IsModeSet(UserModeReference& moderef) const
+{
+       if (!moderef)
+               return false;
+       return IsModeSet(*moderef);
+}
 
-#endif
+inline void User::SetMode(ModeHandler* mh, bool value)
+{
+       if (mh && mh->GetId() != ModeParser::MODEID_MAX)
+               modes[mh->GetId()] = value;
+}
index 2a49d8b80efe1c49230137a847a4838a049485be..bc9739f212ccf1a872109a6c71e922d4d7a34bee 100644 (file)
  */
 
 
-#ifndef XLINE_H
-#define XLINE_H
+#pragma once
 
-/** XLine is the base class for ban lines such as G lines and K lines.
+/** XLine is the base class for ban lines such as G-lines and K-lines.
  * Modules may derive from this, and their xlines will automatically be
  * handled as expected by any protocol modules (e.g. m_spanningtree will
  * propogate them using AddLine). The process of translating a type+pattern
@@ -50,8 +49,13 @@ class CoreExport XLine : public classbase
         * @param re The reason of the xline
         * @param t The line type, should be set by the derived class constructor
         */
-       XLine(time_t s_time, long d, std::string src, std::string re, const std::string &t)
-               : set_time(s_time), duration(d), source(src), reason(re), type(t)
+       XLine(time_t s_time, unsigned long d, const std::string& src, const std::string& re, const std::string& t)
+               : set_time(s_time)
+               , duration(d)
+               , source(src)
+               , reason(re)
+               , type(t)
+               , from_config(false)
        {
                expiry = set_time + duration;
        }
@@ -63,7 +67,7 @@ class CoreExport XLine : public classbase
        }
 
        /** Change creation time of an xline. Updates expiry
-        * to be after the creation time
+        * to be after the creation time.
         */
        virtual void SetCreateTime(time_t created)
        {
@@ -98,19 +102,19 @@ class CoreExport XLine : public classbase
        virtual void Unset() { }
 
        /** Called when the expiry message is to be displayed for the
-        * line. Usually a line in the form 'expiring Xline blah, set by...'
+        * line. Usually a line in the form 'expiring X-line blah, set by...'
         * see the DisplayExpiry methods of GLine, ELine etc.
         */
-       virtual void DisplayExpiry() = 0;
+       virtual void DisplayExpiry();
 
        /** Returns the displayable form of the pattern for this xline,
         * e.g. '*\@foo' or '*baz*'. This must always return the full pattern
         * in a form which can be used to construct an entire derived xline,
         * even if it is stored differently internally (e.g. GLine stores the
-        * ident and host parts seperately but will still return ident\@host
-        * for its Displayable() method)
+        * ident and host parts separately but will still return ident\@host
+        * for its Displayable() method).
         */
-       virtual const char* Displayable() = 0;
+       virtual const std::string& Displayable() = 0;
 
        /** Called when the xline has just been added.
         */
@@ -122,7 +126,7 @@ class CoreExport XLine : public classbase
 
        /** The duration of the ban, or 0 if permenant
         */
-       long duration;
+       unsigned long duration;
 
        /** Source of the ban. This can be a servername or an oper nickname
         */
@@ -141,6 +145,9 @@ class CoreExport XLine : public classbase
         */
        const std::string type;
 
+       // Whether this XLine was loaded from the server config.
+       bool from_config;
+
        virtual bool IsBurstable();
 };
 
@@ -150,7 +157,7 @@ class CoreExport KLine : public XLine
 {
   public:
 
-       /** Create a K-Line.
+       /** Create a K-line.
         * @param s_time The set time
         * @param d The duration of the xline
         * @param src The sender of the xline
@@ -158,7 +165,7 @@ class CoreExport KLine : public XLine
         * @param ident Ident to match
         * @param host Host to match
         */
-       KLine(time_t s_time, long d, std::string src, std::string re, std::string ident, std::string host)
+       KLine(time_t s_time, unsigned long d, const std::string& src, const std::string& re, const std::string& ident, const std::string& host)
                : XLine(s_time, d, src, re, "K"), identmask(ident), hostmask(host)
        {
                matchtext = this->identmask;
@@ -171,17 +178,15 @@ class CoreExport KLine : public XLine
        {
        }
 
-       virtual bool Matches(User *u);
+       bool Matches(User* u) CXX11_OVERRIDE;
 
-       virtual bool Matches(const std::string &str);
+       bool Matches(const std::string& str) CXX11_OVERRIDE;
 
-       virtual void Apply(User* u);
-
-       virtual void DisplayExpiry();
+       void Apply(User* u) CXX11_OVERRIDE;
 
-       virtual const char* Displayable();
+       const std::string& Displayable() CXX11_OVERRIDE;
 
-       virtual bool IsBurstable();
+       bool IsBurstable() CXX11_OVERRIDE;
 
        /** Ident mask (ident part only)
         */
@@ -198,7 +203,7 @@ class CoreExport KLine : public XLine
 class CoreExport GLine : public XLine
 {
   public:
-       /** Create a G-Line.
+       /** Create a G-line.
         * @param s_time The set time
         * @param d The duration of the xline
         * @param src The sender of the xline
@@ -206,7 +211,7 @@ class CoreExport GLine : public XLine
         * @param ident Ident to match
         * @param host Host to match
         */
-       GLine(time_t s_time, long d, std::string src, std::string re, std::string ident, std::string host)
+       GLine(time_t s_time, unsigned long d, const std::string& src, const std::string& re, const std::string& ident, const std::string& host)
                : XLine(s_time, d, src, re, "G"), identmask(ident), hostmask(host)
        {
                matchtext = this->identmask;
@@ -219,15 +224,13 @@ class CoreExport GLine : public XLine
        {
        }
 
-       virtual bool Matches(User *u);
+       bool Matches(User* u) CXX11_OVERRIDE;
 
-       virtual bool Matches(const std::string &str);
+       bool Matches(const std::string& str) CXX11_OVERRIDE;
 
-       virtual void Apply(User* u);
+       void Apply(User* u)  CXX11_OVERRIDE;
 
-       virtual void DisplayExpiry();
-
-       virtual const char* Displayable();
+       const std::string& Displayable() CXX11_OVERRIDE;
 
        /** Ident mask (ident part only)
         */
@@ -244,7 +247,7 @@ class CoreExport GLine : public XLine
 class CoreExport ELine : public XLine
 {
   public:
-       /** Create an E-Line.
+       /** Create an E-line.
         * @param s_time The set time
         * @param d The duration of the xline
         * @param src The sender of the xline
@@ -252,7 +255,7 @@ class CoreExport ELine : public XLine
         * @param ident Ident to match
         * @param host Host to match
         */
-       ELine(time_t s_time, long d, std::string src, std::string re, std::string ident, std::string host)
+       ELine(time_t s_time, unsigned long d, const std::string& src, const std::string& re, const std::string& ident, const std::string& host)
                : XLine(s_time, d, src, re, "E"), identmask(ident), hostmask(host)
        {
                matchtext = this->identmask;
@@ -263,17 +266,15 @@ class CoreExport ELine : public XLine
        {
        }
 
-       virtual bool Matches(User *u);
+       bool Matches(User* u) CXX11_OVERRIDE;
 
-       virtual bool Matches(const std::string &str);
+       bool Matches(const std::string& str) CXX11_OVERRIDE;
 
-       virtual void Unset();
+       void Unset() CXX11_OVERRIDE;
 
-       virtual void DisplayExpiry();
+       void OnAdd() CXX11_OVERRIDE;
 
-       virtual void OnAdd();
-
-       virtual const char* Displayable();
+       const std::string& Displayable() CXX11_OVERRIDE;
 
        /** Ident mask (ident part only)
         */
@@ -290,14 +291,14 @@ class CoreExport ELine : public XLine
 class CoreExport ZLine : public XLine
 {
   public:
-       /** Create a Z-Line.
+       /** Create a Z-line.
         * @param s_time The set time
         * @param d The duration of the xline
         * @param src The sender of the xline
         * @param re The reason of the xline
         * @param ip IP to match
         */
-       ZLine(time_t s_time, long d, std::string src, std::string re, std::string ip)
+       ZLine(time_t s_time, unsigned long d, const std::string& src, const std::string& re, const std::string& ip)
                : XLine(s_time, d, src, re, "Z"), ipaddr(ip)
        {
        }
@@ -308,15 +309,13 @@ class CoreExport ZLine : public XLine
        {
        }
 
-       virtual bool Matches(User *u);
+       bool Matches(User* u) CXX11_OVERRIDE;
 
-       virtual bool Matches(const std::string &str);
+       bool Matches(const std::string& str) CXX11_OVERRIDE;
 
-       virtual void Apply(User* u);
+       void Apply(User* u) CXX11_OVERRIDE;
 
-       virtual void DisplayExpiry();
-
-       virtual const char* Displayable();
+       const std::string& Displayable() CXX11_OVERRIDE;
 
        /** IP mask (no ident part)
         */
@@ -328,14 +327,14 @@ class CoreExport ZLine : public XLine
 class CoreExport QLine : public XLine
 {
   public:
-       /** Create a G-Line.
+       /** Create a Q-line.
         * @param s_time The set time
         * @param d The duration of the xline
         * @param src The sender of the xline
         * @param re The reason of the xline
         * @param nickname Nickname to match
         */
-       QLine(time_t s_time, long d, std::string src, std::string re, std::string nickname)
+       QLine(time_t s_time, unsigned long d, const std::string& src, const std::string& re, const std::string& nickname)
                : XLine(s_time, d, src, re, "Q"), nick(nickname)
        {
        }
@@ -345,15 +344,13 @@ class CoreExport QLine : public XLine
        ~QLine()
        {
        }
-       virtual bool Matches(User *u);
+       bool Matches(User* u) CXX11_OVERRIDE;
 
-       virtual bool Matches(const std::string &str);
+       bool Matches(const std::string& str) CXX11_OVERRIDE;
 
-       virtual void Apply(User* u);
+       void Apply(User* u) CXX11_OVERRIDE;
 
-       virtual void DisplayExpiry();
-
-       virtual const char* Displayable();
+       const std::string& Displayable() CXX11_OVERRIDE;
 
        /** Nickname mask
         */
@@ -393,7 +390,7 @@ class CoreExport XLineFactory
         * @param xline_specific_mask The mask string for the line, specific to the XLine type being created.
         * @return A specialized XLine class of the given type for this factory.
         */
-       virtual XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask) = 0;
+       virtual XLine* Generate(time_t set_time, unsigned long duration, const std::string& source, const std::string& reason, const std::string& xline_specific_mask) = 0;
 
        virtual bool AutoApplyToUserList(XLine* x) { return true; }
 
@@ -402,7 +399,7 @@ class CoreExport XLineFactory
        virtual ~XLineFactory() { }
 };
 
-/** XLineManager is a class used to manage glines, klines, elines, zlines and qlines,
+/** XLineManager is a class used to manage G-lines, K-lines, E-lines, Z-lines and Q-lines,
  * or any other line created by a module. It also manages XLineFactory classes which
  * can generate a specialized XLine for use by another module.
  */
@@ -439,7 +436,7 @@ class CoreExport XLineManager
         */
        IdentHostPair IdentSplit(const std::string &ident_and_host);
 
-       /** Checks what users match e:lines and sets their ban exempt flag accordingly.
+       /** Checks what users match E-lines and sets their ban exempt flag accordingly.
         */
        void CheckELines();
 
@@ -470,16 +467,17 @@ class CoreExport XLineManager
        /** Delete an XLine
         * @param hostmask The xline-specific string identifying the line, e.g. "*@foo"
         * @param type The type of xline
+        * @param reason The xline reason, if it is being removed successfully
         * @param user The user removing the line or NULL if its the local server
         * @param simulate If this is true, don't actually remove the line, just return
         * @return True if the line was deleted successfully
         */
-       bool DelLine(const char* hostmask, const std::string &type, User* user, bool simulate = false);
+       bool DelLine(const char* hostmask, const std::string& type, std::string& reason, User* user, bool simulate = false);
 
        /** Registers an xline factory.
         * An xline factory is a class which when given a particular xline type,
         * will generate a new XLine specialized to that type. For example if you
-        * pass the XLineFactory that handles glines some data it will return a
+        * pass the XLineFactory that handles G-lines some data it will return a
         * pointer to a GLine, polymorphically represented as XLine. This is used where
         * you do not know the full details of the item you wish to create, e.g. in a
         * server protocol module like m_spanningtree, when you receive xlines from other
@@ -517,8 +515,9 @@ class CoreExport XLineManager
        /** Expire a line given two iterators which identify it in the main map.
         * @param container Iterator to the first level of entries the map
         * @param item Iterator to the second level of entries in the map
+        * @param silent If true, doesn't send an expiry SNOTICE.
         */
-       void ExpireLine(ContainerIter container, LookupIter item);
+       void ExpireLine(ContainerIter container, LookupIter item, bool silent = false);
 
        /** Apply any new lines that are pending to be applied.
         * This will only apply lines in the pending_lines list, to save on
@@ -531,10 +530,10 @@ 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);
 
-#endif
+       /** Expire X-lines which were added by the server configuration and have been removed. */
+       void ExpireRemovedConfigLines(const std::string& type, const insp::flat_set<std::string>& configlines);
+};
index 069e664b590dfa9fbb891ded7c0a4d9cff2b8e07..5e366d606b3b4723821632b909b96a811ed5f169 100755 (executable)
@@ -1,6 +1,6 @@
 Here you can find locales configuration files.
 
-(!) The idea and several locale files are derived from Bynets UnrealIRCd distribution (See http://www.bynets.org/)
+(!) The idea and several locale files are derived from Bynets UnrealIRCd distribution (See https://bynets.org)
 
 *** File structure ***
 
index 49506dd3be8fe35c05251bad55f477c582880e9b..279fdffcec587c9a8b9d20d27f07a680ceee87fe 100755 (executable)
 #
 
 
+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 File::Basename qw(basename);
+
+use constant {
+       BUILDPATH  => $ENV{BUILDPATH},
+       SOURCEPATH => $ENV{SOURCEPATH}
+};
 
 sub find_output;
 sub gendep($);
 sub dep_cpp($$$);
 sub dep_so($);
-sub dep_dir($);
+sub dep_dir($$);
 sub run();
 
 my %f2dep;
@@ -36,30 +50,20 @@ 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 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}) {
-               run_static();
-       } else {
-               run_dynamic();
-       }
+       run_dynamic();
        close MAKE;
 }
 
 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,113 +75,68 @@ bad-target:
        \@echo "in order to set the correct environment variables"
        \@exit 1
 
-all: inspircd commands modules
+all: inspircd modules
 
 END
-       my(@core_deps, @cmdlist, @modlist);
-       for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, "threadengines/threadengine_pthread.cpp") {
+       my(@core_deps, @modlist);
+       for my $file (<*.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/$ENV{SOCKETENGINE}.cpp";
+               next if $file =~ m#^socketengines/# && $file ne "socketengines/socketengine_$ENV{SOCKETENGINE}.cpp";
+               # 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;
        }
 
-       for my $file (<commands/*.cpp>) {
-               my $out = dep_so $file;
-               push @cmdlist, $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") {
-                       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 $cmds = join ' ', @cmdlist;
        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
 
-commands: $cmds
-
 modules: $mods
 
-.PHONY: all bad-target inspircd commands modules
-
-END
-}
-
-sub run_static() {
-       print MAKE <<END;
-# DO NOT EDIT THIS FILE
-# It is autogenerated by make/calcdep.pl, and will be overwritten
-# every time you rerun make in the main directory
-VPATH = \$(SOURCEPATH)/src
-
-bad-target:
-       \@echo "This Makefile must be run by a sub-make from the source"
-       \@echo "in order to set the correct environment variables"
-       \@exit 1
-
-all: inspircd
-
-END
-       my(@deps, @srcs);
-       for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, <commands/*.cpp>,
-                       <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";
-               }
-               dep_cpp $file, $out, 'gen-o';
-               next if $file =~ m#^socketengines/# && $file ne "socketengines/$ENV{SOCKETENGINE}.cpp";
-               push @deps, $out;
-               push @srcs, $file;
-       }
-
-       my $core_mk = join ' ', @deps;
-       my $core_src = join ' ', @srcs;
-       print MAKE <<END;
-
-obj/ld-extra.cmd: $core_src
-       \@\$(SOURCEPATH)/make/unit-cc.pl gen-ld\$(VERBOSE) \$\@ \$^ \$>
-
-bin/inspircd: $core_mk obj/ld-extra.cmd
-       \@\$(SOURCEPATH)/make/unit-cc.pl static-ld\$(VERBOSE) \$\@ \$^ \$>
-
-inspircd: bin/inspircd
-
-.PHONY: all bad-target inspircd
+.PHONY: all bad-target inspircd modules
 
 END
 }
 
 sub find_output {
-       my($file, $static) = @_;
+       my $file = shift;
        my($path,$base) = $file =~ m#^((?:.*/)?)([^/]+)\.cpp# or die "Bad file $file";
-       if ($path eq 'modules/' || $path eq 'commands/') {
-               return $static ? "obj/$base.o" : "modules/$base.so";
+       if ($path eq 'modules/' || $path eq 'coremods/') {
+               return "modules/$base.so";
        } elsif ($path eq '' || $path eq 'modes/' || $path =~ /^[a-z]+engines\/$/) {
                return "obj/$base.o";
-       } elsif ($path =~ m#modules/(m_.*)/#) {
+       } elsif ($path =~ m#modules/(m_.*)/# || $path =~ m#coremods/(core_.*)/#) {
                return "obj/$1/$base.o";
        } else {
                die "Can't determine output for $file";
@@ -199,7 +158,7 @@ sub gendep($) {
        while (<$in>) {
                if (/^\s*#\s*include\s*"([^"]+)"/) {
                        my $inc = $1;
-                       next if $inc eq 'inspircd_version.h' && $f eq '../include/inspircd.h';
+                       next if $inc eq 'config.h' && $f eq '../include/inspircd.h';
                        my $found = 0;
                        for my $loc ("$basedir/$inc", "../include/$inc") {
                                next unless -e $loc;
@@ -225,26 +184,23 @@ 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($) {
        my($file) = @_;
        my $out = find_output $file;
-       my $split = find_output $file, 1;
 
-       if ($ENV{SPLIT_CC}) {
-               dep_cpp $file, $split, 'gen-o';
-               print MAKE "$out: $split\n";
-               print MAKE "\t@\$(SOURCEPATH)/make/unit-cc.pl link-so\$(VERBOSE) \$\@ \$(SOURCEPATH)/src/$file \$>\n";
-       } else {
-               dep_cpp $file, $out, 'gen-so';
-       }
+       my $name = basename $out, '.so';
+       print MAKE ".PHONY: $name\n";
+       print MAKE "$name: $out\n";
+
+       dep_cpp $file, $out, 'gen-so';
        return $out;
 }
 
-sub dep_dir($) {
-       my($dir) = @_;
+sub dep_dir($$) {
+       my($dir, $outdir) = @_;
        my @ofiles;
        opendir DIR, $dir;
        for my $file (sort readdir DIR) {
@@ -256,8 +212,11 @@ sub dep_dir($) {
        closedir DIR;
        if (@ofiles) {
                my $ofiles = join ' ', @ofiles;
-               print MAKE "$dir.so: $ofiles\n";
-               print MAKE "\t@\$(SOURCEPATH)/make/unit-cc.pl link-dir\$(VERBOSE) \$\@ \$^ \$>\n";
+               my $name = basename $outdir;
+               print MAKE ".PHONY: $name\n";
+               print MAKE "$name: $outdir.so\n";
+               print MAKE "$outdir.so: $ofiles\n";
+               print MAKE "\t@\$(SOURCEPATH)/make/unit-cc.pl link-dir \$\@ ${\SOURCEPATH}/src/$dir \$^ \$>\n";
                return 1;
        } else {
                return 0;
diff --git a/make/check_epoll.cpp b/make/check_epoll.cpp
deleted file mode 100644 (file)
index a5ed1c1..0000000
+++ /dev/null
@@ -1,25 +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 <sys/epoll.h>
-
-int main() {
-       int fd = epoll_create(1);
-       return (fd < 0);
-}
diff --git a/make/check_eventfd.cpp b/make/check_eventfd.cpp
deleted file mode 100644 (file)
index 9b38b79..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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
- * 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 <sys/eventfd.h>
-
-int main() {
-       eventfd_t efd_data;
-       int fd;
-
-       fd = eventfd(0, EFD_NONBLOCK);
-       eventfd_read(fd, &efd_data);
-
-       return (fd < 0);
-}
diff --git a/make/check_kqueue.cpp b/make/check_kqueue.cpp
deleted file mode 100644 (file)
index 6034253..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 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 <sys/types.h>
-#include <sys/event.h>
-
-int main() {
-       int fd = kqueue();
-       return (fd < 0);
-}
diff --git a/make/check_stdint.cpp b/make/check_stdint.cpp
deleted file mode 100644 (file)
index fbd01b8..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 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 <stdint.h>
-
-int main() {
-       uint32_t ret = 0;
-       return ret;
-}
diff --git a/make/check_strlcpy.cpp b/make/check_strlcpy.cpp
deleted file mode 100644 (file)
index e51d18d..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 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 <string.h>
-
-int main() {
-       char test[5];
-       strlcpy(test, "test", sizeof(test));
-}
diff --git a/make/common.pm b/make/common.pm
new file mode 100644 (file)
index 0000000..ba6b03f
--- /dev/null
@@ -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]+)(?:[a-z]+\d+)?(?:-\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;
index 9b8e2d0e4b411f8c4174686d35565f5120168f94..877e4ce6cdde40a7132ab25a7ceb9d63188683df 100644 (file)
@@ -1,7 +1,7 @@
 #
 # InspIRCd -- Internet Relay Chat Daemon
 #
-#   Copyright (C) 2012 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>
 #
 
 
-package make::configure;
+BEGIN {
+       require 5.10.0;
+}
 
-require 5.8.0;
+package make::configure;
 
+use feature ':5.10';
 use strict;
 use warnings FATAL => qw(all);
 
-use Exporter 'import';
-use POSIX;
-use make::utilities;
-our @EXPORT = qw(promptnumeric dumphash is_dir getmodules getrevision getcompilerflags getlinkerflags getdependencies nopedantic resolve_directory yesno showhelp promptstring_s module_installed);
-
-my $no_git = 0;
-
-sub yesno {
-       my ($flag,$prompt) = @_;
-       print "$prompt [\e[1;32m$main::config{$flag}\e[0m] -> ";
-       chomp(my $tmp = <STDIN>);
-       if ($tmp eq "") { $tmp = $main::config{$flag} }
-       if (($tmp eq "") || ($tmp =~ /^y/i))
-       {
-               $main::config{$flag} = "y";
+use Cwd                   qw(getcwd);
+use Exporter              qw(import);
+use File::Basename        qw(basename dirname);
+use File::Spec::Functions qw(catdir catfile);
+
+use make::common;
+use make::console;
+
+use constant CONFIGURE_ROOT          => dirname dirname __FILE__;
+use constant CONFIGURE_DIRECTORY     => catdir(CONFIGURE_ROOT, '.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/;
+               push @socketengines, $1;
        }
-       else
-       {
-               $main::config{$flag} = "n";
-       }
-       return;
+       return @socketengines;
 }
 
-sub resolve_directory
-{
-       my $ret = $_[0];
-       eval
-       {
-               use File::Spec;
-               $ret = File::Spec->rel2abs($_[0]);
-       };
-       return $ret;
-}
+# TODO: when buildtool is done this can be mostly removed with
+#       the remainder being merged into parse_templates.
+sub __get_template_settings($$$) {
+
+       # These are actually hash references
+       my ($config, $compiler, $version) = @_;
 
-sub getrevision {
-       if ($no_git)
-       {
-               return "0";
+       # Start off by populating with the config
+       my %settings = %$config;
+
+       # Compiler information
+       while (my ($key, $value) = each %{$compiler}) {
+               $settings{'COMPILER_' . $key} = $value;
        }
-       my $data = `git describe --tags 2>/dev/null`;
-       if ($data eq "")
-       {
-               $no_git = 1;
-               return '0';
+
+       # Version information
+       while (my ($key, $value) = each %{$version}) {
+               $settings{'VERSION_' . $key} = $value;
        }
-       chomp $data; # remove \n
-       return $data;
+
+       # Miscellaneous information
+       $settings{CONFIGURE_DIRECTORY} = CONFIGURE_DIRECTORY;
+       $settings{CONFIGURE_CACHE_FILE} = CONFIGURE_CACHE_FILE;
+       $settings{SYSTEM_NAME} = lc $^O;
+
+       return %settings;
 }
 
-sub getcompilerflags {
-       my ($file) = @_;
-       open(FLAGS, $file) or return "";
-       while (<FLAGS>) {
-               if ($_ =~ /^\/\* \$CompileFlags: (.+) \*\/\r?$/) {
-                       my $x = translate_functions($1, $file);
-                       next if ($x eq "");
-                       close(FLAGS);
-                       return $x;
-               }
-       }
-       close(FLAGS);
-       return "";
+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 getlinkerflags {
-       my ($file) = @_;
-       open(FLAGS, $file) or return "";
-       while (<FLAGS>) {
-               if ($_ =~ /^\/\* \$LinkerFlags: (.+) \*\/\r?$/) {
-                       my $x = translate_functions($1, $file);
-                       next if ($x eq "");
-                       close(FLAGS);
-                       return $x;
-               }
-       }
-       close(FLAGS);
-       return "";
+sub cmd_clean {
+       unlink CONFIGURE_CACHE_FILE;
 }
 
-sub getdependencies {
-       my ($file) = @_;
-       open(FLAGS, $file) or return "";
-       while (<FLAGS>) {
-               if ($_ =~ /^\/\* \$ModDep: (.+) \*\/\r?$/) {
-                       my $x = translate_functions($1, $file);
-                       next if ($x eq "");
-                       close(FLAGS);
-                       return $x;
-               }
-       }
-       close(FLAGS);
-       return "";
+sub cmd_help {
+       my $PWD = getcwd();
+       my $SELIST = join ', ', __get_socketengines();
+       print <<EOH;
+Usage: $0 [options]
+
+When no options are specified, configure runs in interactive mode and you must
+specify any required values manually. If one or more options are specified,
+non-interactive configuration is started and any omitted values are defaulted.
+
+PATH OPTIONS
+
+  --system                      Automatically set up the installation paths
+                                for system-wide installation.
+  --prefix=[dir]                The root install directory. If this is set then
+                                all subdirectories will be adjusted accordingly.
+                                [$PWD/run]
+  --binary-dir=[dir]            The location where the main server binary is
+                                stored.
+                                [$PWD/run/bin]
+  --config-dir=[dir]            The location where the configuration files and
+                                SSL certificates are stored.
+                                [$PWD/run/conf]
+  --data-dir=[dir]              The location where the data files, such as the
+                                pid file, are stored.
+                                [$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]
+  --script-dir=[dir]            The location where the scripts, such as the
+                                init scripts, are stored.
+                                [$PWD/run]
+
+EXTRA MODULE OPTIONS
+
+  --enable-extras=[extras]      Enables a comma separated list of extra modules.
+  --disable-extras=[extras]     Disables a comma separated list of extra modules.
+  --list-extras                 Shows the availability status of all extra
+                                modules.
+
+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.
+  --socketengine=[name]         Sets the socket engine to be used. Possible
+                                values are $SELIST.
+  --uid=[id|name]               Sets the user to run InspIRCd as.
+  --update                      Updates the build environment with the settings
+                                from the cache.
+
+
+FLAGS
+
+  CXX=[name]                    Sets the C++ compiler to use when building the
+                                server. If not specified then the build system
+                                will search for c++, g++, clang++ or icpc.
+
+If you have any problems with configuring InspIRCd then visit our IRC channel
+at irc.inspircd.org #InspIRCd for support.
+
+EOH
+       exit 0;
 }
 
-sub nopedantic {
-       my ($file) = @_;
-       open(FLAGS, $file) or return "";
-       while (<FLAGS>) {
-               if ($_ =~ /^\/\* \$NoPedantic \*\/\r?$/) {
-                       my $x = translate_functions($_, $file);
-                       next if ($x eq "");
-                       close(FLAGS);
-                       return 1;
-               }
-       }
-       close(FLAGS);
-       return 0;
+sub cmd_update {
+       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});
+       my %version = get_version $config{DISTRIBUTION};
+       parse_templates(\%config, \%compiler, \%version);
+       say 'Update complete!';
+       exit 0;
 }
 
-sub getmodules
-{
-       my ($silent) = @_;
+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;
+}
 
-       my $i = 0;
+sub test_file($$;$) {
+       my ($compiler, $file, $args) = @_;
+       my $status = 0;
+       $args //= '';
+       $status ||= system "$compiler -o __test_$file ${\CONFIGURE_ROOT}/make/test/$file $args ${\CONFIGURE_ERROR_PIPE}";
+       $status ||= system "./__test_$file ${\CONFIGURE_ERROR_PIPE}";
+       unlink "./__test_$file";
+       return !$status;
+}
 
-       if (!$silent)
-       {
-               print "Detecting modules ";
-       }
+sub test_header($$;$) {
+       my ($compiler, $header, $args) = @_;
+       $args //= '';
+       open(my $fh, "| $compiler -E - $args ${\CONFIGURE_ERROR_PIPE}") or return 0;
+       print $fh "#include <$header>";
+       close $fh;
+       return !$?;
+}
 
-       opendir(DIRHANDLE, "src/modules") or die("WTF, missing src/modules!");
-       foreach my $name (sort readdir(DIRHANDLE))
-       {
-               if ($name =~ /^m_(.+)\.cpp$/)
-               {
-                       my $mod = $1;
-                       $main::modlist[$i++] = $mod;
-                       if (!$silent)
-                       {
-                               print ".";
-                       }
-               }
+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}: $!";
        }
-       closedir(DIRHANDLE);
 
-       if (!$silent)
-       {
-               print "\nOk, $i modules.\n";
-       }
+       print_format "Writing <|GREEN ${\CONFIGURE_CACHE_FILE}|> ...\n";
+       my %config = @_;
+       write_config_file CONFIGURE_CACHE_FILE, %config;
 }
 
-sub promptnumeric($$)
-{
-       my $continue = 0;
-       my ($prompt, $configitem) = @_;
-       while (!$continue)
-       {
-               print "Please enter the maximum $prompt?\n";
-               print "[\e[1;32m$main::config{$configitem}\e[0m] -> ";
-               chomp(my $var = <STDIN>);
-               if ($var eq "")
-               {
-                       $var = $main::config{$configitem};
-               }
-               if ($var =~ /^\d+$/) {
-                       # We don't care what the number is, set it and be on our way.
-                       $main::config{$configitem} = $var;
-                       $continue = 1;
-                       print "\n";
-               } else {
-                       print "You must enter a number in this field. Please try again.\n\n";
-               }
+sub get_compiler_info($) {
+       my $binary = shift;
+       my %info = (NAME => 'Unknown', VERSION => '0.0');
+       return %info if system "$binary -o __compiler_info ${\CONFIGURE_ROOT}/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 module_installed($)
-{
-       my $module = shift;
-       eval("use $module;");
-       return !$@;
+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 promptstring_s($$)
-{
-       my ($prompt,$default) = @_;
-       my $var;
-       print "$prompt\n";
-       print "[\e[1;32m$default\e[0m] -> ";
-       chomp($var = <STDIN>);
-       $var = $default if $var eq "";
-       print "\n";
-       return $var;
-}
+sub parse_templates($$$) {
+
+       # These are actually hash references
+       my ($config, $compiler, $version) = @_;
+
+       # Collect settings to be used when generating files
+       my %settings = __get_template_settings($config, $compiler, $version);
+
+       # Iterate through files in make/template.
+       foreach (<make/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 = <$fh>) {
+                       chomp $line;
+
+                       # Does this line match a variable?
+                       while ($line =~ /(@(\w+?)@)/) {
+                               my ($variable, $name) = ($1, $2);
+                               if (defined $settings{$name}) {
+                                       $line =~ s/\Q$variable\E/$settings{$name}/;
+                               } else {
+                                       print_warning "unknown template variable '$name' in $_!";
+                                       last;
+                               }
+                       }
 
-sub dumphash()
-{
-       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[0mModule path:\e[1;32m\t\t\t$main::config{MODULE_DIR}\e[0m\n";
-       print "\e[0mGCC Version Found:\e[1;32m\t\t$main::config{GCCVER}.$main::config{GCCMINOR}\e[0m\n";
-       print "\e[0mCompiler program:\e[1;32m\t\t$main::config{CC}\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\n";
-       print "\e[1;32mImportant note: The maximum length values are now configured in the\e[0m\n";
-       print "\e[1;32m                configuration file, not in ./configure! See the <limits>\e[0m\n";
-       print "\e[1;32m                tag in the configuration file for more information.\e[0m\n\n";
-}
+                       # Does this line match a directive?
+                       if ($line =~ /^\s*%(\w+)\s+(.+)$/) {
+                               if ($1 eq 'define') {
+                                       if ($settings{$2}) {
+                                               push @lines, "#define $2";
+                                       } else {
+                                               push @lines, "#undef $2";
+                                       }
+                               } elsif ($1 eq 'mode') {
+                                       $mode = oct $2;
+                               } elsif ($1 eq 'platform') {
+                                       push @platforms, $2;
+                               } elsif ($1 eq 'target') {
+                                       push @targets, $2
+                               } else {
+                                       print_warning "unknown template command '$1' in $_!";
+                                       push @lines, $line;
+                               }
+                               next;
+                       }
+                       push @lines, $line;
+               }
+               close $fh;
 
-sub is_dir
-{
-       my ($path) = @_;
-       if (chdir($path))
-       {
-               chdir($main::this);
-               return 1;
-       }
-       else
-       {
-               # Just in case..
-               chdir($main::this);
-               return 0;
-       }
-}
+               # Only proceed if this file should be templated on this platform.
+               if ($#platforms < 0 || grep { $_ eq $^O } @platforms) {
 
-sub showhelp
-{
-       chomp(my $PWD = `pwd`);
-       print <<EOH;
-Usage: configure [options]
-
-*** NOTE: NON-INTERACTIVE CONFIGURE IS *NOT* SUPPORTED BY THE ***
-*** INSPIRCD DEVELOPMENT TEAM. DO NOT ASK FOR HELP REGARDING  ***
-***     NON-INTERACTIVE CONFIGURE ON THE FORUMS OR ON IRC!    ***
-
-Options: [defaults in brackets after descriptions]
-
-When no options are specified, interactive
-configuration is started and you must specify
-any required values manually. If one or more
-options are specified, non-interactive configuration
-is started, and any omitted values are defaulted.
-
-Arguments with a single \"-\" symbol, as in
-InspIRCd 1.0.x, are also allowed.
-
-  --disable-interactive        Sets no options itself, but
-                               will disable any interactive prompting.
-  --update                     Update makefiles and dependencies
-  --clean                      Remove .config.cache file and go interactive
-  --enable-gnutls              Enable GnuTLS module [no]
-  --enable-openssl             Enable OpenSSL module [no]
-  --enable-epoll               Enable epoll() where supported [set]
-  --enable-kqueue              Enable kqueue() where supported [set]
-  --disable-epoll              Do not enable epoll(), fall back
-                               to select() [not set]
-  --disable-kqueue             Do not enable kqueue(), fall back
-                               to select() [not set]
-  --with-cc=[filename]         Use an alternative compiler to
-                               build InspIRCd [g++]
-  --with-maxbuf=[n]            Change the per message buffer size [512]
-                               DO NOT ALTER THIS OPTION WITHOUT GOOD REASON
-                               AS IT *WILL* BREAK CLIENTS!!!
-  --prefix=[directory]         Base directory to install into (if defined,
-                               can automatically define config, module, bin
-                               and library dirs as subdirectories of prefix)
-                               [$PWD]
-  --config-dir=[directory]     Config file directory for config and SSL certs
-                               [$PWD/run/conf]
-  --log-dir=[directory]               Log file directory for logs
-                               [$PWD/run/logs]
-  --data-dir=[directory]       Data directory for variable data, such as the
-                               permchannel configuration and the XLine database
-                               [$PWD/run/data]
-  --module-dir=[directory]     Modules directory for loadable modules
-                               [$PWD/run/modules]
-  --binary-dir=[directory]     Binaries directory for core binary
-                               [$PWD/run/bin]
-  --list-extras                Show current status of extra modules
-  --enable-extras=[extras]     Enable the specified list of extras
-  --disable-extras=[extras]    Disable the specified list of extras
-  --help                       Show this help text and exit
+                       # Add a default target if the template has not defined one.
+                       unless (@targets) {
+                               push @targets, catfile(CONFIGURE_DIRECTORY, basename $_);
+                       }
 
-EOH
-       exit(0);
+                       # Write the templated files to disk.
+                       for my $target (@targets) {
+
+                               # 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_format "Writing <|GREEN $target|> ...\n";
+                               open(my $fh, '>', $target) or print_error "unable to write $target: $!";
+                               foreach (@lines) {
+                                       say $fh $_;
+                               }
+                               close $fh;
+
+                               # Set file permissions.
+                               if (defined $mode) {
+                                       chmod $mode, $target;
+                               }
+                       }
+               }
+       }
 }
 
 1;
-
diff --git a/make/console.pm b/make/console.pm
new file mode 100644 (file)
index 0000000..132544c
--- /dev/null
@@ -0,0 +1,163 @@
+#
+# 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) = @_;
+       while (1) {
+               my $answer = prompt_string($interactive, $question, $default ? 'yes' : 'no');
+               return 1 if $answer =~ /^y(?:es)?$/i;
+               return 0 if $answer =~ /^no?$/i;
+               print_warning "\"$answer\" is not \"yes\" or \"no\". Please try again.\n";
+       }
+}
+
+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 (file)
index 0000000..c0c2aee
--- /dev/null
@@ -0,0 +1,309 @@
+#
+# 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 dirname);
+use File::Spec::Functions qw(catdir);
+use Exporter              qw(import);
+
+use make::configure;
+use make::console;
+
+use constant DIRECTIVE_ERROR_PIPE => $ENV{INSPIRCD_VERBOSE} ? '' : '2>/dev/null';
+use constant VENDOR_DIRECTORY     => catdir(dirname(dirname(__FILE__)), 'vendor');
+
+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 <|GREEN $name|> 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 <|GREEN $name|> 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 "Using the default <|GREEN $name|> compiler flags for <|GREEN ${\basename $file, '.cpp'}|>: <|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 <|GREEN $name|> 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 <|GREEN $name|> 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 <|GREEN $name|> 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 "Using the default <|GREEN $name|> linker flags for <|GREEN ${\basename $file, '.cpp'}|>: <|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 <|GREEN $name|> linker flags for <|GREEN ${\basename $file, '.cpp'}|>!";
+}
+
+sub __function_require_compiler {
+       my ($file, $name, $minimum, $maximum) =  @_;
+
+       # Look up information about the compiler.
+       return undef unless $ENV{CXX};
+       my %compiler = get_compiler_info($ENV{CXX});
+
+       # Check whether the current compiler is suitable.
+       return undef unless $compiler{NAME} eq $name;
+       return undef if defined $minimum && $compiler{VERSION} < $minimum;
+       return undef if defined $maximum && $compiler{VERSION} > $maximum;
+
+       # Requirement directives don't change anything directly.
+       return "";
+}
+
+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 <|GREEN $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_vendor_directory {
+       my ($file, $name) = @_;
+
+       # Try to look the directory up in the environment...
+       my $key = __environment 'INSPIRCD_VENDOR_', $name;
+       if (defined $ENV{$key}) {
+               print_format "Found the <|GREEN $name|> vendor directory for <|GREEN ${\basename $file, '.cpp'}|> using the environment: <|BOLD $ENV{$key}|>\n";
+               return $ENV{$key};
+       }
+
+       my $directory = catdir(VENDOR_DIRECTORY, $name);
+       if (-d $directory) {
+               print_format "Using the default <|GREEN $name|> vendor directory for <|GREEN ${\basename $file, '.cpp'}|>: <|BOLD $directory|>\n";
+               return $directory;
+       }
+
+       # We can't find it via the environment or via the filesystem so give up.
+       __error $file, "unable to find the <|GREEN $name|> vendor directory for <|GREEN ${\basename $file, '.cpp'}|>!";
+}
+
+sub __function_warning {
+       my ($file, @messages) = @_;
+       print_warning @messages;
+}
+
+1;
diff --git a/make/gnutlscert.pm b/make/gnutlscert.pm
deleted file mode 100644 (file)
index 2c46e0e..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-#
-# InspIRCd -- Internet Relay Chat Daemon
-#
-#   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
-#   Copyright (C) 2007 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/>.
-#
-
-
-package make::gnutlscert;
-
-require 5.8.0;
-
-use strict;
-use warnings FATAL => qw(all);
-
-use Exporter 'import';
-use make::configure;
-our @EXPORT = qw(make_gnutls_cert);
-
-# On OS X the GnuTLS certtool is prefixed to avoid collision with the system certtool.
-my $certtool = $^O eq 'darwin' ? 'gnutls-certtool' : 'certtool';
-
-sub make_gnutls_cert()
-{
-       if (system "$certtool --version >/dev/null 2>&1")
-       {
-               print "\e[1;31mError:\e[0m unable to find '$certtool' in the PATH!\n";
-               return 1;
-       }
-       open (FH, ">certtool.template");
-       my $timestr = time();
-       my $commonname = promptstring_s('What is the hostname of your server?', 'irc.example.com');
-       my $email = promptstring_s('What email address can you be contacted at?', 'example@example.com');
-       my $unit = promptstring_s('What is the name of your unit?', 'Server Admins');
-       my $org = promptstring_s('What is the name of your organization?', 'Example IRC Network');
-       my $city = promptstring_s('What city are you located in?', 'Example City');
-       my $state = promptstring_s('What state are you located in?', 'Example State');
-       my $country = promptstring_s('What is the ISO 3166-1 code for the country you are located in?', 'XZ');
-       my $days = promptstring_s('How many days do you want your certificate to be valid for?', '365');
-       print FH <<__END__;
-# X.509 Certificate options
-#
-# DN options
-
-# The organization of the subject.
-organization = "$org"
-
-# The organizational unit of the subject.
-unit = "$unit"
-
-# The locality of the subject.
-locality = "$city"
-
-# The state of the certificate owner.
-state = "$state"
-
-# The country of the subject. Two letter code.
-country = "$country"
-
-# The common name of the certificate owner.
-cn = "$commonname"
-
-# A user id of the certificate owner.
-#uid = "clauper"
-
-# If the supported DN OIDs are not adequate you can set
-# any OID here.
-# For example set the X.520 Title and the X.520 Pseudonym
-# by using OID and string pairs.
-#dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal"
-
-# This is deprecated and should not be used in new
-# certificates.
-# pkcs9_email = "none\@none.org"
-
-# The serial number of the certificate
-serial = $timestr
-
-# In how many days, counting from today, this certificate will expire.
-expiration_days = $days
-
-# X.509 v3 extensions
-
-# A dnsname in case of a WWW server.
-#dns_name = "www.none.org"
-
-# An IP address in case of a server.
-#ip_address = "192.168.1.1"
-
-# An email in case of a person
-email = "$email"
-
-# An URL that has CRLs (certificate revocation lists)
-# available. Needed in CA certificates.
-#crl_dist_points = "http://www.getcrl.crl/getcrl/"
-
-# Whether this is a CA certificate or not
-#ca
-
-# Whether this certificate will be used for a TLS client
-tls_www_client
-
-# Whether this certificate will be used for a TLS server
-tls_www_server
-
-# Whether this certificate will be used to sign data (needed
-# in TLS DHE ciphersuites).
-signing_key
-
-# Whether this certificate will be used to encrypt data (needed
-# in TLS RSA ciphersuites). Note that it is prefered to use different
-# keys for encryption and signing.
-encryption_key
-
-# Whether this key will be used to sign other certificates.
-cert_signing_key
-
-# Whether this key will be used to sign CRLs.
-crl_signing_key
-
-# Whether this key will be used to sign code.
-code_signing_key
-
-# Whether this key will be used to sign OCSP data.
-ocsp_signing_key
-
-# Whether this key will be used for time stamping.
-time_stamping_key
-__END__
-close(FH);
-if ( (my $status = system("$certtool --generate-privkey --outfile key.pem")) ne 0) { return 1; }
-if ( (my $status = system("$certtool --generate-self-signed --load-privkey key.pem --outfile cert.pem --template certtool.template")) ne 0) { return 1; }
-unlink("certtool.template");
-return 0;
-}
-
-1;
-
diff --git a/make/opensslcert.pm b/make/opensslcert.pm
deleted file mode 100644 (file)
index 20da704..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# InspIRCd -- Internet Relay Chat Daemon
-#
-#   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
-#   Copyright (C) 2007 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/>.
-#
-
-
-package make::opensslcert;
-
-require 5.8.0;
-
-use strict;
-use warnings FATAL => qw(all);
-
-use Exporter 'import';
-use make::configure;
-our @EXPORT = qw(make_openssl_cert);
-
-
-sub make_openssl_cert()
-{
-       if (system 'openssl version >/dev/null 2>&1')
-       {
-               print "\e[1;31mCertificate generation failed:\e[0m unable to find 'openssl' in the PATH!\n";
-               return;
-       }
-       open (FH, ">openssl.template");
-       my $commonname = promptstring_s('What is the hostname of your server?', 'irc.example.com');
-       my $email = promptstring_s('What email address can you be contacted at?', 'example@example.com');
-       my $unit = promptstring_s('What is the name of your unit?', 'Server Admins');
-       my $org = promptstring_s('What is the name of your organization?', 'Example IRC Network');
-       my $city = promptstring_s('What city are you located in?', 'Example City');
-       my $state = promptstring_s('What state are you located in?', 'Example State');
-       my $country = promptstring_s('What is the ISO 3166-1 code for the country you are located in?', 'XZ');
-       my $time = promptstring_s('How many days do you want your certificate to be valid for?', '365');
-       my $use_1024 = promptstring_s('Do you want to generate less secure dhparams which are compatible with old versions of Java?', 'n');
-       print FH <<__END__;
-$country
-$state
-$city
-$org
-$unit
-$commonname
-$email
-__END__
-close(FH);
-my $dhbits = $use_1024 =~ /^(1|on|true|yes|y)$/ ? 1024 : 2048;
-system("cat openssl.template | openssl req -x509 -nodes -newkey rsa:2048 -keyout key.pem -out cert.pem -days $time 2>/dev/null");
-system("openssl dhparam -out dhparams.pem $dhbits");
-unlink("openssl.template");
-}
-
-1;
diff --git a/make/run-cc.pl b/make/run-cc.pl
deleted file mode 100755 (executable)
index 58b5850..0000000
+++ /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/bsd.mk b/make/template/bsd.mk
new file mode 100644 (file)
index 0000000..05d413d
--- /dev/null
@@ -0,0 +1,33 @@
+%platform darwin
+%platform freebsd
+%platform netbsd
+%platform openbsd
+%target Makefile
+#
+# 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/>.
+#
+
+# This file will be installed as `Makefile` on BSD derivatives. When a user runs
+# BSD Make it will be picked up as the default makefile even on systems like
+# OpenBSD which have removed BSDMakefile support. If they run GNU Make then it
+# will ignore this file and run GNUmakefile instead.
+
+all clean configureclean debug deinstall distclean help install:
+       @echo "InspIRCd no longer supports BSD Make. You should install GNU Make instead."
+       @echo "If this is problematic for you then please contact us via our IRC channel"
+       @echo "at irc.inspircd.org #InspIRCd."
+       @exit 1
diff --git a/make/template/config.h b/make/template/config.h
new file mode 100644 (file)
index 0000000..2fbf5c5
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+#define INSPIRCD_BRANCH   "InspIRCd-@VERSION_MAJOR@"
+#define INSPIRCD_VERSION  "InspIRCd-@VERSION_FULL@"
+
+#define INSPIRCD_CONFIG_PATH "@CONFIG_DIR@"
+#define INSPIRCD_DATA_PATH   "@DATA_DIR@"
+#define INSPIRCD_LOG_PATH    "@LOG_DIR@"
+#define INSPIRCD_MODULE_PATH "@MODULE_DIR@"
+
+#ifndef _WIN32
+ %target include/config.h
+ %define HAS_ARC4RANDOM_BUF
+ %define HAS_CLOCK_GETTIME
+ %define HAS_EVENTFD
+#endif
diff --git a/make/template/gdbargs b/make/template/gdbargs
new file mode 100644 (file)
index 0000000..de76c72
--- /dev/null
@@ -0,0 +1,4 @@
+%target .gdbargs
+handle SIGPIPE pass nostop noprint
+handle SIGHUP pass nostop noprint
+run
index b43ad60c9ecbb67981cf36258dc765964c36b860..91b2694121f2ed05eb0079c56a3b1b129810decd 100644 (file)
@@ -1,3 +1,4 @@
+%mode 0750
 #!/usr/bin/env perl
 
 #
@@ -29,17 +30,36 @@ 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 $scriptpath = "@SCRIPT_DIR@";
 my $basepath   =       "@BASE_DIR@";
 my $confpath   =       "@CONFIG_DIR@/";
 my $binpath    =       "@BINARY_DIR@";
 my $runpath    =       "@BASE_DIR@";
 my $datadir    =       "@DATA_DIR@";
 my $valgrindlogpath    =       "$basepath/valgrindlogs";
-my $executable =       "@EXECUTABLE@";
-my $version    =       "@VERSION@";
+my $executable =       "inspircd";
+my $version    =       "@VERSION_FULL@";
 my $uid = "@UID@";
 
-if ($< == 0 || $> == 0) {
+if (!(grep { $_ eq '--runasroot' } @ARGV) && ($< == 0 || $> == 0)) {
        if ($uid !~ /^\d+$/) {
                # Named UID, look it up
                $uid = getpwnam $uid;
@@ -87,12 +107,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()
@@ -105,7 +124,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()
@@ -113,10 +132,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;
        }
 }
 
@@ -126,23 +145,23 @@ 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();
+       exit GENERIC_EXIT_UNSPECIFIED;
 }
 
 sub cmd_version()
 {
        print "InspIRCd version: $version\n";
-       exit();
+       exit GENERIC_EXIT_SUCCESS;
 }
 
 sub cmd_restart(@)
@@ -156,13 +175,13 @@ 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");
@@ -185,7 +204,7 @@ sub dev_debug(@)
        checkgdb();
 
        # If we are still alive here.. Try starting the IRCd..
-       exec 'gdb', "--command=$basepath/.gdbargs", '--args', "$binpath/$executable", qw(--nofork --debug), @_;
+       exec 'gdb', "--command=$scriptpath/.gdbargs", '--args', "$binpath/$executable", qw(--nofork --debug), @_;
        die "Failed to start GDB: $!\n";
 }
 
@@ -204,7 +223,7 @@ sub dev_screendebug(@)
        # 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 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 gdb), "--command=$basepath/.gdbargs", '-args', "$binpath/$executable", qw(--nofork --debug --nolog), @_;
+       exec qw(screen -m -d gdb), "--command=$scriptpath/.gdbargs", '-args', "$binpath/$executable", qw(--nofork --debug --nolog), @_;
        die "Failed to start screen: $!\n";
 }
 
@@ -224,7 +243,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";
 }
 
@@ -258,7 +277,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), @_;
@@ -283,28 +302,28 @@ 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";
-       my $maxwait = (`ps -o command $pid` =~ /valgrind/i) ? 90 : 15;
+       my $maxwait = (`ps -o command $pid 2>/dev/null` =~ /valgrind/i) ? 90 : 15;
        kill TERM => $pid or die "Cannot terminate IRCd: $!\n";
        for (1..$maxwait) {
                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;
 }
 
 ###
@@ -415,7 +434,7 @@ sub checkvalgrind
        unless(`valgrind --version`)
        {
                print "Couldn't start valgrind: $!\n";
-               exit;
+               exit GENERIC_EXIT_UNSPECIFIED;
        }
 }
 
@@ -424,7 +443,7 @@ sub checkgdb
        unless(`gdb --version`)
        {
                print "Couldn't start gdb: $!\n";
-               exit;
+               exit GENERIC_EXIT_UNSPECIFIED;
        }
 }
 
@@ -433,6 +452,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 (file)
index 0000000..63d65c8
--- /dev/null
@@ -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 ircs://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 (file)
index 0000000..2bc8fd0
--- /dev/null
@@ -0,0 +1,108 @@
+.\"
+.\" 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] [--nopid] [--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 "--nopid"
+.br
+Don't write to the PID file.
+.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 ircs://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 (file)
index 0000000..e29aa59
--- /dev/null
@@ -0,0 +1,37 @@
+%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=@SCRIPT_DIR@/inspircd rehash
+ExecStart=@SCRIPT_DIR@/inspircd start
+ExecStop=@SCRIPT_DIR@/inspircd stop
+PIDFile=@DATA_DIR@/inspircd.pid
+Restart=on-failure
+Type=forking
+User=@USER@
+Group=@GROUP@
+
+[Install]
+WantedBy=multi-user.target
index 6979e1c74877919fbc55f5c9789a683ecf2baa6d..15d2fa863822c5ea2de92bfeac151ba6023d082c 100644 (file)
@@ -1,3 +1,4 @@
+%target GNUmakefile
 #
 # InspIRCd -- Internet Relay Chat Daemon
 #
 # make/template/main.mk. Any changes made to the generated
 #     files will go away whenever it is regenerated!
 #
-# Please do not edit unless you know what you're doing. This
-# needs to work in both GNU and BSD make; it is mangled for
-# them by configure.
+# Please do not edit unless you know what you're doing.
 #
 
 
-CC = @CC@
-SYSTEM = @SYSTEM@
-BUILDPATH = @BUILD_DIR@
+CXX = @CXX@
+COMPILER = @COMPILER_NAME@
+SYSTEM = @SYSTEM_NAME@
+BUILDPATH ?= $(PWD)/build
 SOCKETENGINE = @SOCKETENGINE@
-CXXFLAGS = -pipe -fPIC -DPIC
-LDLIBS = -pthread -lstdc++
-LDFLAGS = 
-CORELDFLAGS = -rdynamic -L. $(LDFLAGS)
-PICLDFLAGS = -fPIC -shared -rdynamic $(LDFLAGS)
+CORECXXFLAGS = -fPIC -fvisibility=hidden -fvisibility-inlines-hidden -pipe -Iinclude -Wall -Wextra -Wfatal-errors -Wno-unused-parameter -Wshadow
+LDLIBS = -lstdc++
+CORELDFLAGS = -rdynamic -L.
+PICLDFLAGS = -fPIC -shared -rdynamic
 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@"
+SCRPATH = "$(DESTDIR)@SCRIPT_DIR@"
 INSTALL = install
-INSTUID = @UID@
-INSTMODE_DIR = 0755
-INSTMODE_BIN = 0755
-INSTMODE_LIB = 0644
-
-@IFEQ $(CC) icpc
-  CXXFLAGS += -Wshadow
-@ELSE
-  CXXFLAGS += -pedantic -Woverloaded-virtual -Wshadow -Wformat=2 -Wmissing-format-attribute -Wall
-@ENDIF
-
-
-@IFEQ $(SYSTEM) linux
+INSTMODE_DIR = 0750
+INSTMODE_BIN = 0750
+INSTMODE_LIB = 0640
+
+ifneq ($(COMPILER), ICC)
+  CORECXXFLAGS += -Woverloaded-virtual -Wshadow
+ifneq ($(SYSTEM), openbsd)
+    CORECXXFLAGS += -pedantic -Wformat=2 -Wmissing-format-attribute -Wno-format-nonliteral
+endif
+endif
+
+ifneq ($(SYSTEM), darwin)
+  LDLIBS += -pthread
+endif
+
+ifeq ($(SYSTEM), linux)
   LDLIBS += -ldl -lrt
-@ENDIF
-@IFEQ $(SYSTEM) gnukfreebsd
+endif
+ifeq ($(SYSTEM), gnukfreebsd)
   LDLIBS += -ldl -lrt
-@ENDIF
-@IFEQ $(SYSTEM) gnu
+endif
+ifeq ($(SYSTEM), gnu)
   LDLIBS += -ldl -lrt
-@ENDIF
-@IFEQ $(SYSTEM) solaris
+endif
+ifeq ($(SYSTEM), solaris)
   LDLIBS += -lsocket -lnsl -lrt -lresolv
   INSTALL = ginstall
-@ENDIF
-@IFEQ $(SYSTEM) sunos
-  LDLIBS += -lsocket -lnsl -lrt -lresolv
-       INSTALL = ginstall
-@ENDIF
-@IFEQ $(SYSTEM) darwin
-  CXXFLAGS += -DDARWIN -frtti
+endif
+ifeq ($(SYSTEM), darwin)
   LDLIBS += -ldl
-  CORELDFLAGS = -dynamic -bind_at_load -L. $(LDFLAGS)
-  PICLDFLAGS = -fPIC -shared -twolevel_namespace -undefined dynamic_lookup $(LDFLAGS)
-@ENDIF
-@IFEQ $(SYSTEM) interix
-  CXXFLAGS += -D_ALL_SOURCE -I/usr/local/include
-@ENDIF
-
-@IFNDEF D
-  D=0
-@ENDIF
-
-GCC6=@GCC6@
-@IFEQ $(GCC6) true
-  CXXFLAGS += -fno-delete-null-pointer-checks
-@ENDIF
+  CORELDFLAGS = -dynamic -bind_at_load -L.
+  PICLDFLAGS = -fPIC -shared -twolevel_namespace -undefined dynamic_lookup
+endif
+ifeq ($(SYSTEM), haiku)
+  LDLIBS = -lnetwork -lstdc++
+  CORELDFLAGS = -L.
+  PICLDFLAGS = -fPIC -shared
+endif
+
+ifndef INSPIRCD_DEBUG
+  INSPIRCD_DEBUG=0
+endif
 
 DBGOK=0
-@IFEQ $(D) 0
-  CXXFLAGS += -O2
-@IFEQ $(CC) g++
-    CXXFLAGS += -g1
-@ENDIF
+ifeq ($(INSPIRCD_DEBUG), 0)
+  CORECXXFLAGS += -fno-rtti -O2
+ifeq ($(COMPILER), GCC)
+    CORECXXFLAGS += -g1
+endif
   HEADER = std-header
   DBGOK=1
-@ENDIF
-@IFEQ $(D) 1
-  CXXFLAGS += -O0 -g3 -Werror
+endif
+ifeq ($(INSPIRCD_DEBUG), 1)
+  CORECXXFLAGS += -O0 -g3 -Werror -DINSPIRCD_ENABLE_RTTI
   HEADER = debug-header
   DBGOK=1
-@ENDIF
-@IFEQ $(D) 2
-  CXXFLAGS += -O2 -g3
+endif
+ifeq ($(INSPIRCD_DEBUG), 2)
+  CORECXXFLAGS += -fno-rtti -O2 -g3
   HEADER = debug-header
   DBGOK=1
-@ENDIF
+endif
+ifeq ($(INSPIRCD_DEBUG), 3)
+  CORECXXFLAGS += -fno-rtti -O0 -g0
+  HEADER = std-header
+  DBGOK=1
+endif
 FOOTER = finishmessage
 
-CXXFLAGS += -Iinclude
-
-@GNU_ONLY MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --no-print-directory
 
-@GNU_ONLY SOURCEPATH = $(shell /bin/pwd)
-@BSD_ONLY SOURCEPATH != /bin/pwd
+SOURCEPATH = $(shell pwd)
 
-@IFDEF V
-  RUNCC = $(CC)
-  RUNLD = $(CC)
-  VERBOSE = -v
-@ELSE
-  @GNU_ONLY MAKEFLAGS += --silent
-  @BSD_ONLY MAKE += -s
-  RUNCC = perl "$(SOURCEPATH)/make/run-cc.pl" $(CC)
-  RUNLD = perl "$(SOURCEPATH)/make/run-cc.pl" $(CC)
-  VERBOSE =
-@ENDIF
+ifndef INSPIRCD_VERBOSE
+  MAKEFLAGS += --silent
+endif
 
-@IFDEF PURE_STATIC
-  CXXFLAGS += -DPURE_STATIC
-@ENDIF
+# Append any flags set in the environment after the base flags so
+# that they can be overridden if necessary.
+CORECXXFLAGS += $(CPPFLAGS) $(CXXFLAGS)
+CORELDFLAGS += $(LDFLAGS)
+PICLDFLAGS += $(LDFLAGS)
 
-@DO_EXPORT RUNCC RUNLD CXXFLAGS LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS
-@DO_EXPORT SOURCEPATH BUILDPATH PURE_STATIC SPLIT_CC
+export BUILDPATH
+export CORECXXFLAGS
+export CORELDFLAGS
+export CXX
+export INSPIRCD_VERBOSE
+export LDLIBS
+export PICLDFLAGS
+export SOCKETENGINE
+export SOURCEPATH
 
 # Default target
 TARGET = all
 
-@IFDEF M
+ifdef INSPIRCD_TARGET
     HEADER = mod-header
     FOOTER = mod-footer
-    @BSD_ONLY TARGET = modules/${M:S/.so$//}.so
-    @GNU_ONLY TARGET = modules/$(M:.so=).so
-@ENDIF
+    TARGET = $(INSPIRCD_TARGET)
+endif
 
-@IFDEF T
-    HEADER =
-    FOOTER = target
-    TARGET = $(T)
-@ENDIF
-
-@IFEQ $(DBGOK) 0
+ifeq ($(DBGOK), 0)
   HEADER = unknown-debug-level
-@ENDIF
+endif
 
 all: $(FOOTER)
 
@@ -168,7 +160,7 @@ target: $(HEADER)
        cd "$(BUILDPATH)"; $(MAKEENV) $(MAKE) -f real.mk $(TARGET)
 
 debug:
-       @${MAKE} D=1 all
+       @${MAKE} INSPIRCD_DEBUG=1 all
 
 debug-header:
        @echo "*************************************"
@@ -185,11 +177,7 @@ debug-header:
        @echo "*************************************"
 
 mod-header:
-@IFDEF PURE_STATIC
-       @echo 'Cannot build single modules in pure-static build'
-       @exit 1
-@ENDIF
-       @echo 'Building single module:'
+       @echo 'Building specific targets:'
 
 mod-footer: target
        @echo 'To install, copy $(BUILDPATH)/$(TARGET) to $(MODPATH)'
@@ -201,8 +189,8 @@ std-header:
        @echo "*                                   *"
        @echo "*   This will take a *long* time.   *"
        @echo "*     Why not read our docs at      *"
-       @echo "*     http://docs.inspircd.org      *"
-       @echo "*  while you wait for make to run?  *"
+       @echo "*     https://docs.inspircd.org     *"
+       @echo "*  while you wait for Make to run?  *"
        @echo "*************************************"
 
 finishmessage: target
@@ -211,36 +199,36 @@ finishmessage: target
        @echo "*        BUILD COMPLETE!            *"
        @echo "*                                   *"
        @echo "*   To install InspIRCd, type:      *"
-       @echo "*         make install              *"
+       @echo "*        'make install'             *"
        @echo "*************************************"
 
 install: target
-       @if [ "$(INSTUID)" = 0 -o "$(INSTUID)" = root ]; then \
-               echo ""; \
-               echo "Error: You must specify a non-root UID for the server"; \
-               echo ""; \
-               echo "If you are making a package, please specify using ./configure --uid"; \
-               echo "Otherwise, rerun using 'make INSTUID=irc install', where 'irc' is the user"; \
-               echo "who will be running the ircd. You will also need to modify the start script."; \
-               echo ""; \
-               exit 1; \
-       fi
-       @-$(INSTALL) -d -o $(INSTUID) -m $(INSTMODE_DIR) $(BASE)
-       @-$(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) $(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)
-@ENDIF
-       -$(INSTALL) -m $(INSTMODE_BIN) @STARTSCRIPT@ $(BASE) 2>/dev/null
-       -$(INSTALL) -m $(INSTMODE_LIB) tools/gdbargs $(BASE)/.gdbargs 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) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(BASE)
+       @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(DATPATH)
+       @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(LOGPATH)
+       @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(BINPATH)
+       @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(CONPATH)/examples/services
+       @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(CONPATH)/examples/sql
+       @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(MANPATH)
+       @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(MODPATH)
+       @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(SCRPATH)
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_BIN) "$(BUILDPATH)/bin/inspircd" $(BINPATH)
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_LIB) "$(BUILDPATH)/modules/"*.so $(MODPATH)
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_BIN) @CONFIGURE_DIRECTORY@/inspircd $(SCRPATH) 2>/dev/null
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_LIB) .gdbargs $(SCRPATH)/.gdbargs 2>/dev/null
+ifeq ($(SYSTEM), darwin)
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_BIN) @CONFIGURE_DIRECTORY@/org.inspircd.plist $(SCRPATH) 2>/dev/null
+endif
+ifeq ($(SYSTEM), linux)
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd.service $(SCRPATH) 2>/dev/null
+endif
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd.1 $(MANPATH) 2>/dev/null
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd-genssl.1 $(MANPATH) 2>/dev/null
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_BIN) tools/genssl $(BINPATH)/inspircd-genssl 2>/dev/null
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_LIB) docs/conf/*.example $(CONPATH)/examples
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_LIB) docs/conf/services/*.example $(CONPATH)/examples/services
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_LIB) docs/sql/*.sql $(CONPATH)/examples/sql
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_LIB) *.pem $(CONPATH) 2>/dev/null
        @echo ""
        @echo "*************************************"
        @echo "*        INSTALL COMPLETE!          *"
@@ -251,15 +239,12 @@ install: target
        @echo '  Binaries:' $(BINPATH)
        @echo '  Modules:' $(MODPATH)
        @echo '  Data:' $(DATPATH)
-       @echo 'To start the ircd, run:' $(BASE)/inspircd start
+       @echo 'To start the ircd, run:' $(SCRPATH)/inspircd start
        @echo 'Remember to create your config file:' $(CONPATH)/inspircd.conf
        @echo 'Examples are available at:' $(CONPATH)/examples/
 
-@GNU_ONLY RCS_FILES = $(wildcard .git/index src/version.sh)
-@BSD_ONLY RCS_FILES = src/version.sh
-GNUmakefile BSDmakefile: make/template/main.mk configure $(RCS_FILES)
-       ./configure -update
-@BSD_ONLY .MAKEFILEDEPS: BSDmakefile
+GNUmakefile: make/template/main.mk src/version.sh configure @CONFIGURE_CACHE_FILE@
+       ./configure --update
 
 clean:
        @echo Cleaning...
@@ -272,21 +257,20 @@ clean:
 deinstall:
        -rm -f $(BINPATH)/inspircd
        -rm -rf $(CONPATH)/examples
-       -rm -f $(MODPATH)/cmd_*.so
+       -rm -f $(MANPATH)/inspircd.1
+       -rm -f $(MANPATH)/inspircd-genssl.1
        -rm -f $(MODPATH)/m_*.so
-       -rm -f $(BASE)/.gdbargs
-       -rm -f $(BASE)/org.inspircd.plist
-
-squeakyclean: distclean
+       -rm -f $(MODPATH)/core_*.so
+       -rm -f $(SCRPATH)/.gdbargs
+       -rm -f $(SCRPATH)/inspircd.service
+       -rm -f $(SCRPATH)/org.inspircd.plist
 
 configureclean:
-       rm -f .config.cache
-       rm -f BSDmakefile
+       rm -f .gdbargs
+       -rm -f Makefile
        rm -f GNUmakefile
-       rm -f include/inspircd_config.h
-       rm -f include/inspircd_version.h
-       rm -f inspircd
-       -rm -f org.inspircd.plist
+       rm -f include/config.h
+       rm -rf @CONFIGURE_DIRECTORY@
 
 distclean: clean configureclean
        -rm -rf "$(SOURCEPATH)/run"
@@ -298,11 +282,12 @@ 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 ' INSPIRCD_DEBUG=3    Enable fast build with no optimisations or symbols, for Travis CI'
+       @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)'
@@ -310,10 +295,8 @@ 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_TARGET=target  Builds a user-specified target, such as "inspircd" or "core_dns"'
+       @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)'
@@ -322,4 +305,4 @@ help:
 
 .NOTPARALLEL:
 
-.PHONY: all target debug debug-header mod-header mod-footer std-header finishmessage install clean deinstall squeakyclean configureclean help
+.PHONY: all target debug debug-header mod-header mod-footer std-header finishmessage install clean deinstall configureclean help
index 4dac209f666e3ef8aa612835dd55fa3a30af1876..ae4e90916f3891ca0c07c616384130400ee2c753 100644 (file)
@@ -1,3 +1,4 @@
+%platform darwin
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
@@ -26,6 +27,8 @@
        <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/arc4random_buf.cpp b/make/test/arc4random_buf.cpp
new file mode 100644 (file)
index 0000000..86f74f6
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2018 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 <stdlib.h>
+
+int main() {
+       char buffer[100];
+       arc4random_buf(buffer, sizeof(buffer));
+       return 0;
+}
diff --git a/make/test/clock_gettime.cpp b/make/test/clock_gettime.cpp
new file mode 100644 (file)
index 0000000..d111d59
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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/>.
+ */
+
+
+#include <time.h>
+
+int main() {
+       timespec time_spec;
+       clock_gettime(CLOCK_REALTIME, &time_spec);
+       return 0;
+}
diff --git a/make/test/compiler.cpp b/make/test/compiler.cpp
new file mode 100644 (file)
index 0000000..f014233
--- /dev/null
@@ -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 (file)
index 0000000..10b156f
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..9b38b79
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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
+ * 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 <sys/eventfd.h>
+
+int main() {
+       eventfd_t efd_data;
+       int fd;
+
+       fd = eventfd(0, EFD_NONBLOCK);
+       eventfd_read(fd, &efd_data);
+
+       return (fd < 0);
+}
diff --git a/make/test/kqueue.cpp b/make/test/kqueue.cpp
new file mode 100644 (file)
index 0000000..708677a
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/event.h>
+
+int main() {
+       int fd = kqueue();
+       return (fd < 0);
+}
index a494fb74b06d712a21278c5f22c91cee0e6ef139..18e60d7597ad31a4b0d1fa12a0a20b68c615390a 100755 (executable)
 #
 
 
+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$//);
-
-## BEGIN HACK: REMOVE IN 2.2!
-sub read_config_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;
-}
-
-our %config = read_config_cache();
-## END HACK
 
-if ($type eq 'gen-ld') {
-       do_static_find(@ARGV);
-} elsif ($type eq 'static-ld') {
-       do_static_link(@ARGV);
-} elsif ($type eq 'core-ld') {
+if ($type eq 'core-ld') {
        do_core_link(@ARGV);
 } elsif ($type eq 'link-dir') {
        do_link_dir(@ARGV);
@@ -65,44 +52,34 @@ if ($type eq 'gen-ld') {
 }
 exit 1;
 
-sub do_static_find {
-       my @flags;
-       for my $file (@ARGV) {
-               push @flags, getlinkerflags($file);
+sub message($$$) {
+       my ($type, $file, $command) = @_;
+       if ($ENV{INSPIRCD_VERBOSE}) {
+               print "$command\n";
+       } else {
+               print_format "\t<|GREEN $type:|>\t\t$file\n";
        }
-       open F, '>', $out;
-       print F join ' ', @flags;
-       close F;
-       exit 0;
 }
 
-sub do_static_link {
-       my $execstr = "$ENV{RUNLD} -o $out $ENV{CORELDFLAGS}";
-       for (@ARGV) {
-               if (/\.cmd$/) {
-                       open F, '<', $_;
-                       my $libs = <F>;
-                       chomp $libs;
-                       $execstr .= ' '.$libs;
-                       close F;
-               } else {
-                       $execstr .= ' '.$_;
-               }
-       }
-       $execstr .= ' '.$ENV{LDLIBS};
-       print "$execstr\n" if $verbose;
-       exec $execstr;
+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_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;
 }
 
@@ -111,27 +88,22 @@ sub do_compile {
 
        my $flags = '';
        my $libs = '';
-       my $binary = $ENV{RUNCC};
        if ($do_compile) {
-               $flags = $ENV{CXXFLAGS};
-               $flags =~ s/ -pedantic// if nopedantic($file);
-               $flags .= ' ' . getcompilerflags($file);
+               $flags = $ENV{CORECXXFLAGS} . ' ' . get_directive($file, 'CompilerFlags', '');
 
-               if ($file =~ m#(?:^|/)((?:m|cmd)_[^/. ]+)(?:\.cpp|/.*\.cpp)$#) {
-                       $flags .= ' -DMODNAME='.$1.'.so';
+               if ($file =~ m#(?:^|/)((?:m|core)_[^/. ]+)(?:\.cpp|/.*\.cpp)$#) {
+                       $flags .= ' -DMODNAME=\\"'.$1.'\\"';
                }
-       } else {
-               $binary = $ENV{RUNLD};
        }
 
        if ($do_link) {
                $flags = join ' ', $flags, $ENV{PICLDFLAGS};
-               $libs = join ' ', getlinkerflags($file);
+               $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 (file)
index baba584..0000000
+++ /dev/null
@@ -1,471 +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/>.
-#
-
-
-package make::utilities;
-
-require 5.8.0;
-
-use strict;
-use warnings FATAL => qw(all);
-
-use Exporter 'import';
-use POSIX;
-use File::Temp;
-use Getopt::Long;
-use Fcntl;
-our @EXPORT = qw(make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring);
-
-# Parse the output of a *_config program,
-# such as pcre_config, take out the -L
-# directive and return an rpath for it.
-
-# \e[1;32msrc/Makefile\e[0m
-
-my %already_added = ();
-my $if_skip_lines = 0;
-
-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) = @_;
-       return "" if defined $ENV{DISABLE_RPATH};
-       chomp(my $data = `$executable`);
-       my $output = "";
-       while ($data =~ /-L(\S+)/)
-       {
-               my $libpath = $1;
-               if (!exists $already_added{$libpath})
-               {
-                       print "Adding runtime 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 ";
-               $data =~ s/-L(\S+)//;
-       }
-       return $output;
-}
-
-sub extend_pkg_path()
-{
-       return if defined $ENV{DISABLE_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) = @_;
-
-       my $key = "default_includedir_$packagename";
-       if (exists $main::config{$key})
-       {
-               print "Locating include directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
-               my $ret = $main::config{$key};
-               print "\e[1;32m$ret\e[0m (cached)\n";
-               return $ret;
-       }
-
-       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) = @_;
-
-       my $key = "default_libdir_$packagename";
-       if (exists $main::config{$key})
-       {
-               print "Locating library directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
-               my $ret = $main::config{$key};
-               print "\e[1;32m$ret\e[0m (cached)\n";
-               return $ret;
-       }
-
-       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;
-
-               # This is only a cursory check, just designed to catch casual accidental use of backticks.
-               # There are pleanty of ways around it, but its not supposed to be for security, just checking
-               # that people are using the new configuration api as theyre supposed to and not just using
-               # backticks instead of eval(), being as eval has accountability. People wanting to get around
-               # the accountability will do so anyway.
-               if (($line =~ /`/) && ($line !~ /eval\(.+?`.+?\)/))
-               {
-                       die "Developers should no longer use backticks in configuration macros. Please use exec() and eval() macros instead. Offending line: $line (In module: $module)";
-               }
-
-               if ($line =~ /if(gt|lt)\("(.+?)","(.+?)"\)/) {
-                       chomp(my $result = `$2 2>/dev/null`);
-                       if (($1 eq 'gt' && $result le $3) || ($1 eq 'lt' && $result ge $3)) {
-                               $line = substr $line, 0, $-[0];
-                       } else {
-                               $line =~ s/if$1\("$2","$3"\)//;
-                       }
-               }
-
-               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 = File::Temp::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);
-                       unlink($tmpfile);
-                       $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 libgnutls-dev and libgnutls.\n";
-               exit;
-       }
-       else
-       {
-               return $line;
-       }
-}
-
-1;
-
index f00234994a4dfc59990a1128b46ec4f4f9edd250..50a86a7f7cac40b993396f4e77a5dccd63160962 100755 (executable)
@@ -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
 #
 
 
-use strict;
-use warnings FATAL => qw(all);
-
-BEGIN {
-       require 5.8.0;
-       push @INC, '.';
-}
-
 BEGIN {
-       # HACK: for some reason this needs to be in a second BEGIN block
-       # or it doesn't receive the updated @INC from above.
-       use make::configure;
-       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 LWP::Simple;
+use feature ':5.10';
+use strict;
+use warnings FATAL => qw(all);
+
+use File::Basename qw(basename);
+use FindBin        qw($RealDir);
 
-our @modlist;
+use lib $RealDir;
+use make::common;
+use make::console;
 
 my %installed;
 # $installed{name} = $version
@@ -108,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 {
@@ -116,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>) {
@@ -137,8 +122,6 @@ while (<SRC>) {
 }
 close SRC;
 
-getmodules(1);
-
 # determine core version
 `./src/version.sh` =~ /InspIRCd-([0-9.]+)/ or die "Cannot determine inspircd version";
 $installed{core} = $1;
@@ -162,9 +145,8 @@ $modules{core}{$1} = {
 };
 
 # set up core module list
-for my $modname (@modlist) {
-       my $mod = "m_$modname";
-       my $modfile = "src/modules/$mod.cpp";
+for my $modname (<src/modules/m_*.cpp>) {
+       my $mod = basename($modname, '.cpp');
        my $ver = getmodversion($mod) || '0.0';
        $ver =~ s/\$Rev: (.*) \$/$1/; # for storing revision in SVN
        $installed{$mod} = $ver;
@@ -271,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}) {
@@ -288,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_/;
@@ -298,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}};
@@ -312,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";
@@ -368,11 +343,6 @@ for my $mod (sort keys %todo) {
 }
 
 # 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";
index 52449e55e86d7995e34998a92a511547efc96ff9..13e4dc7c708266547c12be9d519b7d6d868bc390 100644 (file)
  */
 
 
-/* $Core */
-
 #include "inspircd.h"
-#include "bancache.h"
 
-BanCacheHit *BanCacheManager::AddHit(const std::string &ip, const std::string &type, const std::string &reason)
+BanCacheHit::BanCacheHit(const std::string& type, const std::string& reason, time_t seconds)
+       : Type(type)
+       , Reason(reason)
+       , Expiry(ServerInstance->Time() + seconds)
 {
-       BanCacheHit *b;
-
-       if (this->BanHash->find(ip) != this->BanHash->end()) // can't have two cache entries on the same IP, sorry..
-               return NULL;
-
-       b = new BanCacheHit(ip, type, reason);
-
-       this->BanHash->insert(std::make_pair(ip, b));
-       return b;
 }
 
 BanCacheHit *BanCacheManager::AddHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds)
 {
-       BanCacheHit *b;
-
-       if (this->BanHash->find(ip) != this->BanHash->end()) // can't have two cache entries on the same IP, sorry..
+       BanCacheHit*& b = BanHash[ip];
+       if (b != NULL) // can't have two cache entries on the same IP, sorry..
                return NULL;
 
-       b = new BanCacheHit(ip, type, reason, seconds);
-
-       this->BanHash->insert(std::make_pair(ip, b));
+       b = new BanCacheHit(type, reason, (seconds ? seconds : 86400));
        return b;
 }
 
 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
-       else
-       {
-               if (ServerInstance->Time() > i->second->Expiry)
-               {
-                       ServerInstance->Logs->Log("BANCACHE", DEBUG, "Hit on " + ip + " is out of date, removing!");
-                       RemoveHit(i->second);
-                       return NULL; // out of date
-               }
 
-               return i->second; // hit.
-       }
+       if (RemoveIfExpired(i))
+               return NULL; // expired
+
+       return i->second; // hit.
 }
 
-bool BanCacheManager::RemoveHit(BanCacheHit *b)
+bool BanCacheManager::RemoveIfExpired(BanCacheHash::iterator& it)
 {
-       BanCacheHash::iterator i;
-
-       if (!b)
-               return false; // I don't think so.
-
-       i = this->BanHash->find(b->IP);
-
-       if (i == this->BanHash->end())
-       {
-               // err..
-               ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveHit(): I got asked to remove a hit that wasn't in the hash(?)");
-       }
-       else
-       {
-               this->BanHash->erase(i);
-       }
+       if (ServerInstance->Time() < it->second->Expiry)
+               return false;
 
-       delete b;
+       ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "Hit on " + it->first + " is out of date, removing!");
+       delete it->second;
+       it = BanHash.erase(it);
        return true;
 }
 
-unsigned int BanCacheManager::RemoveEntries(const std::string &type, bool positive)
+void BanCacheManager::RemoveEntries(const std::string& type, bool positive)
 {
-       int removed = 0;
-
-       BanCacheHash::iterator safei;
-
        if (positive)
-               ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveEntries(): Removing positive hits for " + type);
+               ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing positive hits for " + type);
        else
-               ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveEntries(): Removing negative hits for " + type);
+               ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing all negative hits");
 
-       for (BanCacheHash::iterator n = BanHash->begin(); n != BanHash->end(); )
+       for (BanCacheHash::iterator i = BanHash.begin(); i != BanHash.end(); )
        {
-               safei = n;
-               safei++;
+               if (RemoveIfExpired(i))
+                       continue; // updates the iterator if expired
 
-               BanCacheHit *b = n->second;
+               BanCacheHit* b = i->second;
+               bool remove = false;
 
-               /* Safe to delete items here through iterator 'n' */
-               if (b->Type == type || !positive) // if removing negative hits, ignore type..
+               if (positive)
                {
-                       if ((positive && !b->Reason.empty()) || b->Reason.empty())
-                       {
-                               /* we need to remove this one. */
-                               ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveEntries(): Removing a hit on " + b->IP);
-                               delete b;
-                               BanHash->erase(n); // WORD TO THE WISE: don't use RemoveHit here, because we MUST remove the iterator in a safe way.
-                               removed++;
-                       }
+                       // when removing positive hits, remove only if the type matches
+                       remove = b->IsPositive() && (b->Type == type);
+               }
+               else
+               {
+                       // when removing negative hits, remove all of them
+                       remove = !b->IsPositive();
                }
 
-               /* End of safe section */
-               n = safei;
-       }
-
-
-       return removed;
-}
-
-void BanCacheManager::RehashCache()
-{
-       BanCacheHash* NewHash = new BanCacheHash();
-
-       BanCacheHash::iterator safei;
-       for (BanCacheHash::iterator n = BanHash->begin(); n != BanHash->end(); )
-       {
-               safei = n;
-               safei++;
-
-               /* Safe to delete items here through iterator 'n' */
-               BanCacheHit *b = n->second;
-
-               if (ServerInstance->Time() > b->Expiry)
+               if (remove)
                {
                        /* we need to remove this one. */
+                       ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing a hit on " + i->first);
                        delete b;
-                       BanHash->erase(n); // WORD TO THE WISE: don't use RemoveHit here, because we MUST remove the iterator in a safe way.
+                       i = BanHash.erase(i);
                }
                else
-               {
-                       /* Actually inserts a std::pair */
-                       NewHash->insert(*n);
-               }
-
-               /* End of safe section */
-
-               n = safei;
+                       ++i;
        }
-
-       delete BanHash;
-       BanHash = NewHash;
 }
 
 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;
 }
index 66a3cb140076e0db326435458b98b8b7fecf8f06..8749aee8ea983b2df55788c1c4744f35c651184d 100644 (file)
 #include "inspircd.h"
 #include "base.h"
 #include <time.h>
+#ifdef INSPIRCD_ENABLE_RTTI
 #include <typeinfo>
+#endif
 
 classbase::classbase()
 {
-       if (ServerInstance && ServerInstance->Logs)
-               ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::+ @%p", (void*)this);
+       if (ServerInstance)
+               ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::+ @%p", (void*)this);
 }
 
 CullResult classbase::cull()
 {
-       if (ServerInstance && ServerInstance->Logs)
-               ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::-%s @%p",
+       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)
-               ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::~ @%p", (void*)this);
+       if (ServerInstance)
+               ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::~ @%p", (void*)this);
 }
 
 CullResult::CullResult()
@@ -73,15 +79,15 @@ refcountbase::refcountbase() : refcount(0)
 
 refcountbase::~refcountbase()
 {
-       if (refcount && ServerInstance && ServerInstance->Logs)
-               ServerInstance->Logs->Log("CULLLIST", DEBUG, "refcountbase::~ @%p with refcount %d",
+       if (refcount && ServerInstance)
+               ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "refcountbase::~ @%p with refcount %d",
                        (void*)this, refcount);
 }
 
 usecountbase::~usecountbase()
 {
-       if (usecount && ServerInstance && ServerInstance->Logs)
-               ServerInstance->Logs->Log("CULLLIST", DEBUG, "usecountbase::~ @%p with refcount %d",
+       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;
@@ -168,41 +186,41 @@ void Extensible::doUnhookExtensions(const std::vector<reference<ExtensionItem> >
                ExtensibleStore::iterator e = extensions.find(item);
                if (e != extensions.end())
                {
-                       item->free(e->second);
+                       item->free(this, e->second);
                        extensions.erase(e);
                }
        }
 }
 
-static struct DummyExtensionItem : LocalExtItem
-{
-       DummyExtensionItem() : LocalExtItem("", NULL) {}
-       void free(void*) {}
-} dummy;
-
 Extensible::Extensible()
+       : culled(false)
 {
-       extensions[&dummy] = NULL;
 }
 
 CullResult Extensible::cull()
+{
+       FreeAllExtItems();
+       culled = true;
+       return classbase::cull();
+}
+
+void Extensible::FreeAllExtItems()
 {
        for(ExtensibleStore::iterator i = extensions.begin(); i != extensions.end(); ++i)
        {
-               i->first->free(i->second);
+               i->first->free(this, i->second);
        }
        extensions.clear();
-       return classbase::cull();
 }
 
 Extensible::~Extensible()
 {
-       if (!extensions.empty() && ServerInstance && ServerInstance->Logs)
-               ServerInstance->Logs->Log("CULLLIST", DEBUG,
-                       "Extensible destructor called without cull @%p", (void*)this);
+       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)
 {
 }
 
@@ -219,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()
 {
@@ -228,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)
 {
 }
 
@@ -243,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, ConvToNum<intptr_t>(value));
+}
+
 intptr_t LocalIntExt::get(const Extensible* container) const
 {
        return reinterpret_cast<intptr_t>(get_raw(container));
@@ -261,11 +294,12 @@ intptr_t LocalIntExt::set(Extensible* container, intptr_t value)
                return reinterpret_cast<intptr_t>(unset_raw(container));
 }
 
-void LocalIntExt::free(void*)
+void LocalIntExt::free(Extensible* container, void* item)
 {
 }
 
-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)
 {
 }
 
@@ -303,7 +337,7 @@ void StringExtItem::unset(Extensible* container)
        delete static_cast<std::string*>(old);
 }
 
-void StringExtItem::free(void* item)
+void StringExtItem::free(Extensible* container, void* item)
 {
        delete static_cast<std::string*>(item);
 }
@@ -312,4 +346,3 @@ ModuleException::ModuleException(const std::string &message, Module* who)
        : CoreException(message, who ? who->ModuleSourceFile : "A Module")
 {
 }
-
index df2212a219b9f94ae9864477939fd926d96904b9..0ff252c9517835100e8f7385668f35023aa9c061 100644 (file)
  */
 
 
-/* $Core */
-
 #include "inspircd.h"
-#include <cstdarg>
-#include "mode.h"
+#include "listmode.h"
 
-Channel::Channel(const std::string &cname, time_t ts)
+namespace
 {
-       if (!ServerInstance->chanlist->insert(std::make_pair(cname, this)).second)
-               throw CoreException("Cannot create duplicate channel " + cname);
-
-       this->name = cname;
-       this->age = ts ? ts : ServerInstance->Time();
-
-       maxbans = topicset = 0;
-       modes.reset();
+       ChanModeReference ban(NULL, "ban");
 }
 
-void Channel::SetMode(char mode,bool mode_on)
+Channel::Channel(const std::string &cname, time_t ts)
+       : name(cname), age(ts), topicset(0)
 {
-       modes[mode-65] = mode_on;
+       if (!ServerInstance->chanlist.insert(std::make_pair(cname, this)).second)
+               throw CoreException("Cannot create duplicate channel " + cname);
 }
 
 void Channel::SetMode(ModeHandler* mh, bool on)
 {
-       modes[mh->GetModeChar() - 65] = on;
+       if (mh && mh->GetId() != ModeParser::MODEID_MAX)
+               modes[mh->GetId()] = on;
 }
 
-void Channel::SetModeParam(char mode, const std::string& parameter)
+void Channel::SetTopic(User* u, const std::string& ntopic, time_t topicts, const std::string* setter)
 {
-       CustomModeList::iterator n = custom_mode_params.find(mode);
-       // always erase, even if changing, so that the map gets the new value
-       if (n != custom_mode_params.end())
-               custom_mode_params.erase(n);
-       if (parameter.empty())
-       {
-               modes[mode-65] = false;
-       }
-       else
+       // Send a TOPIC message to the channel only if the new topic text differs
+       if (this->topic != ntopic)
        {
-               custom_mode_params[mode] = parameter;
-               modes[mode-65] = true;
+               this->topic = ntopic;
+               ClientProtocol::Messages::Topic topicmsg(u, this, this->topic);
+               Write(ServerInstance->GetRFCEvents().topic, topicmsg);
        }
-}
 
-void Channel::SetModeParam(ModeHandler* mode, const std::string& parameter)
-{
-       SetModeParam(mode->GetModeChar(), parameter);
-}
+       // 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;
 
-std::string Channel::GetModeParameter(char mode)
-{
-       CustomModeList::iterator n = custom_mode_params.find(mode);
-       if (n != custom_mode_params.end())
-               return n->second;
-       return "";
+       FOREACH_MOD(OnPostTopicChange, (u, this, this->topic));
 }
 
-std::string Channel::GetModeParameter(ModeHandler* mode)
-{
-       CustomModeList::iterator n = custom_mode_params.find(mode->GetModeChar());
-       if (n != custom_mode_params.end())
-               return n->second;
-       return "";
-}
-
-int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
+Membership* Channel::AddUser(User* user)
 {
-       if (!u)
-               u = ServerInstance->FakeClient;
-       if (IS_LOCAL(u) && !forceset)
-       {
-               ModResult res;
-               FIRST_MOD_RESULT(OnPreTopicChange, res, (u,this,ntopic));
-
-               if (res == MOD_RES_DENY)
-                       return CMD_FAILURE;
-               if (res != MOD_RES_ALLOW)
-               {
-                       if (!this->HasUser(u))
-                       {
-                               u->WriteNumeric(442, "%s %s :You're not on that channel!",u->nick.c_str(), this->name.c_str());
-                               return CMD_FAILURE;
-                       }
-                       if (IsModeSet('t') && !ServerInstance->OnCheckExemption(u,this,"topiclock").check(GetPrefixValue(u) >= HALFOP_VALUE))
-                       {
-                               u->WriteNumeric(482, "%s %s :You do not have access to change the topic on this channel", u->nick.c_str(), this->name.c_str());
-                               return CMD_FAILURE;
-                       }
-               }
-       }
-
-       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();
-
-       FOREACH_MOD(I_OnPostTopicChange,OnPostTopicChange(u, this, this->topic));
+       std::pair<MemberMap::iterator, bool> ret = userlist.insert(std::make_pair(user, insp::aligned_storage<Membership>()));
+       if (!ret.second)
+               return NULL;
 
-       return CMD_SUCCESS;
+       Membership* memb = new(ret.first->second) Membership(user, this);
+       return memb;
 }
 
-long Channel::GetUserCounter()
+void Channel::DelUser(User* user)
 {
-       return userlist.size();
+       MemberMap::iterator it = userlist.find(user);
+       if (it != userlist.end())
+               DelUser(it);
 }
 
-Membership* Channel::AddUser(User* user)
+void Channel::CheckDestroy()
 {
-       Membership* memb = new Membership(user, this);
-       userlist[user] = memb;
-       return memb;
-}
-
-void Channel::DelUser(User* user)
-{
-       UserMembIter a = userlist.find(user);
+       if (!userlist.empty())
+               return;
 
-       if (a != userlist.end())
-       {
-               a->second->cull();
-               delete a->second;
-               userlist.erase(a);
-       }
+       ModResult res;
+       FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
+       if (res == MOD_RES_DENY)
+               return;
 
-       if (userlist.empty())
-       {
-               ModResult res;
-               FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
-               if (res == MOD_RES_DENY)
-                       return;
-               chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
-               /* kill the record */
-               if (iter != ServerInstance->chanlist->end())
-               {
-                       FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
-                       ServerInstance->chanlist->erase(iter);
-               }
+       // 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);
+       if ((iter == ServerInstance->chanlist.end()) || (iter->second != this))
+               return;
 
-               ClearInvites();
-               ServerInstance->GlobalCulls.AddItem(this);
-       }
+       FOREACH_MOD(OnChannelDelete, (this));
+       ServerInstance->chanlist.erase(iter);
+       ServerInstance->GlobalCulls.AddItem(this);
 }
 
-bool Channel::HasUser(User* user)
+void Channel::DelUser(const MemberMap::iterator& membiter)
 {
-       return (userlist.find(user) != userlist.end());
+       Membership* memb = membiter->second;
+       memb->cull();
+       memb->~Membership();
+       userlist.erase(membiter);
+
+       // If this channel became empty then it should be removed
+       CheckDestroy();
 }
 
 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;
 }
 
-const UserMembList* Channel::GetUsers()
-{
-       return &userlist;
-}
-
 void Channel::SetDefaultModes()
 {
-       ServerInstance->Logs->Log("CHANNELS", DEBUG, "SetDefaultModes %s",
+       ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "SetDefaultModes %s",
                ServerInstance->Config->DefaultModes.c_str());
        irc::spacesepstream list(ServerInstance->Config->DefaultModes);
        std::string modeseq;
@@ -201,7 +134,10 @@ void Channel::SetDefaultModes()
                ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
                if (mode)
                {
-                       if (mode->GetNumParams(true))
+                       if (mode->IsPrefixMode())
+                               continue;
+
+                       if (mode->NeedsParam(true))
                        {
                                list.GetToken(parameter);
                                // If the parameter begins with a ':' then it's invalid
@@ -211,7 +147,7 @@ void Channel::SetDefaultModes()
                        else
                                parameter.clear();
 
-                       if ((mode->GetNumParams(true)) && (parameter.empty()))
+                       if ((mode->NeedsParam(true)) && (parameter.empty()))
                                continue;
 
                        mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, parameter, true);
@@ -223,204 +159,141 @@ void Channel::SetDefaultModes()
  * add a channel to a user, creating the record for it if needed and linking
  * it to the user record
  */
-Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS)
+Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, const std::string& key)
 {
-       // Fix: unregistered users could be joined using /SAJOIN
-       if (!user || !cn || user->registered != REG_ALL)
+       if (user->registered != REG_ALL)
+       {
+               ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join unregistered user " + user->uuid + " to channel " + cname);
                return NULL;
-
-       std::string privs;
-       Channel *Ptr;
+       }
 
        /*
         * We don't restrict the number of channels that remote users or users that are override-joining may be in.
-        * We restrict local users to MaxChans channels.
-        * We restrict local operators to OperMaxChans channels.
+        * We restrict local users to <connect:maxchans> channels.
+        * We restrict local operators to <oper:maxchans> channels.
         * This is a lot more logical than how it was formerly. -- w00t
         */
-       if (IS_LOCAL(user) && !override)
+       if (!override)
        {
-               if (user->HasPrivPermission("channels/high-join-limit"))
+               unsigned int maxchans = user->GetClass()->maxchans;
+               if (user->IsOper())
                {
-                       if (user->chans.size() >= ServerInstance->Config->OperMaxChans)
-                       {
-                               user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
-                               return NULL;
-                       }
+                       unsigned int opermaxchans = ConvToNum<unsigned int>(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 && user->HasPrivPermission("channels/high-join-limit"))
+                               opermaxchans = ServerInstance->Config->OperMaxChans;
+                       if (opermaxchans)
+                               maxchans = opermaxchans;
                }
-               else
+               if (user->chans.size() >= maxchans)
                {
-                       unsigned int maxchans = user->GetClass()->maxchans;
-                       if (!maxchans)
-                               maxchans = ServerInstance->Config->MaxChans;
-                       if (user->chans.size() >= maxchans)
-                       {
-                               user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
-                               return NULL;
-                       }
+                       user->WriteNumeric(ERR_TOOMANYCHANNELS, cname, "You are on too many channels");
+                       return NULL;
                }
        }
 
-       std::string cname;
-       cname.assign(std::string(cn), 0, ServerInstance->Config->Limits.ChanMax);
-       Ptr = ServerInstance->FindChan(cname);
-       bool created_by_local = false;
+       // Crop channel name if it's too long
+       if (cname.length() > ServerInstance->Config->Limits.ChanMax)
+               cname.resize(ServerInstance->Config->Limits.ChanMax);
 
-       if (!Ptr)
+       Channel* chan = ServerInstance->FindChan(cname);
+       bool created_by_local = (chan == NULL); // Flag that will be passed to modules in the OnUserJoin() hook later
+       std::string privs; // Prefix mode(letter)s to give to the joining user
+
+       if (!chan)
        {
-               /*
-                * Fix: desync bug was here, don't set @ on remote users - spanningtree handles their permissions. bug #358. -- w00t
-                */
-               if (!IS_LOCAL(user))
-               {
-                       if (!TS)
-                               ServerInstance->Logs->Log("CHANNELS",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cn);
-               }
-               else
-               {
-                       privs = "o";
-                       created_by_local = true;
-               }
+               privs = ServerInstance->Config->DefaultModes.substr(0, ServerInstance->Config->DefaultModes.find(' '));
 
-               if (IS_LOCAL(user) && override == false)
+               if (override == false)
                {
+                       // Ask the modules whether they're ok with the join, pass NULL as Channel* as the channel is yet to be created
                        ModResult MOD_RESULT;
-                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname.c_str(), privs, key ? key : ""));
+                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname, privs, key));
                        if (MOD_RESULT == MOD_RES_DENY)
-                               return NULL;
+                               return NULL; // A module wasn't happy with the join, abort
                }
 
-               Ptr = new Channel(cname, TS);
+               chan = new Channel(cname, ServerInstance->Time());
+               // Set the default modes on the channel (<options:defaultmodes>)
+               chan->SetDefaultModes();
        }
        else
        {
                /* Already on the channel */
-               if (Ptr->HasUser(user))
+               if (chan->HasUser(user))
                        return NULL;
 
-               /*
-                * remote users are allowed us to bypass channel modes
-                * and bans (used by servers)
-                */
-               if (IS_LOCAL(user) && override == false)
+               if (override == false)
                {
                        ModResult MOD_RESULT;
-                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, Ptr, cname.c_str(), privs, key ? key : ""));
+                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, chan, cname, privs, key));
+
+                       // A module explicitly denied the join and (hopefully) generated a message
+                       // describing the situation, so we may stop here without sending anything
                        if (MOD_RESULT == MOD_RES_DENY)
-                       {
                                return NULL;
-                       }
-                       else if (MOD_RESULT == MOD_RES_PASSTHRU)
-                       {
-                               std::string ckey = Ptr->GetModeParameter('k');
-                               bool invited = IS_LOCAL(user)->IsInvited(Ptr->name.c_str());
-                               bool can_bypass = ServerInstance->Config->InvBypassModes && invited;
-
-                               if (!ckey.empty())
-                               {
-                                       FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, Ptr, key ? key : ""));
-                                       if (!MOD_RESULT.check((key && ckey == key) || can_bypass))
-                                       {
-                                               // 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 %s :Cannot join channel (Incorrect channel key)",user->nick.c_str(), Ptr->name.c_str());
-                                               return NULL;
-                                       }
-                               }
-
-                               if (Ptr->IsModeSet('i'))
-                               {
-                                       FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, Ptr));
-                                       if (!MOD_RESULT.check(invited))
-                                       {
-                                               user->WriteNumeric(ERR_INVITEONLYCHAN, "%s %s :Cannot join channel (Invite only)",user->nick.c_str(), Ptr->name.c_str());
-                                               return NULL;
-                                       }
-                               }
-
-                               std::string limit = Ptr->GetModeParameter('l');
-                               if (!limit.empty())
-                               {
-                                       FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, Ptr));
-                                       if (!MOD_RESULT.check((Ptr->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
-                                       {
-                                               user->WriteNumeric(ERR_CHANNELISFULL, "%s %s :Cannot join channel (Channel is full)",user->nick.c_str(), Ptr->name.c_str());
-                                               return NULL;
-                                       }
-                               }
 
-                               if (Ptr->IsBanned(user) && !can_bypass)
+                       // If no module returned MOD_RES_DENY or MOD_RES_ALLOW (which is the case
+                       // most of the time) then proceed to check channel bans.
+                       //
+                       // If a module explicitly allowed the join (by returning MOD_RES_ALLOW),
+                       // then this entire section is skipped
+                       if (MOD_RESULT == MOD_RES_PASSTHRU)
+                       {
+                               if (chan->IsBanned(user))
                                {
-                                       user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Cannot join channel (You're banned)",user->nick.c_str(), Ptr->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)
-                               {
-                                       IS_LOCAL(user)->RemoveInvite(Ptr->name.c_str());
-                               }
                        }
                }
        }
 
-       if (created_by_local)
-       {
-               /* As spotted by jilles, dont bother to set this on remote users */
-               Ptr->SetDefaultModes();
-       }
-
-       return Channel::ForceChan(Ptr, user, privs, bursting, created_by_local);
+       // We figured that this join is allowed and also created the
+       // channel if it didn't exist before, now do the actual join
+       chan->ForceJoin(user, &privs, false, created_by_local);
+       return chan;
 }
 
-Channel* Channel::ForceChan(Channel* Ptr, User* user, const std::string &privs, bool bursting, bool created)
+Membership* Channel::ForceJoin(User* user, const std::string* privs, bool bursting, bool created_by_local)
 {
-       std::string nick = user->nick;
+       if (IS_SERVER(user))
+       {
+               ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join server user " + user->uuid + " to channel " + this->name);
+               return NULL;
+       }
+
+       Membership* memb = this->AddUser(user);
+       if (!memb)
+               return NULL; // Already on the channel
 
-       Membership* memb = Ptr->AddUser(user);
-       user->chans.insert(Ptr);
+       user->chans.push_front(memb);
 
-       for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
+       if (privs)
        {
-               const char status = *x;
-               ModeHandler* mh = ServerInstance->Modes->FindMode(status, MODETYPE_CHANNEL);
-               if (mh)
+               // If the user was granted prefix modes (in the OnUserPreJoin hook, or he's a
+               // remote user and his own server set the modes), then set them internally now
+               for (std::string::const_iterator i = privs->begin(); i != privs->end(); ++i)
                {
-                       /* Set, and make sure that the mode handler knows this mode was now set */
-                       Ptr->SetPrefix(user, mh->GetModeChar(), true);
-                       mh->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, Ptr, nick, true);
+                       PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
+                       if (mh)
+                       {
+                               std::string nick = user->nick;
+                               // Set the mode on the user
+                               mh->OnModeChange(ServerInstance->FakeClient, NULL, this, nick, true);
+                       }
                }
        }
 
+       // Tell modules about this join, they have the chance now to populate except_list with users we won't send the JOIN (and possibly MODE) to
        CUList except_list;
-       FOREACH_MOD(I_OnUserJoin,OnUserJoin(memb, bursting, created, except_list));
+       FOREACH_MOD(OnUserJoin, (memb, bursting, created_by_local, except_list));
 
-       Ptr->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", Ptr->name.c_str());
-
-       /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
-       if ((Ptr->GetUserCounter() > 1) && (!memb->modes.empty()))
-       {
-               std::string ms = memb->modes;
-               for(unsigned int i=0; i < memb->modes.length(); i++)
-                       ms.append(" ").append(user->nick);
-
-               except_list.insert(user);
-               Ptr->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
-       }
+       ClientProtocol::Events::Join joinevent(memb);
+       this->Write(joinevent, 0, except_list);
 
-       if (IS_LOCAL(user))
-       {
-               if (Ptr->topicset)
-               {
-                       user->WriteNumeric(RPL_TOPIC, "%s %s :%s", user->nick.c_str(), Ptr->name.c_str(), Ptr->topic.c_str());
-                       user->WriteNumeric(RPL_TOPICTIME, "%s %s %s %lu", user->nick.c_str(), Ptr->name.c_str(), Ptr->setby.c_str(), (unsigned long)Ptr->topicset);
-               }
-               Ptr->UserList(user);
-       }
-       FOREACH_MOD(I_OnPostJoin,OnPostJoin(memb));
-       return Ptr;
+       FOREACH_MOD(OnPostJoin, (memb));
+       return memb;
 }
 
 bool Channel::IsBanned(User* user)
@@ -431,10 +304,18 @@ bool Channel::IsBanned(User* user)
        if (result != MOD_RES_PASSTHRU)
                return (result == MOD_RES_DENY);
 
-       for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
+       ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
+       if (!banlm)
+               return false;
+
+       const ListModeBase::ModeList* bans = banlm->GetList(this);
+       if (bans)
        {
-               if (CheckBan(user, i->data))
-                       return true;
+               for (ListModeBase::ModeList::const_iterator it = bans->begin(); it != bans->end(); it++)
+               {
+                       if (CheckBan(user, it->mask))
+                               return true;
+               }
        }
        return false;
 }
@@ -454,14 +335,13 @@ bool Channel::CheckBan(User* user, const std::string& mask)
        if (at == std::string::npos)
                return false;
 
-       char tomatch[MAXBUF];
-       snprintf(tomatch, MAXBUF, "%s!%s", user->nick.c_str(), user->ident.c_str());
-       std::string prefix = mask.substr(0, at);
-       if (InspIRCd::Match(tomatch, prefix, NULL))
+       const std::string nickIdent = user->nick + "!" + user->ident;
+       std::string prefix(mask, 0, at);
+       if (InspIRCd::Match(nickIdent, prefix, NULL))
        {
-               std::string suffix = mask.substr(at + 1);
-               if (InspIRCd::Match(user->host, suffix, NULL) ||
-                       InspIRCd::Match(user->dhost, suffix, NULL) ||
+               std::string suffix(mask, at + 1);
+               if (InspIRCd::Match(user->GetRealHost(), suffix, NULL) ||
+                       InspIRCd::Match(user->GetDisplayedHost(), suffix, NULL) ||
                        InspIRCd::MatchCIDR(user->GetIPString(), suffix, NULL))
                        return true;
        }
@@ -474,12 +354,20 @@ ModResult Channel::GetExtBanStatus(User *user, char type)
        FIRST_MOD_RESULT(OnExtBanCheck, rv, (user, this, type));
        if (rv != MOD_RES_PASSTHRU)
                return rv;
-       for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
+
+       ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
+       if (!banlm)
+               return MOD_RES_PASSTHRU;
+
+       const ListModeBase::ModeList* bans = banlm->GetList(this);
+       if (bans)
        {
-               if (i->data.length() > 2 && i->data[0] == type && i->data[1] == ':')
+               for (ListModeBase::ModeList::const_iterator it = bans->begin(); it != bans->end(); ++it)
                {
-                       std::string val = i->data.substr(2);
-                       if (CheckBan(user, val))
+                       if (it->mask.length() <= 2 || it->mask[0] != type || it->mask[1] != ':')
+                               continue;
+
+                       if (CheckBan(user, it->mask.substr(2)))
                                return MOD_RES_DENY;
                }
        }
@@ -487,407 +375,124 @@ ModResult Channel::GetExtBanStatus(User *user, char type)
 }
 
 /* Channel::PartUser
- * remove a channel from a users record, and return the number of users left.
- * Therefore, if this function returns 0 the caller should delete the Channel.
+ * 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)
-{
-       if (!user)
-               return;
-
-       Membership* memb = GetUser(user);
-
-       if (memb)
-       {
-               CUList except_list;
-               FOREACH_MOD(I_OnUserPart,OnUserPart(memb, reason, except_list));
-
-               WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
-
-               user->chans.erase(this);
-               this->RemoveAllPrefixes(user);
-       }
-
-       this->DelUser(user);
-}
-
-void Channel::KickUser(User *src, User *user, const char* reason)
-{
-       if (!src || !user || !reason)
-               return;
-
-       Membership* memb = GetUser(user);
-       if (IS_LOCAL(src))
-       {
-               if (!memb)
-               {
-                       src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s %s :They are not on that channel",src->nick.c_str(), user->nick.c_str(), this->name.c_str());
-                       return;
-               }
-               if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
-               {
-                       src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only a u-line may kick a u-line from a channel.",src->nick.c_str(), this->name.c_str());
-                       return;
-               }
-
-               ModResult res;
-               if (ServerInstance->ULine(src->server))
-                       res = MOD_RES_ALLOW;
-               else
-                       FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
-
-               if (res == MOD_RES_DENY)
-                       return;
-
-               if (res == MOD_RES_PASSTHRU)
-               {
-                       unsigned int them = this->GetPrefixValue(src);
-                       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)
-                       {
-                               src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",
-                                       src->nick.c_str(), this->name.c_str(), req > HALFOP_VALUE ? "" : "half-");
-                               return;
-                       }
-               }
-       }
-
-       if (memb)
-       {
-               CUList except_list;
-               FOREACH_MOD(I_OnUserKick,OnUserKick(src, memb, reason, except_list));
-
-               WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), user->nick.c_str(), reason);
-
-               user->chans.erase(this);
-               this->RemoveAllPrefixes(user);
-       }
-
-       this->DelUser(user);
-}
-
-void Channel::WriteChannel(User* user, const char* text, ...)
-{
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       if (!user || !text)
-               return;
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteChannel(user, std::string(textbuffer));
-}
-
-void Channel::WriteChannel(User* user, const std::string &text)
-{
-       char tb[MAXBUF];
-
-       if (!user)
-               return;
-
-       snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
-       std::string out = tb;
-
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
-       {
-               if (IS_LOCAL(i->first))
-                       i->first->Write(out);
-       }
-}
-
-void Channel::WriteChannelWithServ(const std::string& ServName, const char* text, ...)
-{
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       if (!text)
-               return;
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteChannelWithServ(ServName, std::string(textbuffer));
-}
-
-void Channel::WriteChannelWithServ(const std::string& ServName, const std::string &text)
-{
-       char tb[MAXBUF];
-
-       snprintf(tb,MAXBUF,":%s %s", ServName.empty() ? ServerInstance->Config->ServerName.c_str() : ServName.c_str(), text.c_str());
-       std::string out = tb;
-
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
-       {
-               if (IS_LOCAL(i->first))
-                       i->first->Write(out);
-       }
-}
-
-/* write formatted text from a source user to all users on a channel except
- * for the sender (for privmsg etc) */
-void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
+bool Channel::PartUser(User* user, std::string& reason)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
+       MemberMap::iterator membiter = userlist.find(user);
 
-       if (!text)
-               return;
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
-}
-
-void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
-{
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
+       if (membiter == userlist.end())
+               return false;
 
-       if (!text)
-               return;
+       Membership* memb = membiter->second;
+       CUList except_list;
+       FOREACH_MOD(OnUserPart, (memb, reason, except_list));
 
-       int offset = snprintf(textbuffer,MAXBUF,":%s ", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str());
+       ClientProtocol::Messages::Part partmsg(memb, reason);
+       Write(ServerInstance->GetRFCEvents().part, partmsg, 0, except_list);
 
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer + offset, MAXBUF - offset, text, argsPtr);
-       va_end(argsPtr);
+       // 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);
 
-       this->RawWriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
+       return true;
 }
 
-void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
+void Channel::KickUser(User* src, const MemberMap::iterator& victimiter, const std::string& reason)
 {
-       char tb[MAXBUF];
+       Membership* memb = victimiter->second;
+       CUList except_list;
+       FOREACH_MOD(OnUserKick, (src, memb, reason, except_list));
 
-       snprintf(tb,MAXBUF,":%s %s", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str(), text.c_str());
+       ClientProtocol::Messages::Kick kickmsg(src, memb, reason);
+       Write(ServerInstance->GetRFCEvents().kick, kickmsg, 0, except_list);
 
-       this->RawWriteAllExcept(user, serversource, status, except_list, std::string(tb));
+       memb->user->chans.erase(memb);
+       this->DelUser(victimiter);
 }
 
-void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &out)
+void Channel::Write(ClientProtocol::Event& protoev, char status, const CUList& except_list)
 {
        unsigned int minrank = 0;
        if (status)
        {
-               ModeHandler* mh = ServerInstance->Modes->FindPrefix(status);
+               PrefixMode* mh = ServerInstance->Modes->FindPrefix(status);
                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()))
+               LocalUser* user = IS_LOCAL(i->first);
+               if ((user) && (!except_list.count(user)))
                {
                        /* User doesn't have the status we're after */
                        if (minrank && i->second->getRank() < minrank)
                                continue;
 
-                       i->first->Write(out);
+                       user->Send(protoev);
                }
        }
 }
 
-void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const std::string& text)
-{
-       CUList except_list;
-       except_list.insert(user);
-       this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
-}
-
-/*
- * return a count of the users on a specific channel accounting for
- * invisible users who won't increase the count. e.g. for /LIST
- */
-int Channel::CountInvisible()
-{
-       int count = 0;
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
-       {
-               if (!i->first->quitting && !i->first->IsModeSet('i'))
-                       count++;
-       }
-
-       return count;
-}
-
-char* Channel::ChanModes(bool showkey)
+const char* Channel::ChanModes(bool showsecret)
 {
-       static char scratch[MAXBUF];
-       static char sparam[MAXBUF];
-       char* offset = scratch;
-       std::string extparam;
+       static std::string scratch;
+       std::string sparam;
 
-       *scratch = '\0';
-       *sparam = '\0';
+       scratch.clear();
 
        /* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
        for(int n = 0; n < 64; n++)
        {
-               if(this->modes[n])
+               ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_CHANNEL);
+               if (mh && IsModeSet(mh))
                {
-                       *offset++ = n + 65;
-                       extparam.clear();
-                       if (n == 'k' - 65 && !showkey)
+                       scratch.push_back(n + 65);
+
+                       ParamModeBase* pm = mh->IsParameterMode();
+                       if (!pm)
+                               continue;
+
+                       if (pm->IsParameterSecret() && !showsecret)
                        {
-                               extparam = "<key>";
+                               sparam += " <" + pm->name + ">";
                        }
                        else
                        {
-                               extparam = this->GetModeParameter(n + 65);
-                       }
-                       if (!extparam.empty())
-                       {
-                               charlcat(sparam,' ',MAXBUF);
-                               strlcat(sparam,extparam.c_str(),MAXBUF);
+                               sparam += ' ';
+                               pm->GetParameter(this, sparam);
                        }
                }
        }
 
-       /* Null terminate scratch */
-       *offset = '\0';
-       strlcat(scratch,sparam,MAXBUF);
-       return scratch;
-}
-
-/* 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)
-{
-       if (!IS_LOCAL(user))
-               return;
-
-       bool has_privs = user->HasPrivPermission("channels/auspex");
-
-       if (this->IsModeSet('s') && !this->HasUser(user) && !has_privs)
-       {
-               user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
-               return;
-       }
-
-       std::string list = user->nick;
-       list.push_back(' ');
-       list.push_back(this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=');
-       list.push_back(' ');
-       list.append(this->name).append(" :");
-       std::string::size_type pos = list.size();
-
-       bool has_one = false;
-
-       /* Improvement by Brain - this doesnt change in value, so why was it inside
-        * the loop?
-        */
-       bool has_user = this->HasUser(user);
-
-       const size_t maxlen = MAXBUF - 10 - ServerInstance->Config->ServerName.size();
-       std::string prefixlist;
-       std::string nick;
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); ++i)
-       {
-               if (i->first->quitting)
-                       continue;
-               if ((!has_user) && (i->first->IsModeSet('i')) && (!has_privs))
-               {
-                       /*
-                        * user is +i, and source not on the channel, does not show
-                        * nick in NAMES list
-                        */
-                       continue;
-               }
-
-               prefixlist = this->GetPrefixChar(i->first);
-               nick = i->first->nick;
-
-               FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
-
-               /* Nick was nuked, a module wants us to skip it */
-               if (nick.empty())
-                       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);
-                       has_one = false;
-               }
-
-               list.append(prefixlist).append(nick).push_back(' ');
-
-               has_one = true;
-       }
-
-       /* if whats left in the list isnt empty, send it */
-       if (has_one)
-       {
-               user->WriteNumeric(RPL_NAMREPLY, list);
-       }
-
-       user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
-}
-
-long Channel::GetMaxBans()
-{
-       /* Return the cached value if there is one */
-       if (this->maxbans)
-               return this->maxbans;
-
-       /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
-       for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
-       {
-               if (InspIRCd::Match(this->name, n->first, NULL))
-               {
-                       this->maxbans = n->second;
-                       return n->second;
-               }
-       }
-
-       /* Screw it, just return the default of 64 */
-       this->maxbans = 64;
-       return this->maxbans;
+       scratch += sparam;
+       return scratch.c_str();
 }
 
-void Channel::ResetMaxBans()
+void Channel::WriteNotice(const std::string& text)
 {
-       this->maxbans = 0;
+       ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, this, text, MSG_NOTICE);
+       Write(ServerInstance->GetRFCEvents().privmsg, privmsg);
 }
 
 /* returns the status character for a given user on a channel, e.g. @ for op,
  * % for halfop etc. If the user has several modes set, the highest mode
  * the user has must be returned.
  */
-const char* Channel::GetPrefixChar(User *user)
+char Membership::GetPrefixChar() const
 {
-       static char pf[2] = {0, 0};
-       *pf = 0;
+       char pf = 0;
        unsigned int bestrank = 0;
 
-       UserMembIter m = userlist.find(user);
-       if (m != userlist.end())
+       for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
        {
-               for(unsigned int i=0; i < m->second->modes.length(); i++)
+               PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
+               if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
                {
-                       char mchar = m->second->modes[i];
-                       ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
-                       if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
-                       {
-                               bestrank = mh->GetPrefixRank();
-                               pf[0] = mh->GetPrefix();
-                       }
+                       bestrank = mh->GetPrefixRank();
+                       pf = mh->GetPrefix();
                }
        }
        return pf;
@@ -899,160 +504,61 @@ unsigned int Membership::getRank()
        unsigned int rv = 0;
        if (mchar)
        {
-               ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
+               PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(mchar);
                if (mh)
                        rv = mh->GetPrefixRank();
        }
        return rv;
 }
 
-const char* Channel::GetAllPrefixChars(User* user)
+std::string Membership::GetAllPrefixChars() const
 {
-       static char prefix[64];
-       int ctr = 0;
-
-       UserMembIter m = userlist.find(user);
-       if (m != userlist.end())
+       std::string ret;
+       for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
        {
-               for(unsigned int i=0; i < m->second->modes.length(); i++)
-               {
-                       char mchar = m->second->modes[i];
-                       ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
-                       if (mh && mh->GetPrefix())
-                               prefix[ctr++] = mh->GetPrefix();
-               }
+               PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
+               if (mh && 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();
 }
 
-bool Channel::SetPrefix(User* user, char prefix, bool adding)
+bool Membership::SetPrefix(PrefixMode* delta_mh, bool adding)
 {
-       ModeHandler* delta_mh = ServerInstance->Modes->FindMode(prefix, MODETYPE_CHANNEL);
-       if (!delta_mh)
-               return false;
-       UserMembIter m = userlist.find(user);
-       if (m == userlist.end())
-               return false;
-       for(unsigned int i=0; i < m->second->modes.length(); i++)
+       char prefix = delta_mh->GetModeChar();
+       for (unsigned int i = 0; i < modes.length(); i++)
        {
-               char mchar = m->second->modes[i];
-               ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
+               char mchar = modes[i];
+               PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(mchar);
                if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank())
                {
-                       m->second->modes =
-                               m->second->modes.substr(0,i) +
+                       modes = modes.substr(0,i) +
                                (adding ? std::string(1, prefix) : "") +
-                               m->second->modes.substr(mchar == prefix ? i+1 : i);
+                               modes.substr(mchar == prefix ? i+1 : i);
                        return adding != (mchar == prefix);
                }
        }
        if (adding)
-               m->second->modes += std::string(1, prefix);
+               modes.push_back(prefix);
        return adding;
 }
 
-void Channel::RemoveAllPrefixes(User* user)
-{
-       UserMembIter m = userlist.find(user);
-       if (m != userlist.end())
-       {
-               m->second->modes.clear();
-       }
-}
 
-void Invitation::Create(Channel* c, LocalUser* u, time_t timeout)
+void Membership::WriteNotice(const std::string& text) const
 {
-       if ((timeout != 0) && (ServerInstance->Time() >= timeout))
-               // Expired, don't bother
+       LocalUser* const localuser = IS_LOCAL(user);
+       if (!localuser)
                return;
 
-       ServerInstance->Logs->Log("INVITATION", 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", DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
-       }
-       else
-       {
-               inv = new Invitation(c, u, timeout);
-               c->invites.push_back(inv);
-               u->invites.push_back(inv);
-               ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
-       }
-}
-
-Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired)
-{
-       ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find chan=%s user=%s check_expired=%d", c ? c->name.c_str() : "NULL", u ? u->uuid.c_str() : "NULL", check_expired);
-       if (!u || u->invites.empty())
-               return NULL;
-
-       InviteList locallist;
-       locallist.swap(u->invites);
-
-       Invitation* result = NULL;
-       for (InviteList::iterator i = locallist.begin(); i != locallist.end(); )
-       {
-               Invitation* inv = *i;
-               if ((check_expired) && (inv->expiry != 0) && (inv->expiry <= ServerInstance->Time()))
-               {
-                       /* Expired invite, remove it. */
-                       std::string expiration = ServerInstance->TimeString(inv->expiry);
-                       ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
-                       i = locallist.erase(i);
-                       inv->cull();
-                       delete inv;
-               }
-               else
-               {
-                       /* Is it what we're searching for? */
-                       if (inv->chan == c)
-                       {
-                               result = inv;
-                               break;
-                       }
-                       ++i;
-               }
-       }
-
-       locallist.swap(u->invites);
-       ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find result=%p", (void*) result);
-       return result;
-}
-
-Invitation::~Invitation()
-{
-       // Remove this entry from both lists
-       InviteList::iterator it = std::find(chan->invites.begin(), chan->invites.end(), this);
-       if (it != chan->invites.end())
-               chan->invites.erase(it);
-       it = std::find(user->invites.begin(), user->invites.end(), this);
-       if (it != user->invites.end())
-               user->invites.erase(it);
-}
-
-void InviteBase::ClearInvites()
-{
-       ServerInstance->Logs->Log("INVITEBASE", DEBUG, "InviteBase::ClearInvites %p", (void*) this);
-       InviteList locallist;
-       locallist.swap(invites);
-       for (InviteList::const_iterator i = locallist.begin(); i != locallist.end(); ++i)
-       {
-               (*i)->cull();
-               delete *i;
-       }
+       ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, this->chan, text, MSG_NOTICE);
+       localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
 }
index b245a15527e7a25077f855ceae2df3c0be33e52c..3776114372b2d5361ecfc9cdf4a91b1536aa3bfb 100644 (file)
@@ -19,8 +19,6 @@
  */
 
 
-/* $Core */
-
 #include "inspircd.h"
 
 /* Match CIDR strings, e.g. 127.0.0.1 to 127.0.0.0/8 or 3ffe:1:5:6::8 to 3ffe:1::0/32
@@ -55,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
@@ -75,12 +73,14 @@ bool irc::sockets::MatchCIDR(const std::string &address, const std::string &cidr
        }
 
        irc::sockets::sockaddrs addr;
-       irc::sockets::aptosa(address_copy, 0, addr);
+       if (!irc::sockets::aptosa(address_copy, 0, addr))
+       {
+               // The address could not be parsed.
+               return false;
+       }
 
        irc::sockets::cidr_mask mask(cidr_copy);
        irc::sockets::cidr_mask mask2(addr, mask.length);
 
        return mask == mask2;
 }
-
-
diff --git a/src/clientprotocol.cpp b/src/clientprotocol.cpp
new file mode 100644 (file)
index 0000000..ee3909f
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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"
+
+ClientProtocol::Serializer::Serializer(Module* mod, const char* Name)
+       : DataProvider(mod, std::string("serializer/") + Name)
+       , evprov(mod, "event/messagetag")
+{
+}
+
+bool ClientProtocol::Serializer::HandleTag(LocalUser* user, const std::string& tagname, std::string& tagvalue, TagMap& tags) const
+{
+       // Catch and block empty tags
+       if (tagname.empty())
+               return false;
+
+       const ::Events::ModuleEventProvider::SubscriberList& list = evprov.GetSubscribers();
+       for (::Events::ModuleEventProvider::SubscriberList::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               MessageTagProvider* const tagprov = static_cast<MessageTagProvider*>(*i);
+               const ModResult res = tagprov->OnProcessTag(user, tagname, tagvalue);
+               if (res == MOD_RES_ALLOW)
+                       return tags.insert(std::make_pair(tagname, MessageTagData(tagprov, tagvalue))).second;
+               else if (res == MOD_RES_DENY)
+                       break;
+       }
+
+       // No module handles the tag but that's not an error
+       return true;
+}
+
+ClientProtocol::TagSelection ClientProtocol::Serializer::MakeTagWhitelist(LocalUser* user, const TagMap& tagmap) const
+{
+       TagSelection tagwl;
+       for (TagMap::const_iterator i = tagmap.begin(); i != tagmap.end(); ++i)
+       {
+               const MessageTagData& tagdata = i->second;
+               if (tagdata.tagprov->ShouldSendTag(user, tagdata))
+                       tagwl.Select(tagmap, i);
+       }
+       return tagwl;
+}
+
+const ClientProtocol::SerializedMessage& ClientProtocol::Serializer::SerializeForUser(LocalUser* user, Message& msg)
+{
+       if (!msg.msginit_done)
+       {
+               msg.msginit_done = true;
+               FOREACH_MOD_CUSTOM(evprov, MessageTagProvider, OnPopulateTags, (msg));
+       }
+       return msg.GetSerialized(Message::SerializedInfo(this, MakeTagWhitelist(user, msg.GetTags())));
+}
+
+const ClientProtocol::SerializedMessage& ClientProtocol::Message::GetSerialized(const SerializedInfo& serializeinfo) const
+{
+       // First check if the serialized line they're asking for is in the cache
+       for (SerializedList::const_iterator i = serlist.begin(); i != serlist.end(); ++i)
+       {
+               const SerializedInfo& curr = i->first;
+               if (curr == serializeinfo)
+                       return i->second;
+       }
+
+       // Not cached, generate it and put it in the cache for later use
+       serlist.push_back(std::make_pair(serializeinfo, serializeinfo.serializer->Serialize(*this, serializeinfo.tagwl)));
+       return serlist.back().second;
+}
+
+void ClientProtocol::Event::GetMessagesForUser(LocalUser* user, MessageList& messagelist)
+{
+       if (!eventinit_done)
+       {
+               eventinit_done = true;
+               FOREACH_MOD_CUSTOM(*event, EventHook, OnEventInit, (*this));
+       }
+
+       // Most of the time there's only a single message but in rare cases there are more
+       if (initialmsg)
+               messagelist.assign(1, initialmsg);
+       else
+               messagelist = *initialmsglist;
+
+       // Let modules modify the message list
+       ModResult res;
+       FIRST_MOD_RESULT_CUSTOM(*event, EventHook, OnPreEventSend, res, (user, *this, messagelist));
+       if (res == MOD_RES_DENY)
+               messagelist.clear();
+}
index 53f19437bfa3bf7dc5764510d483453f7b68eb49..57db5d496c3dc99f3852e7a00be08e622e753fc9 100644 (file)
 
 #include "inspircd.h"
 
-int InspIRCd::PassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype)
+bool InspIRCd::PassCompare(Extensible* ex, const std::string& data, const std::string& input, const std::string& hashtype)
 {
        ModResult res;
        FIRST_MOD_RESULT(OnPassCompare, res, (ex, data, input, hashtype));
 
        /* Module matched */
        if (res == MOD_RES_ALLOW)
-               return 0;
+               return true;
 
        /* Module explicitly didnt match */
        if (res == MOD_RES_DENY)
-               return 1;
+               return false;
 
        /* We dont handle any hash types except for plaintext - Thanks tra26 */
        if (!hashtype.empty() && hashtype != "plaintext")
-               /* See below. 1 because they dont match */
-               return 1;
+               return false;
 
-       return (data != input); // this seems back to front, but returns 0 if they *match*, 1 else
+       return TimingSafeCompare(data, input);
 }
 
-/* LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
- * There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
- * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
- * the channel names and their keys as follows:
- * JOIN #chan1,#chan2,#chan3 key1,,key3
- * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
- * two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
- * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
- * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
- */
-int CommandParser::LoopCall(User* user, Command* CommandObj, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
+bool CommandParser::LoopCall(User* user, Command* handler, const CommandBase::Params& parameters, unsigned int splithere, int extra, bool usemax)
 {
        if (splithere >= parameters.size())
-               return 0;
-
-       if (extra >= (signed)parameters.size())
-               extra = -1;
+               return false;
 
-       /* First check if we have more than one item in the list, if we don't we return zero here and the handler
+       /* First check if we have more than one item in the list, if we don't we return false here and the handler
         * which called us just carries on as it was.
         */
        if (parameters[splithere].find(',') == std::string::npos)
-               return 0;
+               return false;
 
        /** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm.
         * By using std::set (thanks for the idea w00t) we can cut this down a ton.
         * ...VOOODOOOO!
+        *
+        * 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 lists, one for channel names, one for keys
+       /* Create two sepstreams, if we have only one list, then initialize the second sepstream with
+        * an empty string. The second parameter of the constructor of the sepstream tells whether
+        * or not to allow empty tokens.
+        * We allow empty keys, so "JOIN #a,#b ,bkey" will be interpreted as "JOIN #a", "JOIN #b bkey"
         */
        irc::commasepstream items1(parameters[splithere]);
-       irc::commasepstream items2(extra >= 0 ? parameters[extra] : "");
-       std::string extrastuff;
+       irc::commasepstream items2(extra >= 0 ? parameters[extra] : "", true);
        std::string item;
        unsigned int max = 0;
+       LocalUser* localuser = IS_LOCAL(user);
 
-       /* Attempt to iterate these lists and call the command objech
-        * which called us, for every parameter pair until there are
-        * no more left to parse.
+       /* Attempt to iterate these lists and call the command handler
+        * for every parameter or parameter pair until there are no more
+        * left to parse.
         */
        while (items1.GetToken(item) && (!usemax || max++ < ServerInstance->Config->MaxTargets))
        {
-               if (dupes.find(item.c_str()) == dupes.end())
+               if ((!check_dupes) || (dupes.insert(item).second))
                {
-                       std::vector<std::string> new_parameters(parameters);
-
-                       if (!items2.GetToken(extrastuff))
-                               extrastuff.clear();
-
+                       CommandBase::Params new_parameters(parameters);
                        new_parameters[splithere] = item;
-                       if (extra >= 0)
-                               new_parameters[extra] = extrastuff;
 
-                       CommandObj->Handle(new_parameters, user);
-
-                       dupes.insert(item.c_str());
-               }
-       }
-       return 1;
-}
-
-bool CommandParser::IsValidCommand(const std::string &commandname, unsigned int pcnt, User * user)
-{
-       Commandtable::iterator n = cmdlist.find(commandname);
-
-       if (n != cmdlist.end())
-       {
-               if ((pcnt >= n->second->min_params))
-               {
-                       if (IS_LOCAL(user) && n->second->flags_needed)
+                       if (extra >= 0)
                        {
-                               if (user->IsModeSet(n->second->flags_needed))
-                               {
-                                       return (user->HasPermission(commandname));
-                               }
+                               // If we have two lists then get the next item from the second list.
+                               // In case it runs out of elements then 'item' will be an empty string.
+                               items2.GetToken(item);
+                               new_parameters[extra] = item;
                        }
-                       else
+
+                       CommandBase::Params params(new_parameters, parameters.GetTags());
+                       CmdResult result = handler->Handle(user, params);
+                       if (localuser)
                        {
-                               return true;
+                               // Run the OnPostCommand hook with the last parameter being true to indicate
+                               // that the event is being called in a loop.
+                               item.clear();
+                               FOREACH_MOD(OnPostCommand, (handler, new_parameters, localuser, result, true));
                        }
                }
        }
-       return false;
+
+       return true;
 }
 
 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;
 
@@ -142,9 +119,9 @@ Command* CommandParser::GetHandler(const std::string &commandname)
 
 // calls a handler function for a command
 
-CmdResult CommandParser::CallHandler(const std::string &commandname, const std::vector<std::string>& parameters, User *user)
+CmdResult CommandParser::CallHandler(const std::string& commandname, const CommandBase::Params& parameters, User* user, Command** cmd)
 {
-       Commandtable::iterator n = cmdlist.find(commandname);
+       CommandMap::iterator n = cmdlist.find(commandname);
 
        if (n != cmdlist.end())
        {
@@ -162,7 +139,7 @@ CmdResult CommandParser::CallHandler(const std::string &commandname, const std::
                                if (user->IsModeSet(n->second->flags_needed))
                                {
                                        /* if user has the flags, and now has the permissions, go ahead */
-                                       if (user->HasPermission(commandname))
+                                       if (user->HasCommandPermission(commandname))
                                                bOkay = true;
                                }
                        }
@@ -174,49 +151,30 @@ CmdResult CommandParser::CallHandler(const std::string &commandname, const std::
 
                        if (bOkay)
                        {
-                               return n->second->Handle(parameters,user);
+                               if (cmd)
+                                       *cmd = n->second;
+
+                               ClientProtocol::TagMap tags;
+                               return n->second->Handle(user, CommandBase::Params(parameters, tags));
                        }
                }
        }
        return CMD_INVALID;
 }
 
-bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
+void CommandParser::ProcessCommand(LocalUser* user, std::string& command, CommandBase::Params& command_p)
 {
-       std::vector<std::string> command_p;
-       irc::tokenstream tokens(cmd);
-       std::string command, token;
-       tokens.GetToken(command);
-
-       /* A client sent a nick prefix on their command (ick)
-        * rhapsody and some braindead bouncers do this --
-        * the rfc says they shouldnt but also says the ircd should
-        * discard it if they do.
-        */
-       if ((command.empty() || command[0] == ':') && !tokens.GetToken(command))
-       {
-               // Penalise the user to discourage them from spamming the server with trash.
-               user->CommandFloodPenalty += 2000;
-               return false;
-       }
-
-       while (tokens.GetToken(token) && (command_p.size() <= MAXPARAMETERS))
-               command_p.push_back(token);
-
-       std::transform(command.begin(), command.end(), command.begin(), ::toupper);
-
        /* find the command, check it exists */
-       Commandtable::iterator cm = cmdlist.find(command);
+       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 */
-       bool do_more = true;
        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
-               unsigned int penalty = (cm != cmdlist.end() ? cm->second->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
@@ -226,13 +184,12 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
                        failpenalty = 1000;
        }
 
-
-       if (cm == cmdlist.end())
+       if (!handler)
        {
                ModResult MOD_RESULT;
-               FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
+               FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false));
                if (MOD_RESULT == MOD_RES_DENY)
-                       return true;
+                       return;
 
                /*
                 * This double lookup is in case a module (abbreviation) wishes to change a command.
@@ -241,17 +198,19 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
                 * Thanks dz for making me actually understand why this is necessary!
                 * -- w00t
                 */
-               cm = cmdlist.find(command);
-               if (cm == cmdlist.end())
+               handler = GetHandler(command);
+               if (!handler)
                {
                        if (user->registered == REG_ALL)
-                               user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
-                       ServerInstance->stats->statsUnknown++;
-                       return true;
+                               user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "Unknown command");
+                       ServerInstance->stats.Unknown++;
+                       return;
                }
        }
 
-       if (cm->second->max_params && command_p.size() > cm->second->max_params)
+       // If we were given more parameters than max_params then append the excess parameter(s)
+       // to command_p[maxparams-1], i.e. to the last param that is still allowed
+       if (handler->max_params && command_p.size() > handler->max_params)
        {
                /*
                 * command_p input (assuming max_params 1):
@@ -260,32 +219,21 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
                 *      a
                 *      test
                 */
-               std::string lparam;
 
-               /*
-                * The '-1' here is a clever trick, we'll go backwards throwing everything into a temporary param
-                * and then just toss that into the array.
-                * -- w00t
-                */
-               while (command_p.size() > (cm->second->max_params - 1))
-               {
-                       // BE CAREFUL: .end() returns past the end of the vector, hence decrement.
-                       std::vector<std::string>::iterator it = command_p.end() - 1;
+               // Iterator to the last parameter that will be kept
+               const CommandBase::Params::iterator lastkeep = command_p.begin() + (handler->max_params - 1);
+               // Iterator to the first excess parameter
+               const CommandBase::Params::iterator firstexcess = lastkeep + 1;
 
-                       lparam.insert(0, " " + *(it));
-                       command_p.erase(it); // remove last element
+               // Append all excess parameter(s) to the last parameter, seperated by spaces
+               for (CommandBase::Params::const_iterator i = firstexcess; i != command_p.end(); ++i)
+               {
+                       lastkeep->push_back(' ');
+                       lastkeep->append(*i);
                }
 
-               /* we now have (each iteration):
-                *      ' test'
-                *      ' a test'
-                *      ' is a test' <-- final string
-                * ...now remove the ' ' at the start...
-                */
-               lparam.erase(lparam.begin());
-
-               /* param is now 'is a test', which is exactly what we wanted! */
-               command_p.push_back(lparam);
+               // Erase the excess parameter(s)
+               command_p.erase(firstexcess, command_p.end());
        }
 
        /*
@@ -293,106 +241,127 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
         * truncate to max_params if necessary. -- w00t
         */
        ModResult MOD_RESULT;
-       FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
+       FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false));
        if (MOD_RESULT == MOD_RES_DENY)
-               return true;
+               return;
 
        /* activity resets the ping pending timer */
-       user->nping = ServerInstance->Time() + user->MyClass->GetPingTime();
+       user->nextping = ServerInstance->Time() + user->MyClass->GetPingTime();
 
-       if (cm->second->flags_needed)
+       if (handler->flags_needed)
        {
-               if (!user->IsModeSet(cm->second->flags_needed))
+               if (!user->IsModeSet(handler->flags_needed))
                {
                        user->CommandFloodPenalty += failpenalty;
-                       user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - You do not have the required operator privileges",user->nick.c_str());
-                       return do_more;
+                       user->WriteNumeric(ERR_NOPRIVILEGES, "Permission Denied - You do not have the required operator privileges");
+                       return;
                }
-               if (!user->HasPermission(command))
+
+               if (!user->HasCommandPermission(command))
                {
                        user->CommandFloodPenalty += failpenalty;
-                       user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to command %s",
-                               user->nick.c_str(), user->oper->NameStr(), command.c_str());
-                       return do_more;
-               }
-       }
-       if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled()))
-       {
-               /* command is disabled! */
-               user->CommandFloodPenalty += failpenalty;
-               if (ServerInstance->Config->DisabledDontExist)
-               {
-                       user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
+                       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;
                }
-               else
-               {
-                       user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :This command has been disabled.",
-                                                                               user->nick.c_str(), command.c_str());
-               }
-
-               ServerInstance->SNO->WriteToSnoMask('a', "%s denied for %s (%s@%s)",
-                               command.c_str(), user->nick.c_str(), user->ident.c_str(), user->host.c_str());
-               return do_more;
        }
 
-       if ((!command_p.empty()) && (command_p.back().empty()) && (!cm->second->allow_empty_last_param))
+       if ((!command_p.empty()) && (command_p.back().empty()) && (!handler->allow_empty_last_param))
                command_p.pop_back();
 
-       if (command_p.size() < cm->second->min_params)
+       if (command_p.size() < handler->min_params)
        {
                user->CommandFloodPenalty += failpenalty;
-               user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s %s :Not enough parameters.", user->nick.c_str(), command.c_str());
-               if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (cm->second->syntax.length()))
-                       user->WriteNumeric(RPL_SYNTAX, "%s :SYNTAX %s %s", user->nick.c_str(), cm->second->name.c_str(), cm->second->syntax.c_str());
-               return do_more;
+               user->WriteNumeric(ERR_NEEDMOREPARAMS, command, "Not enough parameters.");
+               if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (handler->syntax.length()))
+                       user->WriteNumeric(RPL_SYNTAX, handler->name, handler->syntax);
+               return;
        }
-       if ((user->registered != REG_ALL) && (!cm->second->WorksBeforeReg()))
+
+       if ((user->registered != REG_ALL) && (!handler->WorksBeforeReg()))
        {
                user->CommandFloodPenalty += failpenalty;
-               user->WriteNumeric(ERR_NOTREGISTERED, "%s %s :You have not registered", user->nick.c_str(), command.c_str());
-               return do_more;
+               user->WriteNumeric(ERR_NOTREGISTERED, command, "You have not registered");
        }
        else
        {
                /* passed all checks.. first, do the (ugly) stats counters. */
-               cm->second->use_count++;
-               cm->second->total_bytes += cmd.length();
+               handler->use_count++;
 
                /* module calls too */
-               FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, true, cmd));
+               FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, true));
                if (MOD_RESULT == MOD_RES_DENY)
-                       return do_more;
+                       return;
 
                /*
                 * WARNING: be careful, the user may be deleted soon
                 */
-               CmdResult result = cm->second->Handle(command_p, user);
+               CmdResult result = handler->Handle(user, command_p);
 
-               FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, user, result,cmd));
-               return do_more;
+               FOREACH_MOD(OnPostCommand, (handler, command_p, user, result, false));
        }
 }
 
 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)
+       , works_before_reg(false)
+       , allow_empty_last_param(true)
+       , Penalty(1)
+{
+}
+
+CommandBase::~CommandBase()
+{
+}
+
+void CommandBase::EncodeParameter(std::string& parameter, unsigned int index)
+{
+}
+
+RouteDescriptor CommandBase::GetRouting(User* user, const Params& 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);
 }
 
-bool CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
+void Command::RegisterService()
 {
-       if (!user || buffer.empty())
-               return true;
+       if (!ServerInstance->Parser.AddCommand(this))
+               throw ModuleException("Command already exists: " + name);
+}
+
+void CommandParser::ProcessBuffer(LocalUser* user, const std::string& buffer)
+{
+       ClientProtocol::ParseOutput parseoutput;
+       if (!user->serializer->Parse(user, buffer, parseoutput))
+               return;
+
+       std::string& command = parseoutput.cmd;
+       std::transform(command.begin(), command.end(), command.begin(), ::toupper);
 
-       ServerInstance->Logs->Log("USERINPUT", RAWIO, "C[%s] I :%s %s",
-               user->uuid.c_str(), user->nick.c_str(), buffer.c_str());
-       return ProcessCommand(user,buffer);
+       CommandBase::Params parameters(parseoutput.params, parseoutput.tags);
+       ProcessCommand(user, command, parameters);
 }
 
 bool CommandParser::AddCommand(Command *f)
@@ -410,88 +379,64 @@ CommandParser::CommandParser()
 {
 }
 
-int CommandParser::TranslateUIDs(const std::vector<TranslateType> to, const std::vector<std::string> &source, std::string &dest, bool prefix_final, Command* custom_translator)
+std::string CommandParser::TranslateUIDs(const std::vector<TranslateType>& to, const CommandBase::Params& source, bool prefix_final, CommandBase* custom_translator)
 {
        std::vector<TranslateType>::const_iterator types = to.begin();
-       User* user = NULL;
-       unsigned int i;
-       int translations = 0;
-       dest.clear();
+       std::string dest;
 
-       for(i=0; i < source.size(); i++)
+       for (unsigned int i = 0; i < source.size(); i++)
        {
-               TranslateType t;
-               std::string item = source[i];
-
-               if (types == to.end())
-                       t = TR_TEXT;
-               else
+               TranslateType t = TR_TEXT;
+               // They might supply less translation types than parameters,
+               // in that case pretend that all remaining types are TR_TEXT
+               if (types != to.end())
                {
                        t = *types;
                        types++;
                }
 
-               if (prefix_final && i == source.size() - 1)
-                       dest.append(":");
+               bool last = (i == (source.size() - 1));
+               if (prefix_final && last)
+                       dest.push_back(':');
 
-               switch (t)
-               {
-                       case TR_NICK:
-                               /* Translate single nickname */
-                               user = ServerInstance->FindNick(item);
-                               if (user)
-                               {
-                                       dest.append(user->uuid);
-                                       translations++;
-                               }
-                               else
-                                       dest.append(item);
-                       break;
-                       case TR_CUSTOM:
-                               if (custom_translator)
-                                       custom_translator->EncodeParameter(item, i);
-                               dest.append(item);
-                       break;
-                       case TR_END:
-                       case TR_TEXT:
-                       default:
-                               /* Do nothing */
-                               dest.append(item);
-                       break;
-               }
-               if (i != source.size() - 1)
-                       dest.append(" ");
+               TranslateSingleParam(t, source[i], dest, custom_translator, i);
+
+               if (!last)
+                       dest.push_back(' ');
        }
 
-       return translations;
+       return dest;
 }
 
-int CommandParser::TranslateUIDs(TranslateType to, const std::string &source, std::string &dest)
+void CommandParser::TranslateSingleParam(TranslateType to, const std::string& item, std::string& dest, CommandBase* custom_translator, unsigned int paramnumber)
 {
-       User* user = NULL;
-       int translations = 0;
-       dest.clear();
-
        switch (to)
        {
                case TR_NICK:
+               {
                        /* Translate single nickname */
-                       user = ServerInstance->FindNick(source);
+                       User* user = ServerInstance->FindNick(item);
                        if (user)
+                               dest.append(user->uuid);
+                       else
+                               dest.append(item);
+                       break;
+               }
+               case TR_CUSTOM:
+               {
+                       if (custom_translator)
                        {
-                               dest = user->uuid;
-                               translations++;
+                               std::string translated = item;
+                               custom_translator->EncodeParameter(translated, paramnumber);
+                               dest.append(translated);
+                               break;
                        }
-                       else
-                               dest = source;
-               break;
-               case TR_END:
-               case TR_TEXT:
+                       // If no custom translator was given, fall through
+               }
+               /*@fallthrough@*/
                default:
                        /* Do nothing */
-                       dest = source;
+                       dest.append(item);
                break;
        }
-
-       return translations;
 }
index 5bf2aeb03a420e024289d0478ed05b2fe4419195..c5b34c72f0e655d3c8a64e557c75252e5141ace1 100644 (file)
  */
 
 
-/* $Core */
-
 #include "inspircd.h"
-#include "xline.h"
-#include "command_parse.h"
-
-/* All other ircds when doing this check usually just look for a string of *@* or *. We're smarter than that, though. */
 
-bool InspIRCd::HostMatchesEveryone(const std::string &mask, User* user)
+CmdResult SplitCommand::Handle(User* user, const Params& parameters)
 {
-       long matches = 0;
-
-       ConfigTag* insane = Config->ConfValue("insane");
-
-       if (insane->getBool("hostmasks"))
-               return false;
-
-       float itrigger = insane->getFloat("trigger", 95.5);
-
-       for (user_hash::iterator u = this->Users->clientlist->begin(); u != this->Users->clientlist->end(); u++)
+       switch (user->usertype)
        {
-               if ((InspIRCd::MatchCIDR(u->second->MakeHost(), mask, ascii_case_insensitive_map)) ||
-                   (InspIRCd::MatchCIDR(u->second->MakeHostIP(), mask, ascii_case_insensitive_map)))
-               {
-                       matches++;
-               }
-       }
+               case USERTYPE_LOCAL:
+                       return HandleLocal(static_cast<LocalUser*>(user), parameters);
 
-       if (!matches)
-               return false;
+               case USERTYPE_REMOTE:
+                       return HandleRemote(static_cast<RemoteUser*>(user), parameters);
 
-       float percent = ((float)matches / (float)this->Users->clientlist->size()) * 100;
-       if (percent > itrigger)
-       {
-               SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a G/K/E line mask of %s, which covers %.2f%% of the network!",user->nick.c_str(),mask.c_str(),percent);
-               return true;
+               case USERTYPE_SERVER:
+                       return HandleServer(static_cast<FakeUser*>(user), parameters);
        }
-       return false;
-}
 
-bool InspIRCd::IPMatchesEveryone(const std::string &ip, User* user)
-{
-       long matches = 0;
-
-       ConfigTag* insane = Config->ConfValue("insane");
-
-       if (insane->getBool("ipmasks"))
-               return false;
-
-       float itrigger = insane->getFloat("trigger", 95.5);
-
-       for (user_hash::iterator u = this->Users->clientlist->begin(); u != this->Users->clientlist->end(); u++)
-       {
-               if (InspIRCd::MatchCIDR(u->second->GetIPString(), ip, ascii_case_insensitive_map))
-                       matches++;
-       }
-
-       if (!matches)
-               return false;
-
-       float percent = ((float)matches / (float)this->Users->clientlist->size()) * 100;
-       if (percent > itrigger)
-       {
-               SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a Z line mask of %s, which covers %.2f%% of the network!",user->nick.c_str(),ip.c_str(),percent);
-               return true;
-       }
-       return false;
-}
-
-bool InspIRCd::NickMatchesEveryone(const std::string &nick, User* user)
-{
-       long matches = 0;
-
-       ConfigTag* insane = Config->ConfValue("insane");
-
-       if (insane->getBool("nickmasks"))
-               return false;
-
-       float itrigger = insane->getFloat("trigger", 95.5);
-
-       for (user_hash::iterator u = this->Users->clientlist->begin(); u != this->Users->clientlist->end(); u++)
-       {
-               if (InspIRCd::Match(u->second->nick, nick))
-                       matches++;
-       }
-
-       if (!matches)
-               return false;
-
-       float percent = ((float)matches / (float)this->Users->clientlist->size()) * 100;
-       if (percent > itrigger)
-       {
-               SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a Q line mask of %s, which covers %.2f%% of the network!",user->nick.c_str(),nick.c_str(),percent);
-               return true;
-       }
-       return false;
-}
-
-CmdResult SplitCommand::Handle(const std::vector<std::string>& parms, User* u)
-{
-       if (IS_LOCAL(u))
-               return HandleLocal(parms, IS_LOCAL(u));
-       if (IS_REMOTE(u))
-               return HandleRemote(parms, IS_REMOTE(u));
-       if (IS_SERVER(u))
-               return HandleServer(parms, IS_SERVER(u));
-       ServerInstance->Logs->Log("COMMAND", DEFAULT, "Unknown user type in command (uuid=%s)!", u->uuid.c_str());
+       ServerInstance->Logs->Log("COMMAND", LOG_DEFAULT, "Unknown user type %d in command (uuid=%s)!",
+               user->usertype, user->uuid.c_str());
        return CMD_INVALID;
 }
 
-CmdResult SplitCommand::HandleLocal(const std::vector<std::string>&, LocalUser*)
+CmdResult SplitCommand::HandleLocal(LocalUser* user, const Params& parameters)
 {
        return CMD_INVALID;
 }
 
-CmdResult SplitCommand::HandleRemote(const std::vector<std::string>&, RemoteUser*)
+CmdResult SplitCommand::HandleRemote(RemoteUser* user, const Params& parameters)
 {
        return CMD_INVALID;
 }
 
-CmdResult SplitCommand::HandleServer(const std::vector<std::string>&, FakeUser*)
+CmdResult SplitCommand::HandleServer(FakeUser* user, const Params& parameters)
 {
        return CMD_INVALID;
 }
-
diff --git a/src/commands/cmd_admin.cpp b/src/commands/cmd_admin.cpp
deleted file mode 100644 (file)
index 0d6c235..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /ADMIN. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandAdmin : public Command
-{
- public:
-       /** Constructor for admin.
-        */
-       CommandAdmin(Module* parent) : Command(parent,"ADMIN",0,0)
-       {
-               Penalty = 2;
-               syntax = "[<servername>]";
-       }
-
-       /** 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);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               if (parameters.size() > 0)
-                       return ROUTE_UNICAST(parameters[0]);
-               return ROUTE_LOCALONLY;
-       }
-};
-
-/** Handle /ADMIN
- */
-CmdResult CommandAdmin::Handle (const std::vector<std::string>& parameters, User *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());
-       if (!ServerInstance->Config->AdminName.empty())
-               user->SendText(":%s %03d %s :Name     - %s", ServerInstance->Config->ServerName.c_str(),
-                       RPL_ADMINLOC1, user->nick.c_str(), ServerInstance->Config->AdminName.c_str());
-       user->SendText(":%s %03d %s :Nickname - %s", ServerInstance->Config->ServerName.c_str(),
-               RPL_ADMINLOC2, user->nick.c_str(), ServerInstance->Config->AdminNick.c_str());
-       user->SendText(":%s %03d %s :E-Mail   - %s", ServerInstance->Config->ServerName.c_str(),
-               RPL_ADMINEMAIL, user->nick.c_str(), ServerInstance->Config->AdminEmail.c_str());
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandAdmin)
diff --git a/src/commands/cmd_away.cpp b/src/commands/cmd_away.cpp
deleted file mode 100644 (file)
index fa3f7fa..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /AWAY. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandAway : public Command
-{
- public:
-       /** Constructor for away.
-        */
-       CommandAway ( Module* parent) : Command(parent,"AWAY",0,0) { syntax = "[<message>]"; }
-       /** 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);
-};
-
-/** Handle /AWAY
- */
-CmdResult CommandAway::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       ModResult MOD_RESULT;
-
-       if ((parameters.size()) && (!parameters[0].empty()))
-       {
-               FIRST_MOD_RESULT(OnSetAway, MOD_RESULT, (user, parameters[0]));
-
-               if (MOD_RESULT == MOD_RES_DENY && IS_LOCAL(user))
-                       return CMD_FAILURE;
-
-               user->awaytime = ServerInstance->Time();
-               user->awaymsg.assign(parameters[0], 0, ServerInstance->Config->Limits.MaxAway);
-
-               user->WriteNumeric(RPL_NOWAWAY, "%s :You have been marked as being away",user->nick.c_str());
-       }
-       else
-       {
-               FIRST_MOD_RESULT(OnSetAway, MOD_RESULT, (user, ""));
-
-               if (MOD_RESULT == MOD_RES_DENY && IS_LOCAL(user))
-                       return CMD_FAILURE;
-
-               user->awaymsg.clear();
-               user->WriteNumeric(RPL_UNAWAY, "%s :You are no longer marked as being away",user->nick.c_str());
-       }
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandAway)
diff --git a/src/commands/cmd_clearcache.cpp b/src/commands/cmd_clearcache.cpp
deleted file mode 100644 (file)
index 5914f9a..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /CLEARCACHE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandClearcache : public Command
-{
- public:
-       /** Constructor for clearcache.
-        */
-       CommandClearcache ( Module* parent) : Command(parent,"CLEARCACHE",0) { flags_needed = 'o'; }
-       /** 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);
-};
-
-/** Handle /CLEARCACHE
- */
-CmdResult CommandClearcache::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       int n = ServerInstance->Res->ClearCache();
-       user->WriteServ("NOTICE %s :*** Cleared DNS cache of %d items.", user->nick.c_str(), n);
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandClearcache)
diff --git a/src/commands/cmd_commands.cpp b/src/commands/cmd_commands.cpp
deleted file mode 100644 (file)
index 1555b4d..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /COMMANDS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandCommands : public Command
-{
- public:
-       /** Constructor for commands.
-        */
-       CommandCommands(Module* parent) : Command(parent,"COMMANDS",0,0)
-       {
-               Penalty = 3;
-       }
-
-       /** 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);
-};
-
-/** Handle /COMMANDS
- */
-CmdResult CommandCommands::Handle (const std::vector<std::string>&, User *user)
-{
-       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++)
-       {
-               // Don't show S2S commands to users
-               if (i->second->flags_needed == FLAG_SERVERONLY)
-                       continue;
-
-               Module* src = i->second->creator;
-               char buffer[MAXBUF];
-               snprintf(buffer, MAXBUF, ":%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(),
-                       i->second->min_params, i->second->Penalty);
-               list.push_back(buffer);
-       }
-       sort(list.begin(), list.end());
-       for(unsigned int i=0; i < list.size(); i++)
-               user->Write(list[i]);
-       user->WriteNumeric(RPL_COMMANDSEND, "%s :End of COMMANDS list",user->nick.c_str());
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandCommands)
diff --git a/src/commands/cmd_connect.cpp b/src/commands/cmd_connect.cpp
deleted file mode 100644 (file)
index 8fb82fa..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /CONNECT. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandConnect : public Command
-{
- public:
-       /** Constructor for connect.
-        */
-       CommandConnect ( Module* parent) : Command(parent,"CONNECT",1) { flags_needed = 'o'; syntax = "<servername>"; }
-       /** 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);
-};
-
-/*
- * This is handled by the server linking module, if necessary. Do not remove this stub.
- */
-
-/** Handle /CONNECT
- */
-CmdResult CommandConnect::Handle (const std::vector<std::string>&, 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());
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandConnect)
diff --git a/src/commands/cmd_die.cpp b/src/commands/cmd_die.cpp
deleted file mode 100644 (file)
index 1d66402..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /DIE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandDie : public Command
-{
- public:
-       /** Constructor for die.
-        */
-       CommandDie ( Module* parent) : Command(parent,"DIE",1) { flags_needed = 'o'; syntax = "<password>"; }
-       /** 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);
-};
-
-#include "exitcodes.h"
-
-/** Handle /DIE
- */
-CmdResult CommandDie::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       if (!ServerInstance->PassCompare(user, ServerInstance->Config->diepass, parameters[0].c_str(), ServerInstance->Config->powerhash))
-       {
-               {
-                       std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating.";
-                       ServerInstance->Logs->Log("COMMAND",SPARSE, diebuf);
-                       ServerInstance->SendError(diebuf);
-               }
-
-               ServerInstance->Exit(EXIT_STATUS_DIE);
-       }
-       else
-       {
-               ServerInstance->Logs->Log("COMMAND",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;
-       }
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandDie)
diff --git a/src/commands/cmd_eline.cpp b/src/commands/cmd_eline.cpp
deleted file mode 100644 (file)
index ca39f90..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 "xline.h"
-
-/** Handle /ELINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandEline : public Command
-{
- public:
-       /** Constructor for eline.
-        */
-       CommandEline ( Module* parent) : Command(parent,"ELINE",1,3) { flags_needed = 'o'; syntax = "<ident@host> [<duration> :<reason>]"; }
-       /** 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);
-};
-
-/** Handle /ELINE
- */
-CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       std::string target = parameters[0];
-
-       if (parameters.size() >= 3)
-       {
-               IdentHostPair ih;
-               User* find = ServerInstance->FindNick(target);
-               if ((find) && (find->registered == REG_ALL))
-               {
-                       ih.first = "*";
-                       ih.second = find->GetIPString();
-                       target = std::string("*@") + find->GetIPString();
-               }
-               else
-                       ih = ServerInstance->XLines->IdentSplit(target);
-
-        if (ih.first.empty())
-        {
-            user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
-            return CMD_FAILURE;
-        }
-
-               if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
-                       return CMD_FAILURE;
-
-               long duration = ServerInstance->Duration(parameters[1].c_str());
-
-               ELine* el = new ELine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
-               if (ServerInstance->XLines->AddLine(el, user))
-               {
-                       if (!duration)
-                       {
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent E-line for %s: %s", user->nick.c_str(), target.c_str(), parameters[2].c_str());
-                       }
-                       else
-                       {
-                               time_t c_requires_crap = duration + ServerInstance->Time();
-                               std::string timestr = ServerInstance->TimeString(c_requires_crap);
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s added timed E-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
-                                               timestr.c_str(), parameters[2].c_str());
-                       }
-               }
-               else
-               {
-                       delete el;
-                       user->WriteServ("NOTICE %s :*** E-Line for %s already exists",user->nick.c_str(),target.c_str());
-               }
-       }
-       else
-       {
-               if (ServerInstance->XLines->DelLine(target.c_str(), "E", user))
-               {
-                       ServerInstance->SNO->WriteToSnoMask('x',"%s removed E-line on %s",user->nick.c_str(),target.c_str());
-               }
-               else
-               {
-                       user->WriteServ("NOTICE %s :*** E-Line %s not found in list, try /stats e.",user->nick.c_str(),target.c_str());
-               }
-       }
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandEline)
diff --git a/src/commands/cmd_gline.cpp b/src/commands/cmd_gline.cpp
deleted file mode 100644 (file)
index 6505b74..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 "xline.h"
-
-/** Handle /GLINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandGline : public Command
-{
- public:
-       /** Constructor for gline.
-        */
-       CommandGline (Module* parent) : Command(parent,"GLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<ident@host> [<duration> :<reason>]"; }
-       /** 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);
-};
-
-
-/** Handle /GLINE
- */
-CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       std::string target = parameters[0];
-
-       if (parameters.size() >= 3)
-       {
-               IdentHostPair ih;
-               User* find = ServerInstance->FindNick(target);
-               if ((find) && (find->registered == REG_ALL))
-               {
-                       ih.first = "*";
-                       ih.second = find->GetIPString();
-                       target = std::string("*@") + find->GetIPString();
-               }
-               else
-                       ih = ServerInstance->XLines->IdentSplit(target);
-
-               if (ih.first.empty())
-               {
-                       user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
-                       return CMD_FAILURE;
-               }
-
-               if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
-                       return CMD_FAILURE;
-
-               else if (target.find('!') != std::string::npos)
-               {
-                       user->WriteServ("NOTICE %s :*** G-Line cannot operate on nick!user@host masks",user->nick.c_str());
-                       return CMD_FAILURE;
-               }
-
-               long duration = ServerInstance->Duration(parameters[1].c_str());
-               GLine* gl = new GLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
-               if (ServerInstance->XLines->AddLine(gl, user))
-               {
-                       if (!duration)
-                       {
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent G-line for %s: %s",user->nick.c_str(),target.c_str(), parameters[2].c_str());
-                       }
-                       else
-                       {
-                               time_t c_requires_crap = duration + ServerInstance->Time();
-                               std::string timestr = ServerInstance->TimeString(c_requires_crap);
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s added timed G-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
-                                               timestr.c_str(), parameters[2].c_str());
-                       }
-
-                       ServerInstance->XLines->ApplyLines();
-               }
-               else
-               {
-                       delete gl;
-                       user->WriteServ("NOTICE %s :*** G-Line for %s already exists",user->nick.c_str(),target.c_str());
-               }
-
-       }
-       else
-       {
-               if (ServerInstance->XLines->DelLine(target.c_str(),"G",user))
-               {
-                       ServerInstance->SNO->WriteToSnoMask('x',"%s removed G-line on %s",user->nick.c_str(),target.c_str());
-               }
-               else
-               {
-                       user->WriteServ("NOTICE %s :*** G-line %s not found in list, try /stats g.",user->nick.c_str(),target.c_str());
-               }
-       }
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandGline)
diff --git a/src/commands/cmd_info.cpp b/src/commands/cmd_info.cpp
deleted file mode 100644 (file)
index 76e414b..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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) 2008 Thomas Stagner <aquanight@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"
-
-/** Handle /INFO. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandInfo : public Command
-{
- public:
-       /** Constructor for info.
-        */
-       CommandInfo(Module* parent) : Command(parent,"INFO")
-       {
-               Penalty = 4;
-               syntax = "[<servername>]";
-       }
-
-       /** 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);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               if (parameters.size() > 0)
-                       return ROUTE_UNICAST(parameters[0]);
-               return ROUTE_LOCALONLY;
-       }
-};
-
-static const char* const lines[] = {
-       "                   -/\\- \2InspIRCd\2 -\\/-",
-       "                 November 2002 - Present",
-       " ",
-       "\2Core Developers\2:",
-       "    Craig Edwards,          Brain,      <brain@inspircd.org>",
-       "    Craig McLure,           Craig,      <craig@inspircd.org>",
-       "    Robin Burchell,         w00t,       <w00t@inspircd.org>",
-       "    Oliver Lupton,          Om,         <om@inspircd.org>",
-       "    John Brooks,            Special,    <special@inspircd.org>",
-       "    Dennis Friis,           peavey,     <peavey@inspircd.org>",
-       "    Thomas Stagner,         aquanight,  <aquanight@inspircd.org>",
-       "    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",
-       " ",
-       "\2Other Contributors\2:",
-       "    ChrisTX        Shawn           Shutter",
-       " ",
-       "\2Former Contributors\2:",
-       "   dmb             Zaba            skenmy         GreenReaper",
-       "   Dan             Jason           satmd          owine",
-       "   Adremelech      John2           jilles         HiroP",
-       "   eggy            Bricker         AnMaster       djGrrr",
-       "   nenolod         Quension        praetorian     pippijn",
-       "   CC              jamie           typobox43      Burlex (win32)",
-       "   Stskeeps        ThaPrince       BuildSmart     Thunderhacker",
-       "   Skip            LeaChim         Majic          MacGyver",
-       "   Namegduf        Ankit           Phoenix        Taros",
-       " ",
-       "\2Thanks To\2:",
-       "   searchirc.com   irc-junkie.org  Brik           fraggeln",
-       " ",
-       " Best experienced with: \2An IRC client\2",
-       NULL
-};
-
-/** Handle /INFO
- */
-CmdResult CommandInfo::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
-               return CMD_SUCCESS;
-
-       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++]);
-       FOREACH_MOD(I_OnInfo,OnInfo(user));
-       user->SendText(":%s %03d %s :End of /INFO list", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFINFO, user->nick.c_str());
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandInfo)
diff --git a/src/commands/cmd_invite.cpp b/src/commands/cmd_invite.cpp
deleted file mode 100644 (file)
index c69e6bd..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2008 Thomas Stagner <aquanight@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"
-
-/** Handle /INVITE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandInvite : public Command
-{
- public:
-       /** Constructor for invite.
-        */
-       CommandInvite ( Module* parent) : Command(parent,"INVITE", 0, 0) { Penalty = 4; syntax = "[<nick> <channel>]"; }
-       /** 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);
-};
-
-/** Handle /INVITE
- */
-CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       ModResult MOD_RESULT;
-
-       if (parameters.size() == 2 || parameters.size() == 3)
-       {
-               User* u;
-               if (IS_LOCAL(user))
-                       u = ServerInstance->FindNickOnly(parameters[0]);
-               else
-                       u = ServerInstance->FindNick(parameters[0]);
-
-               Channel* c = ServerInstance->FindChan(parameters[1]);
-               time_t timeout = 0;
-               if (parameters.size() == 3)
-               {
-                       if (IS_LOCAL(user))
-                               timeout = ServerInstance->Time() + ServerInstance->Duration(parameters[2]);
-                       else
-                               timeout = ConvToInt(parameters[2]);
-               }
-
-               if ((!c) || (!u) || (u->registered != REG_ALL))
-               {
-                       user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), c ? parameters[0].c_str() : parameters[1].c_str());
-                       return CMD_FAILURE;
-               }
-
-               if ((IS_LOCAL(user)) && (!c->HasUser(user)))
-               {
-                       user->WriteNumeric(ERR_NOTONCHANNEL, "%s %s :You're not on that channel!",user->nick.c_str(), c->name.c_str());
-                       return CMD_FAILURE;
-               }
-
-               if (c->HasUser(u))
-               {
-                       user->WriteNumeric(ERR_USERONCHANNEL, "%s %s %s :is already on channel",user->nick.c_str(),u->nick.c_str(),c->name.c_str());
-                       return CMD_FAILURE;
-               }
-
-               FIRST_MOD_RESULT(OnUserPreInvite, MOD_RESULT, (user,u,c,timeout));
-
-               if (MOD_RESULT == MOD_RES_DENY)
-               {
-                       return CMD_FAILURE;
-               }
-               else if (MOD_RESULT == MOD_RES_PASSTHRU)
-               {
-                       if (IS_LOCAL(user))
-                       {
-                               unsigned int rank = c->GetPrefixValue(user);
-                               if (rank < HALFOP_VALUE)
-                               {
-                                       // Check whether halfop mode is available and phrase error message accordingly
-                                       ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
-                                       user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",
-                                               user->nick.c_str(), c->name.c_str(), (mh && mh->name == "halfop" ? "half-" : ""));
-                                       return CMD_FAILURE;
-                               }
-                       }
-               }
-
-               if (IS_LOCAL(u))
-                       IS_LOCAL(u)->InviteTo(c->name.c_str(), timeout);
-               u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str());
-               user->WriteNumeric(RPL_INVITING, "%s %s %s",user->nick.c_str(),u->nick.c_str(),c->name.c_str());
-               if (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE)
-               {
-                       char prefix;
-                       switch (ServerInstance->Config->AnnounceInvites)
-                       {
-                               case ServerConfig::INVITE_ANNOUNCE_OPS:
-                               {
-                                       prefix = '@';
-                                       break;
-                               }
-                               case ServerConfig::INVITE_ANNOUNCE_DYNAMIC:
-                               {
-                                       ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
-                                       prefix = (mh && mh->name == "halfop" ? mh->GetPrefix() : '@');
-                                       break;
-                               }
-                               default:
-                               {
-                                       prefix = 0;
-                                       break;
-                               }
-                       }
-                       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(I_OnUserInvite,OnUserInvite(user,u,c,timeout));
-       }
-       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)
-               {
-                       user->WriteNumeric(RPL_INVITELIST, "%s :%s",user->nick.c_str(), (*i)->chan->name.c_str());
-               }
-               user->WriteNumeric(RPL_ENDOFINVITELIST, "%s :End of INVITE list",user->nick.c_str());
-       }
-       return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandInvite)
diff --git a/src/commands/cmd_ison.cpp b/src/commands/cmd_ison.cpp
deleted file mode 100644 (file)
index 01d12e1..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /ISON. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandIson : public Command
-{
- public:
-       /** Constructor for ison.
-        */
-       CommandIson ( Module* parent) : Command(parent,"ISON", 1) {
-               syntax = "<nick> {nick}";
-       }
-       /** 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);
-};
-
-/** Handle /ISON
- */
-CmdResult CommandIson::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       std::map<User*,User*> ison_already;
-       User *u;
-       std::string reply = "303 " + user->nick + " :";
-
-       for (unsigned int i = 0; i < parameters.size(); i++)
-       {
-               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;
-
-                               while (list.GetToken(item))
-                               {
-                                       u = ServerInstance->FindNickOnly(item);
-                                       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;
-                                       }
-                               }
-                       }
-               }
-       }
-
-       if (!reply.empty())
-               user->WriteServ(reply);
-
-       return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandIson)
diff --git a/src/commands/cmd_join.cpp b/src/commands/cmd_join.cpp
deleted file mode 100644 (file)
index 6124fcc..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /JOIN. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandJoin : public Command
-{
- public:
-       /** Constructor for join.
-        */
-       CommandJoin ( Module* parent) : Command(parent,"JOIN", 1, 2) { syntax = "<channel>{,<channel>} {<key>{,<key>}}"; Penalty = 2; }
-       /** 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);
-};
-
-/** Handle /JOIN
- */
-CmdResult CommandJoin::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       if (parameters.size() > 1)
-       {
-               if (ServerInstance->Parser->LoopCall(user, this, parameters, 0, 1, false))
-                       return CMD_SUCCESS;
-
-               if (ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
-               {
-                       Channel::JoinUser(user, parameters[0].c_str(), false, parameters[1].c_str(), false);
-                       return CMD_SUCCESS;
-               }
-       }
-       else
-       {
-               if (ServerInstance->Parser->LoopCall(user, this, parameters, 0, -1, false))
-                       return CMD_SUCCESS;
-
-               if (ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
-               {
-                       Channel::JoinUser(user, parameters[0].c_str(), false, "", false);
-                       return CMD_SUCCESS;
-               }
-       }
-
-       user->WriteNumeric(ERR_NOSUCHCHANNEL, "%s %s :Invalid channel name",user->nick.c_str(), parameters[0].c_str());
-       return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandJoin)
diff --git a/src/commands/cmd_kick.cpp b/src/commands/cmd_kick.cpp
deleted file mode 100644 (file)
index c497dd4..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /KICK. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandKick : public Command
-{
- public:
-       /** Constructor for kick.
-        */
-       CommandKick ( Module* parent) : Command(parent,"KICK",2,3) { syntax = "<channel> <nick>{,<nick>} [<reason>]"; }
-       /** 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);
-};
-
-/** Handle /KICK
- */
-CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       std::string reason;
-       Channel* c = ServerInstance->FindChan(parameters[0]);
-       User* u;
-
-       if (ServerInstance->Parser->LoopCall(user, this, parameters, 1))
-               return CMD_SUCCESS;
-
-       if (IS_LOCAL(user))
-               u = ServerInstance->FindNickOnly(parameters[1]);
-       else
-               u = ServerInstance->FindNick(parameters[1]);
-
-       if ((!u) || (!c) || (u->registered != REG_ALL))
-       {
-               user->WriteServ( "401 %s %s :No such nick/channel", user->nick.c_str(), c ? parameters[1].c_str() : parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-
-       if ((IS_LOCAL(user)) && (!c->HasUser(user)) && (!ServerInstance->ULine(user->server)))
-       {
-               user->WriteServ( "442 %s %s :You're not on that channel!", user->nick.c_str(), parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-
-       if (parameters.size() > 2)
-       {
-               reason.assign(parameters[2], 0, ServerInstance->Config->Limits.MaxKick);
-       }
-       else
-       {
-               reason.assign(user->nick, 0, ServerInstance->Config->Limits.MaxKick);
-       }
-
-       c->KickUser(user, u, reason.c_str());
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandKick)
diff --git a/src/commands/cmd_kill.cpp b/src/commands/cmd_kill.cpp
deleted file mode 100644 (file)
index 7bdf32c..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /KILL. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandKill : public Command
-{
- public:
-       /** Constructor for kill.
-        */
-       CommandKill ( Module* parent) : Command(parent,"KILL",2,2) {
-               flags_needed = 'o';
-               syntax = "<nickname> <reason>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
-       }
-       /** 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);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               // local kills of remote users are routed via the OnRemoteKill hook
-               if (IS_LOCAL(user))
-                       return ROUTE_LOCALONLY;
-               return ROUTE_BROADCAST;
-       }
-};
-
-/** Handle /KILL
- */
-CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       /* Allow comma seperated lists of users for /KILL (thanks w00t) */
-       if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
-               return CMD_SUCCESS;
-
-       User *u = ServerInstance->FindNick(parameters[0]);
-       if ((u) && (!IS_SERVER(u)))
-       {
-               /*
-                * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
-                * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got.
-                *
-                * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill
-                * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
-                */
-
-               std::string killreason;
-               if (IS_LOCAL(user))
-               {
-                       /*
-                        * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user
-                        * and the other half not. This would be a bad thing. ;p -- w00t
-                        */
-                       ModResult MOD_RESULT;
-                       FIRST_MOD_RESULT(OnKill, MOD_RESULT, (user, u, parameters[1]));
-
-                       if (MOD_RESULT == MOD_RES_DENY)
-                               return CMD_FAILURE;
-
-                       killreason = "Killed (";
-                       if (!ServerInstance->Config->HideKillsServer.empty())
-                       {
-                               // hidekills is on, use it
-                               killreason += ServerInstance->Config->HideKillsServer;
-                       }
-                       else
-                       {
-                               // hidekills is off, do nothing
-                               killreason += user->nick;
-                       }
-
-                       killreason += " (" + parameters[1] + "))";
-               }
-               else
-               {
-                       /* Leave it alone, remote server has already formatted it */
-                       killreason.assign(parameters[1], 0, ServerInstance->Config->Limits.MaxQuit);
-               }
-
-               /*
-                * Now we need to decide whether or not to send a local or remote snotice. Currently this checking is a little flawed.
-                * No time to fix it right now, so left a note. -- w00t
-                */
-               if (!IS_LOCAL(u))
-               {
-                       // remote kill
-                       if (!ServerInstance->Config->HideULineKills || !ServerInstance->ULine(user->server))
-                               ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
-                       FOREACH_MOD(I_OnRemoteKill, OnRemoteKill(user, u, killreason, killreason));
-               }
-               else
-               {
-                       // local kill
-                       /*
-                        * 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 (!ServerInstance->Config->HideULineKills || !ServerInstance->ULine(user->server))
-                       {
-                               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());
-                               else
-                                       ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
-                       }
-                       ServerInstance->Logs->Log("KILL",DEFAULT,"LOCAL KILL: %s :%s!%s!%s (%s)", u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), user->nick.c_str(), parameters[1].c_str());
-                       /* Bug #419, make sure this message can only occur once even in the case of multiple KILL messages crossing the network, and change to show
-                        * hidekillsserver as source if possible
-                        */
-                       if (!u->quitting)
-                       {
-                               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(),
-                                               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());
-                       }
-               }
-
-               // send the quit out
-               ServerInstance->Users->QuitUser(u, killreason);
-       }
-       else
-       {
-               user->WriteServ( "401 %s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-
-       return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandKill)
diff --git a/src/commands/cmd_kline.cpp b/src/commands/cmd_kline.cpp
deleted file mode 100644 (file)
index ce3642f..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 "xline.h"
-
-/** Handle /KLINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandKline : public Command
-{
- public:
-       /** Constructor for kline.
-        */
-       CommandKline ( Module* parent) : Command(parent,"KLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<ident@host> [<duration> :<reason>]"; }
-       /** 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);
-};
-
-
-/** Handle /KLINE
- */
-CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User *user)
-{
-    std::string target = parameters[0];
-
-       if (parameters.size() >= 3)
-       {
-               IdentHostPair ih;
-               User* find = ServerInstance->FindNick(target);
-               if ((find) && (find->registered == REG_ALL))
-               {
-                       ih.first = "*";
-                       ih.second = find->GetIPString();
-                       target = std::string("*@") + find->GetIPString();
-               }
-               else
-                       ih = ServerInstance->XLines->IdentSplit(target);
-
-        if (ih.first.empty())
-        {
-            user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
-            return CMD_FAILURE;
-        }
-
-               if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
-                       return CMD_FAILURE;
-
-               if (target.find('!') != std::string::npos)
-               {
-                       user->WriteServ("NOTICE %s :*** K-Line cannot operate on nick!user@host masks",user->nick.c_str());
-                       return CMD_FAILURE;
-               }
-
-               long duration = ServerInstance->Duration(parameters[1].c_str());
-               KLine* kl = new KLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
-               if (ServerInstance->XLines->AddLine(kl,user))
-               {
-                       if (!duration)
-                       {
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent K-line for %s: %s",user->nick.c_str(),target.c_str(), parameters[2].c_str());
-                       }
-                       else
-                       {
-                               time_t c_requires_crap = duration + ServerInstance->Time();
-                               std::string timestr = ServerInstance->TimeString(c_requires_crap);
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s added timed K-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
-                                               timestr.c_str(), parameters[2].c_str());
-                       }
-
-                       ServerInstance->XLines->ApplyLines();
-               }
-               else
-               {
-                       delete kl;
-                       user->WriteServ("NOTICE %s :*** K-Line for %s already exists",user->nick.c_str(),target.c_str());
-               }
-       }
-       else
-       {
-               if (ServerInstance->XLines->DelLine(target.c_str(),"K",user))
-               {
-                       ServerInstance->SNO->WriteToSnoMask('x',"%s removed K-line on %s",user->nick.c_str(),target.c_str());
-               }
-               else
-               {
-                       user->WriteServ("NOTICE %s :*** K-Line %s not found in list, try /stats k.",user->nick.c_str(),target.c_str());
-               }
-       }
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandKline)
diff --git a/src/commands/cmd_links.cpp b/src/commands/cmd_links.cpp
deleted file mode 100644 (file)
index f4152eb..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /LINKS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandLinks : public Command
-{
- public:
-       /** Constructor for links.
-        */
-       CommandLinks ( Module* parent) : Command(parent,"LINKS",0,0) { }
-       /** 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);
-};
-
-/** Handle /LINKS
- */
-CmdResult CommandLinks::Handle (const std::vector<std::string>&, User *user)
-{
-       user->WriteNumeric(364, "%s %s %s :0 %s",user->nick.c_str(),ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerDesc.c_str());
-       user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick.c_str());
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandLinks)
diff --git a/src/commands/cmd_list.cpp b/src/commands/cmd_list.cpp
deleted file mode 100644 (file)
index eb28fb8..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /LIST. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandList : public Command
-{
- public:
-       /** Constructor for list.
-        */
-       CommandList ( Module* parent) : Command(parent,"LIST", 0, 0) { Penalty = 5; }
-       /** 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);
-};
-
-
-/** Handle /LIST
- */
-CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       int minusers = 0, maxusers = 0;
-
-       user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str());
-
-       if ((parameters.size() == 1) && (!parameters[0].empty()))
-       {
-               if (parameters[0][0] == '<')
-               {
-                       maxusers = atoi((parameters[0].c_str())+1);
-               }
-               else if (parameters[0][0] == '>')
-               {
-                       minusers = atoi((parameters[0].c_str())+1);
-               }
-       }
-
-       for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
-       {
-               // attempt to match a glob pattern
-               long users = i->second->GetUserCounter();
-
-               bool too_few = (minusers && (users <= minusers));
-               bool too_many = (maxusers && (users >= maxusers));
-
-               if (too_many || too_few)
-                       continue;
-
-               if (parameters.size() && !parameters[0].empty() && (parameters[0][0] != '<' && parameters[0][0] != '>'))
-               {
-                       if (!InspIRCd::Match(i->second->name, parameters[0]) && !InspIRCd::Match(i->second->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"));
-
-               // If we're not in the channel and +s is set on it, we want to ignore it
-               if (n || !i->second->IsModeSet('s'))
-               {
-                       if (!n && i->second->IsModeSet('p'))
-                       {
-                               /* Channel is +p and user is outside/not privileged */
-                               user->WriteNumeric(322, "%s * %ld :",user->nick.c_str(), users);
-                       }
-                       else
-                       {
-                               /* User is in the channel/privileged, channel is not +s */
-                               user->WriteNumeric(322, "%s %s %ld :[+%s] %s",user->nick.c_str(),i->second->name.c_str(),users,i->second->ChanModes(n),i->second->topic.c_str());
-                       }
-               }
-       }
-       user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
-
-       return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandList)
diff --git a/src/commands/cmd_loadmodule.cpp b/src/commands/cmd_loadmodule.cpp
deleted file mode 100644 (file)
index 379e597..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /LOADMODULE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandLoadmodule : public Command
-{
- public:
-       /** Constructor for loadmodule.
-        */
-       CommandLoadmodule ( Module* parent) : Command(parent,"LOADMODULE",1,1) { flags_needed='o'; syntax = "<modulename>"; }
-       /** 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);
-};
-
-/** Handle /LOADMODULE
- */
-CmdResult CommandLoadmodule::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       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(975, "%s %s :Module successfully loaded.",user->nick.c_str(), parameters[0].c_str());
-               return CMD_SUCCESS;
-       }
-       else
-       {
-               user->WriteNumeric(974, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
-               return CMD_FAILURE;
-       }
-}
-
-COMMAND_INIT(CommandLoadmodule)
diff --git a/src/commands/cmd_lusers.cpp b/src/commands/cmd_lusers.cpp
deleted file mode 100644 (file)
index 91a7180..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-struct LusersCounters
-{
-       unsigned int max_local;
-       unsigned int max_global;
-       unsigned int invisible;
-
-       LusersCounters()
-               : max_local(ServerInstance->Users->LocalUserCount())
-               , max_global(ServerInstance->Users->RegisteredUserCount())
-               , invisible(ServerInstance->Users->ModeCount('i'))
-       {
-       }
-
-       inline void UpdateMaxUsers()
-       {
-               unsigned int current = ServerInstance->Users->LocalUserCount();
-               if (current > max_local)
-                       max_local = current;
-
-               current = ServerInstance->Users->RegisteredUserCount();
-               if (current > max_global)
-                       max_global = current;
-       }
-};
-
-/** Handle /LUSERS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandLusers : public Command
-{
-       LusersCounters& counters;
- public:
-       /** Constructor for lusers.
-        */
-       CommandLusers(Module* parent, LusersCounters& Counters)
-               : Command(parent,"LUSERS",0,0), counters(Counters)
-       { }
-       /** 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);
-};
-
-/** Handle /LUSERS
- */
-CmdResult CommandLusers::Handle (const std::vector<std::string>&, User *user)
-{
-       unsigned int n_users = ServerInstance->Users->RegisteredUserCount();
-       ProtoServerList serverlist;
-       ServerInstance->PI->GetServerList(serverlist);
-       unsigned int n_serv = serverlist.size();
-       unsigned int n_local_servs = 0;
-       for(ProtoServerList::iterator i = serverlist.begin(); i != serverlist.end(); ++i)
-       {
-               if (i->parentname == ServerInstance->Config->ServerName)
-                       n_local_servs++;
-       }
-       // fix for default GetServerList not returning us
-       if (!n_serv)
-               n_serv = 1;
-
-       counters.UpdateMaxUsers();
-
-       user->WriteNumeric(251, "%s :There are %d users and %d invisible on %d servers",user->nick.c_str(),
-                       n_users - counters.invisible, counters.invisible, n_serv);
-
-       if (ServerInstance->Users->OperCount())
-               user->WriteNumeric(252, "%s %d :operator(s) online",user->nick.c_str(),ServerInstance->Users->OperCount());
-
-       if (ServerInstance->Users->UnregisteredUserCount())
-               user->WriteNumeric(253, "%s %d :unknown connections",user->nick.c_str(),ServerInstance->Users->UnregisteredUserCount());
-
-       user->WriteNumeric(254, "%s %ld :channels formed",user->nick.c_str(),ServerInstance->ChannelCount());
-       user->WriteNumeric(255, "%s :I have %d clients and %d servers",user->nick.c_str(),ServerInstance->Users->LocalUserCount(),n_local_servs);
-       user->WriteNumeric(265, "%s :Current Local Users: %d  Max: %d", user->nick.c_str(), ServerInstance->Users->LocalUserCount(), counters.max_local);
-       user->WriteNumeric(266, "%s :Current Global Users: %d  Max: %d", user->nick.c_str(), n_users, counters.max_global);
-
-       return CMD_SUCCESS;
-}
-
-class InvisibleWatcher : public ModeWatcher
-{
-       unsigned int& invisible;
-public:
-       InvisibleWatcher(Module* mod, unsigned int& Invisible)
-               : ModeWatcher(mod, 'i', MODETYPE_USER), invisible(Invisible)
-       {
-       }
-
-       void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding, ModeType type)
-       {
-               if (dest->registered != REG_ALL)
-                       return;
-
-               if (adding)
-                       invisible++;
-               else
-                       invisible--;
-       }
-};
-
-class ModuleLusers : public Module
-{
-       LusersCounters counters;
-       CommandLusers cmd;
-       InvisibleWatcher mw;
-
- public:
-       ModuleLusers()
-               : cmd(this, counters), mw(this, counters.invisible)
-       {
-       }
-
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-               Implementation events[] = { I_OnPostConnect, I_OnUserQuit };
-               ServerInstance->Modules->Attach(events, this, sizeof(events)/sizeof(Implementation));
-               ServerInstance->Modes->AddModeWatcher(&mw);
-       }
-
-       void OnPostConnect(User* user)
-       {
-               counters.UpdateMaxUsers();
-               if (user->IsModeSet('i'))
-                       counters.invisible++;
-       }
-
-       void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
-       {
-               if (user->IsModeSet('i'))
-                       counters.invisible--;
-       }
-
-       ~ModuleLusers()
-       {
-               ServerInstance->Modes->DelModeWatcher(&mw);
-       }
-
-       Version GetVersion()
-       {
-               return Version("LUSERS", VF_VENDOR | VF_CORE);
-       }
-};
-
-MODULE_INIT(ModuleLusers)
diff --git a/src/commands/cmd_map.cpp b/src/commands/cmd_map.cpp
deleted file mode 100644 (file)
index 385a2c7..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 CommandMap : public Command
-{
- public:
-       /** Constructor for map.
-        */
-       CommandMap ( Module* parent) : Command(parent,"MAP",0,0) { Penalty=2; }
-       /** 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);
-};
-
-/** Handle /MAP
- */
-CmdResult CommandMap::Handle (const std::vector<std::string>&, User *user)
-{
-       // as with /LUSERS this does nothing without a linking
-       // module to override its behaviour and display something
-       // better.
-
-       if (IS_OPER(user))
-       {
-               user->WriteNumeric(006, "%s :%s [%s]", user->nick.c_str(), ServerInstance->Config->ServerName.c_str(), ServerInstance->Config->GetSID().c_str());
-               user->WriteNumeric(007, "%s :End of /MAP", user->nick.c_str());
-               return CMD_SUCCESS;
-       }
-       user->WriteNumeric(006, "%s :%s",user->nick.c_str(),ServerInstance->Config->ServerName.c_str());
-       user->WriteNumeric(007, "%s :End of /MAP",user->nick.c_str());
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandMap)
diff --git a/src/commands/cmd_mode.cpp b/src/commands/cmd_mode.cpp
deleted file mode 100644 (file)
index 17e21b1..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /MODE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-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 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);
-};
-
-
-/** Handle /MODE
- */
-CmdResult CommandMode::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       ServerInstance->Modes->Process(parameters, user, false);
-       return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandMode)
diff --git a/src/commands/cmd_modenotice.cpp b/src/commands/cmd_modenotice.cpp
deleted file mode 100644 (file)
index 852b72f..0000000
+++ /dev/null
@@ -1,55 +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 CommandModeNotice : public Command
-{
- public:
-       CommandModeNotice(Module* parent) : Command(parent,"MODENOTICE",2,2)
-       {
-               syntax = "<modes> <message>";
-               flags_needed = 'o';
-       }
-
-       CmdResult Handle(const std::vector<std::string>& parameters, User *src)
-       {
-               int mlen = parameters[0].length();
-               for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
-               {
-                       User* user = *i;
-                       for (int n = 0; n < mlen; n++)
-                       {
-                               if (!user->IsModeSet(parameters[0][n]))
-                                       goto next_user;
-                       }
-                       user->Write(":%s NOTICE %s :*** From %s: %s", ServerInstance->Config->ServerName.c_str(),
-                               user->nick.c_str(), src->nick.c_str(), parameters[1].c_str());
-next_user:     ;
-               }
-               return CMD_SUCCESS;
-       }
-
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               return ROUTE_BROADCAST;
-       }
-};
-
-COMMAND_INIT(CommandModeNotice)
diff --git a/src/commands/cmd_modules.cpp b/src/commands/cmd_modules.cpp
deleted file mode 100644 (file)
index 2a15b43..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /MODULES. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandModules : public Command
-{
- public:
-       /** Constructor for modules.
-        */
-       CommandModules(Module* parent) : Command(parent,"MODULES",0,0)
-       {
-               Penalty = 4;
-               syntax = "[<servername>]";
-       }
-
-       /** 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);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               if (parameters.size() >= 1)
-                       return ROUTE_UNICAST(parameters[0]);
-               return ROUTE_LOCALONLY;
-       }
-};
-
-/** Handle /MODULES
- */
-CmdResult CommandModules::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       if (parameters.size() >= 1 && parameters[0] != ServerInstance->Config->ServerName)
-               return CMD_SUCCESS;
-
-       std::vector<std::string> module_names = ServerInstance->Modules->GetAllModuleNames(0);
-
-       for (unsigned int i = 0; i < module_names.size(); i++)
-       {
-               Module* m = ServerInstance->Modules->Find(module_names[i]);
-               Version V = m->GetVersion();
-
-               if (IS_LOCAL(user) && user->HasPrivPermission("servers/auspex"))
-               {
-                       std::string flags("SvcC");
-                       int pos = 0;
-                       for (int mult = 1; 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, module_names[i].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, module_names[i].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(), module_names[i].c_str(), V.description.c_str());
-               }
-       }
-       user->SendText(":%s 703 %s :End of MODULES list", ServerInstance->Config->ServerName.c_str(), user->nick.c_str());
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandModules)
diff --git a/src/commands/cmd_motd.cpp b/src/commands/cmd_motd.cpp
deleted file mode 100644 (file)
index 9ed5ff1..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /MOTD. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandMotd : public Command
-{
- public:
-       /** Constructor for motd.
-        */
-       CommandMotd ( Module* parent) : Command(parent,"MOTD",0,1) { syntax = "[<servername>]"; }
-       /** 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);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               if (parameters.size() > 0)
-                       return ROUTE_UNICAST(parameters[0]);
-               return ROUTE_LOCALONLY;
-       }
-};
-
-/** Handle /MOTD
- */
-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) && (!IS_OPER(user)))
-                       localuser->CommandFloodPenalty += 2000;
-               return CMD_SUCCESS;
-       }
-
-       ConfigTag* tag = ServerInstance->Config->EmptyTag;
-       if (IS_LOCAL(user))
-               tag = user->GetClass()->config;
-       std::string motd_name = tag->getString("motd", "motd");
-       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());
-               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());
-
-       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->SendText(":%s %03d %s :End of message of the day.", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFMOTD, user->nick.c_str());
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandMotd)
diff --git a/src/commands/cmd_names.cpp b/src/commands/cmd_names.cpp
deleted file mode 100644 (file)
index fd4a9ce..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /NAMES. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandNames : public Command
-{
- public:
-       /** Constructor for names.
-        */
-       CommandNames ( Module* parent) : Command(parent,"NAMES",0,0) { syntax = "{<channel>{,<channel>}}"; }
-       /** 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);
-};
-
-/** Handle /NAMES
- */
-CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       Channel* c;
-
-       if (!parameters.size())
-       {
-               user->WriteNumeric(366, "%s * :End of /NAMES list.",user->nick.c_str());
-               return CMD_SUCCESS;
-       }
-
-       if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
-               return CMD_SUCCESS;
-
-       c = ServerInstance->FindChan(parameters[0]);
-       if (c)
-       {
-               c->UserList(user);
-       }
-       else
-       {
-               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
-       }
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandNames)
diff --git a/src/commands/cmd_nick.cpp b/src/commands/cmd_nick.cpp
deleted file mode 100644 (file)
index a079e59..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /NICK. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandNick : public Command
-{
- public:
-       /** Constructor for nick.
-        */
-       CommandNick ( Module* parent) : Command(parent,"NICK", 1, 1) { works_before_reg = true; syntax = "<newnick>"; Penalty = 0; }
-       /** 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);
-};
-
-/** Handle nick changes from users.
- * NOTE: If you are used to ircds based on ircd2.8, and are looking
- * 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)
-{
-       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 (newnick.empty())
-       {
-               user->WriteNumeric(432, "%s * :Erroneous Nickname", oldnick.c_str());
-               return CMD_FAILURE;
-       }
-
-       if (newnick == "0")
-       {
-               newnick = user->uuid;
-       }
-       else if (!ServerInstance->IsNick(newnick.c_str(), ServerInstance->Config->Limits.NickMax))
-       {
-               user->WriteNumeric(432, "%s %s :Erroneous Nickname", user->nick.c_str(),newnick.c_str());
-               return CMD_FAILURE;
-       }
-
-       if (!user->ChangeNick(newnick, false))
-               return CMD_FAILURE;
-
-       if (user->registered < REG_NICKUSER)
-       {
-               user->registered = (user->registered | REG_NICK);
-               if (user->registered == REG_NICKUSER)
-               {
-                       /* 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)
-                               return CMD_FAILURE;
-
-                       // return early to not penalize new users
-                       return CMD_SUCCESS;
-               }
-       }
-
-       return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandNick)
diff --git a/src/commands/cmd_notice.cpp b/src/commands/cmd_notice.cpp
deleted file mode 100644 (file)
index d5ef7ba..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-/** Handle /NOTICE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandNotice : public Command
-{
- public:
-       /** Constructor for notice.
-        */
-       CommandNotice ( Module* parent) : Command(parent,"NOTICE",2,2) { syntax = "<target>{,<target>} <message>"; }
-       /** 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);
-
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               if (IS_LOCAL(user))
-                       // This is handled by the OnUserNotice hook to split the LoopCall pieces
-                       return ROUTE_LOCALONLY;
-               else
-                       return ROUTE_MESSAGE(parameters[0]);
-       }
-};
-
-
-CmdResult CommandNotice::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       User *dest;
-       Channel *chan;
-
-       CUList exempt_list;
-
-       user->idle_lastmsg = ServerInstance->Time();
-
-       if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
-               return CMD_SUCCESS;
-       if (parameters[0][0] == '$')
-       {
-               if (!user->HasPrivPermission("users/mass-message"))
-                       return CMD_SUCCESS;
-
-               ModResult MOD_RESULT;
-               std::string temp = parameters[1];
-               FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, exempt_list));
-               if (MOD_RESULT == MOD_RES_DENY)
-                       return CMD_FAILURE;
-               const char* text = temp.c_str();
-               const char* servermask = (parameters[0].c_str()) + 1;
-
-               FOREACH_MOD(I_OnText,OnText(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, exempt_list));
-               if (InspIRCd::Match(ServerInstance->Config->ServerName,servermask, NULL))
-               {
-                       user->SendAll("NOTICE", "%s", text);
-               }
-               FOREACH_MOD(I_OnUserNotice,OnUserNotice(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, exempt_list));
-               return CMD_SUCCESS;
-       }
-       char status = 0;
-       const char* target = parameters[0].c_str();
-
-       if (ServerInstance->Modes->FindPrefix(*target))
-       {
-               status = *target;
-               target++;
-       }
-       if (*target == '#')
-       {
-               chan = ServerInstance->FindChan(target);
-
-               exempt_list.insert(user);
-
-               if (chan)
-               {
-                       if (IS_LOCAL(user))
-                       {
-                               if ((chan->IsModeSet('n')) && (!chan->HasUser(user)))
-                               {
-                                       user->WriteNumeric(404, "%s %s :Cannot send to channel (no external messages)", user->nick.c_str(), chan->name.c_str());
-                                       return CMD_FAILURE;
-                               }
-                               if ((chan->IsModeSet('m')) && (chan->GetPrefixValue(user) < VOICE_VALUE))
-                               {
-                                       user->WriteNumeric(404, "%s %s :Cannot send to channel (+m)", user->nick.c_str(), chan->name.c_str());
-                                       return CMD_FAILURE;
-                               }
-
-                               if (ServerInstance->Config->RestrictBannedUsers)
-                               {
-                                       if (chan->IsBanned(user))
-                                       {
-                                               user->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", user->nick.c_str(), chan->name.c_str());
-                                               return CMD_FAILURE;
-                                       }
-                               }
-                       }
-                       ModResult MOD_RESULT;
-
-                       std::string temp = parameters[1];
-                       FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user,chan,TYPE_CHANNEL,temp,status, exempt_list));
-                       if (MOD_RESULT == MOD_RES_DENY)
-                               return CMD_FAILURE;
-
-                       const char* text = temp.c_str();
-
-                       if (temp.empty())
-                       {
-                               user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
-                               return CMD_FAILURE;
-                       }
-
-                       FOREACH_MOD(I_OnText,OnText(user,chan,TYPE_CHANNEL,text,status,exempt_list));
-
-                       if (status)
-                       {
-                               if (ServerInstance->Config->UndernetMsgPrefix)
-                               {
-                                       chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%c %s", status, chan->name.c_str(), status, text);
-                               }
-                               else
-                               {
-                                       chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%s", status, chan->name.c_str(), text);
-                               }
-                       }
-                       else
-                       {
-                               chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %s :%s", chan->name.c_str(), text);
-                       }
-
-                       FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,chan,TYPE_CHANNEL,text,status,exempt_list));
-               }
-               else
-               {
-                       /* no such nick/channel */
-                       user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), target);
-                       return CMD_FAILURE;
-               }
-               return CMD_SUCCESS;
-       }
-
-       const char* destnick = parameters[0].c_str();
-
-       if (IS_LOCAL(user))
-       {
-               const char* targetserver = strchr(destnick, '@');
-
-               if (targetserver)
-               {
-                       std::string nickonly;
-
-                       nickonly.assign(destnick, 0, targetserver - destnick);
-                       dest = ServerInstance->FindNickOnly(nickonly);
-                       if (dest && strcasecmp(dest->server.c_str(), targetserver + 1))
-                       {
-                               /* Incorrect server for user */
-                               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
-                               return CMD_FAILURE;
-                       }
-               }
-               else
-                       dest = ServerInstance->FindNickOnly(destnick);
-       }
-       else
-               dest = ServerInstance->FindNick(destnick);
-
-       if ((dest) && (dest->registered == REG_ALL))
-       {
-               if (parameters[1].empty())
-               {
-                       user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
-                       return CMD_FAILURE;
-               }
-
-               ModResult MOD_RESULT;
-               std::string temp = parameters[1];
-               FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user,dest,TYPE_USER,temp,0,exempt_list));
-               if (MOD_RESULT == MOD_RES_DENY) {
-                       return CMD_FAILURE;
-               }
-               const char* text = temp.c_str();
-
-               FOREACH_MOD(I_OnText,OnText(user,dest,TYPE_USER,text,0,exempt_list));
-
-               if (IS_LOCAL(dest))
-               {
-                       // direct write, same server
-                       user->WriteTo(dest, "NOTICE %s :%s", dest->nick.c_str(), text);
-               }
-
-               FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,dest,TYPE_USER,text,0,exempt_list));
-       }
-       else
-       {
-               /* no such nick/channel */
-               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-
-       return CMD_SUCCESS;
-
-}
-
-COMMAND_INIT(CommandNotice)
diff --git a/src/commands/cmd_oper.cpp b/src/commands/cmd_oper.cpp
deleted file mode 100644 (file)
index 95f6b98..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-bool OneOfMatches(const char* host, const char* ip, const char* hostlist);
-
-/** Handle /OPER. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandOper : public SplitCommand
-{
- public:
-       /** Constructor for oper.
-        */
-       CommandOper ( Module* parent) : SplitCommand(parent,"OPER",2,2) { syntax = "<username> <password>"; }
-       /** 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 HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
-};
-
-bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
-{
-       std::stringstream hl(hostlist);
-       std::string xhost;
-       while (hl >> xhost)
-       {
-               if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
-               {
-                       return true;
-               }
-       }
-       return false;
-}
-
-CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
-{
-       char TheHost[MAXBUF];
-       char TheIP[MAXBUF];
-       bool match_login = false;
-       bool match_pass = false;
-       bool match_hosts = false;
-
-       snprintf(TheHost,MAXBUF,"%s@%s",user->ident.c_str(),user->host.c_str());
-       snprintf(TheIP, MAXBUF,"%s@%s",user->ident.c_str(),user->GetIPString());
-
-       OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
-       if ((i != ServerInstance->Config->oper_blocks.end()) && (i->second->oper_block))
-       {
-               OperInfo* ifo = i->second;
-               ConfigTag* tag = ifo->oper_block;
-               match_login = true;
-               match_pass = !ServerInstance->PassCompare(user, tag->getString("password"), parameters[1], tag->getString("hash"));
-               match_hosts = OneOfMatches(TheHost,TheIP,tag->getString("host"));
-
-               if (match_pass && match_hosts)
-               {
-                       /* found this oper's opertype */
-                       user->Oper(ifo);
-                       return CMD_SUCCESS;
-               }
-       }
-
-       std::string fields;
-       if (!match_login)
-               fields.append("login ");
-       if (!match_pass)
-               fields.append("password ");
-       if (!match_hosts)
-               fields.append("hosts");
-
-       // tell them they suck, and lag them up to help prevent brute-force attacks
-       user->WriteNumeric(491, "%s :Invalid oper credentials",user->nick.c_str());
-       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());
-       ServerInstance->Logs->Log("OPER",DEFAULT,"OPER: Failed oper attempt by %s using login '%s': The following fields did not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
-       return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandOper)
diff --git a/src/commands/cmd_part.cpp b/src/commands/cmd_part.cpp
deleted file mode 100644 (file)
index aadb42d..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /PART. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPart : public Command
-{
- public:
-       /** Constructor for part.
-        */
-       CommandPart (Module* parent) : Command(parent,"PART", 1, 2) { Penalty = 5; syntax = "<channel>{,<channel>} [<reason>]"; }
-       /** 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);
-};
-
-CmdResult CommandPart::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       std::string reason;
-
-       if (IS_LOCAL(user))
-       {
-               if (!ServerInstance->Config->FixedPart.empty())
-                       reason = ServerInstance->Config->FixedPart;
-               else if (parameters.size() > 1)
-                       reason = ServerInstance->Config->PrefixPart + parameters[1] + ServerInstance->Config->SuffixPart;
-       }
-       else
-       {
-               if (parameters.size() > 1)
-                       reason = parameters[1];
-       }
-
-       if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
-               return CMD_SUCCESS;
-
-       Channel* c = ServerInstance->FindChan(parameters[0]);
-
-       if (c)
-       {
-               c->PartUser(user, reason);
-       }
-       else
-       {
-               user->WriteServ( "401 %s %s :No such channel", user->nick.c_str(), parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPart)
diff --git a/src/commands/cmd_pass.cpp b/src/commands/cmd_pass.cpp
deleted file mode 100644 (file)
index 9fc7588..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /PASS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPass : public SplitCommand
-{
- public:
-       /** Constructor for pass.
-        */
-       CommandPass (Module* parent) : SplitCommand(parent,"PASS",1,1) { works_before_reg = true; Penalty = 0; syntax = "<password>"; }
-       /** 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 HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
-};
-
-
-CmdResult CommandPass::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
-{
-       // Check to make sure they haven't registered -- Fix by FCS
-       if (user->registered == REG_ALL)
-       {
-               user->CommandFloodPenalty += 1000;
-               user->WriteNumeric(ERR_ALREADYREGISTERED, "%s :You may not reregister",user->nick.c_str());
-               return CMD_FAILURE;
-       }
-       user->password = parameters[0];
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPass)
diff --git a/src/commands/cmd_ping.cpp b/src/commands/cmd_ping.cpp
deleted file mode 100644 (file)
index dd14b52..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /PING. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPing : public Command
-{
- public:
-       /** Constructor for ping.
-        */
-       CommandPing ( Module* parent) : Command(parent,"PING", 1, 2) { syntax = "<servername> [:<servername>]"; }
-       /** 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);
-};
-
-CmdResult CommandPing::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       user->WriteServ("PONG %s :%s", ServerInstance->Config->ServerName.c_str(), parameters[0].c_str());
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPing)
diff --git a/src/commands/cmd_pong.cpp b/src/commands/cmd_pong.cpp
deleted file mode 100644 (file)
index 3b6f17f..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /PONG. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPong : public Command
-{
- public:
-       /** Constructor for pong.
-        */
-       CommandPong ( Module* parent) : Command(parent,"PONG", 0, 1) { Penalty = 0; syntax = "<ping-text>"; }
-       /** 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);
-};
-
-CmdResult CommandPong::Handle (const std::vector<std::string>&, User *user)
-{
-       // set the user as alive so they survive to next ping
-       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;
-}
-
-COMMAND_INIT(CommandPong)
diff --git a/src/commands/cmd_privmsg.cpp b/src/commands/cmd_privmsg.cpp
deleted file mode 100644 (file)
index cefdd48..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /PRIVMSG. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPrivmsg : public Command
-{
- public:
-       /** Constructor for privmsg.
-        */
-       CommandPrivmsg ( Module* parent) : Command(parent,"PRIVMSG",2,2) { syntax = "<target>{,<target>} <message>"; }
-       /** 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);
-
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               if (IS_LOCAL(user))
-                       // This is handled by the OnUserMessage hook to split the LoopCall pieces
-                       return ROUTE_LOCALONLY;
-               else
-                       return ROUTE_MESSAGE(parameters[0]);
-       }
-};
-
-CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       User *dest;
-       Channel *chan;
-       CUList except_list;
-
-       user->idle_lastmsg = ServerInstance->Time();
-
-       if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
-               return CMD_SUCCESS;
-
-       if (parameters[0][0] == '$')
-       {
-               if (!user->HasPrivPermission("users/mass-message"))
-                       return CMD_SUCCESS;
-
-               ModResult MOD_RESULT;
-               std::string temp = parameters[1];
-               FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, except_list));
-               if (MOD_RESULT == MOD_RES_DENY)
-                       return CMD_FAILURE;
-
-               const char* text = temp.c_str();
-               const char* servermask = (parameters[0].c_str()) + 1;
-
-               FOREACH_MOD(I_OnText,OnText(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
-               if (InspIRCd::Match(ServerInstance->Config->ServerName, servermask, NULL))
-               {
-                       user->SendAll("PRIVMSG", "%s", text);
-               }
-               FOREACH_MOD(I_OnUserMessage,OnUserMessage(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
-               return CMD_SUCCESS;
-       }
-       char status = 0;
-       const char* target = parameters[0].c_str();
-
-       if (ServerInstance->Modes->FindPrefix(*target))
-       {
-               status = *target;
-               target++;
-       }
-       if (*target == '#')
-       {
-               chan = ServerInstance->FindChan(target);
-
-               except_list.insert(user);
-
-               if (chan)
-               {
-                       if (IS_LOCAL(user) && chan->GetPrefixValue(user) < VOICE_VALUE)
-                       {
-                               if (chan->IsModeSet('n') && !chan->HasUser(user))
-                               {
-                                       user->WriteNumeric(404, "%s %s :Cannot send to channel (no external messages)", user->nick.c_str(), chan->name.c_str());
-                                       return CMD_FAILURE;
-                               }
-
-                               if (chan->IsModeSet('m'))
-                               {
-                                       user->WriteNumeric(404, "%s %s :Cannot send to channel (+m)", user->nick.c_str(), chan->name.c_str());
-                                       return CMD_FAILURE;
-                               }
-
-                               if (ServerInstance->Config->RestrictBannedUsers)
-                               {
-                                       if (chan->IsBanned(user))
-                                       {
-                                               user->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", user->nick.c_str(), chan->name.c_str());
-                                               return CMD_FAILURE;
-                                       }
-                               }
-                       }
-                       ModResult MOD_RESULT;
-
-                       std::string temp = parameters[1];
-                       FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user,chan,TYPE_CHANNEL,temp,status,except_list));
-                       if (MOD_RESULT == MOD_RES_DENY)
-                               return CMD_FAILURE;
-
-                       const char* text = temp.c_str();
-
-                       /* Check again, a module may have zapped the input string */
-                       if (temp.empty())
-                       {
-                               user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
-                               return CMD_FAILURE;
-                       }
-
-                       FOREACH_MOD(I_OnText,OnText(user,chan,TYPE_CHANNEL,text,status,except_list));
-
-                       if (status)
-                       {
-                               if (ServerInstance->Config->UndernetMsgPrefix)
-                               {
-                                       chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%c %s", status, chan->name.c_str(), status, text);
-                               }
-                               else
-                               {
-                                       chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%s", status, chan->name.c_str(), text);
-                               }
-                       }
-                       else
-                       {
-                               chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %s :%s", chan->name.c_str(), text);
-                       }
-
-                       FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,chan,TYPE_CHANNEL,text,status,except_list));
-               }
-               else
-               {
-                       /* no such nick/channel */
-                       user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), target);
-                       return CMD_FAILURE;
-               }
-               return CMD_SUCCESS;
-       }
-
-       const char* destnick = parameters[0].c_str();
-
-       if (IS_LOCAL(user))
-       {
-               const char* targetserver = strchr(destnick, '@');
-
-               if (targetserver)
-               {
-                       std::string nickonly;
-
-                       nickonly.assign(destnick, 0, targetserver - destnick);
-                       dest = ServerInstance->FindNickOnly(nickonly);
-                       if (dest && strcasecmp(dest->server.c_str(), targetserver + 1))
-                       {
-                               /* Incorrect server for user */
-                               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
-                               return CMD_FAILURE;
-                       }
-               }
-               else
-                       dest = ServerInstance->FindNickOnly(destnick);
-       }
-       else
-               dest = ServerInstance->FindNick(destnick);
-
-       if ((dest) && (dest->registered == REG_ALL))
-       {
-               if (parameters[1].empty())
-               {
-                       user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
-                       return CMD_FAILURE;
-               }
-
-               if (IS_AWAY(dest))
-               {
-                       /* auto respond with aweh msg */
-                       user->WriteNumeric(301, "%s %s :%s", user->nick.c_str(), dest->nick.c_str(), dest->awaymsg.c_str());
-               }
-
-               ModResult MOD_RESULT;
-
-               std::string temp = parameters[1];
-               FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, dest, TYPE_USER, temp, 0, except_list));
-               if (MOD_RESULT == MOD_RES_DENY)
-                       return CMD_FAILURE;
-
-               const char* text = temp.c_str();
-
-               FOREACH_MOD(I_OnText,OnText(user, dest, TYPE_USER, text, 0, except_list));
-
-               if (IS_LOCAL(dest))
-               {
-                       // direct write, same server
-                       user->WriteTo(dest, "PRIVMSG %s :%s", dest->nick.c_str(), text);
-               }
-
-               FOREACH_MOD(I_OnUserMessage,OnUserMessage(user, dest, TYPE_USER, text, 0, except_list));
-       }
-       else
-       {
-               /* no such nick/channel */
-               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPrivmsg)
diff --git a/src/commands/cmd_qline.cpp b/src/commands/cmd_qline.cpp
deleted file mode 100644 (file)
index 3118798..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 "xline.h"
-
-/** Handle /QLINE.  */
-class CommandQline : public Command
-{
- public:
-       /** Constructor for qline.
-        */
-       CommandQline ( Module* parent) : Command(parent,"QLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<nick> [<duration> :<reason>]"; }
-       /** Handle command.
-        * @param parameters The parameters to the comamnd
-        * @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.
-        */
-       CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       if (parameters.size() >= 3)
-       {
-               if (ServerInstance->NickMatchesEveryone(parameters[0],user))
-                       return CMD_FAILURE;
-
-               if (parameters[0].find('@') != std::string::npos || parameters[0].find('!') != std::string::npos || parameters[0].find('.') != std::string::npos)
-               {
-                       user->WriteServ("NOTICE %s :*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.",user->nick.c_str());
-                       return CMD_FAILURE;
-               }
-
-               long duration = ServerInstance->Duration(parameters[1].c_str());
-               QLine* ql = new QLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), parameters[0].c_str());
-               if (ServerInstance->XLines->AddLine(ql,user))
-               {
-                       if (!duration)
-                       {
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Q-line for %s: %s",user->nick.c_str(), parameters[0].c_str(), parameters[2].c_str());
-                       }
-                       else
-                       {
-                               time_t c_requires_crap = duration + ServerInstance->Time();
-                               std::string timestr = ServerInstance->TimeString(c_requires_crap);
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Q-line for %s, expires on %s: %s",user->nick.c_str(),parameters[0].c_str(),
-                                               timestr.c_str(), parameters[2].c_str());
-                       }
-                       ServerInstance->XLines->ApplyLines();
-               }
-               else
-               {
-                       delete ql;
-                       user->WriteServ("NOTICE %s :*** Q-Line for %s already exists",user->nick.c_str(),parameters[0].c_str());
-               }
-       }
-       else
-       {
-               if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "Q", user))
-               {
-                       ServerInstance->SNO->WriteToSnoMask('x',"%s removed Q-line on %s",user->nick.c_str(),parameters[0].c_str());
-               }
-               else
-               {
-                       user->WriteServ("NOTICE %s :*** Q-Line %s not found in list, try /stats q.",user->nick.c_str(),parameters[0].c_str());
-                       return CMD_FAILURE;
-               }
-       }
-
-       return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandQline)
diff --git a/src/commands/cmd_quit.cpp b/src/commands/cmd_quit.cpp
deleted file mode 100644 (file)
index 6a6b447..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /QUIT. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandQuit : public Command
-{
- public:
-       /** Constructor for quit.
-        */
-       CommandQuit ( Module* parent) : Command(parent,"QUIT",0,1) { works_before_reg = true; syntax = "[<message>]"; }
-       /** 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);
-};
-
-
-CmdResult CommandQuit::Handle (const std::vector<std::string>& parameters, User *user)
-{
-
-       std::string quitmsg;
-
-       if (IS_LOCAL(user))
-       {
-               if (!ServerInstance->Config->FixedQuit.empty())
-                       quitmsg = ServerInstance->Config->FixedQuit;
-               else
-                       quitmsg = parameters.size() ?
-                               ServerInstance->Config->PrefixQuit + std::string(parameters[0]) + ServerInstance->Config->SuffixQuit
-                               : "Client exited";
-       }
-       else
-               quitmsg = parameters.size() ? parameters[0] : "Client exited";
-
-       std::string* operquit = ServerInstance->OperQuit.get(user);
-       if (operquit)
-       {
-               ServerInstance->Users->QuitUser(user, quitmsg, operquit->c_str());
-       }
-       else
-       {
-               ServerInstance->Users->QuitUser(user, quitmsg);
-       }
-
-       return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandQuit)
diff --git a/src/commands/cmd_rehash.cpp b/src/commands/cmd_rehash.cpp
deleted file mode 100644 (file)
index abf0b78..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *   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/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /REHASH. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandRehash : public Command
-{
- public:
-       /** Constructor for rehash.
-        */
-       CommandRehash ( Module* parent) : Command(parent,"REHASH",0) { flags_needed = 'o'; Penalty = 2; syntax = "[<servermask>]"; }
-       /** 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);
-};
-
-CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       std::string param = parameters.size() ? parameters[0] : "";
-
-       FOREACH_MOD(I_OnPreRehash,OnPreRehash(user, param));
-
-       if (param.empty())
-       {
-               // standard rehash of local server
-       }
-       else if (param.find_first_of("*.") != std::string::npos)
-       {
-               // rehash of servers by server name (with wildcard)
-               if (!InspIRCd::Match(ServerInstance->Config->ServerName, parameters[0]))
-               {
-                       // Doesn't match us. PreRehash is already done, nothing left to do
-                       return CMD_SUCCESS;
-               }
-       }
-       else
-       {
-               // parameterized rehash
-
-               // the leading "-" is optional; remove it if present.
-               if (param[0] == '-')
-                       param = param.substr(1);
-
-               FOREACH_MOD(I_OnModuleRehash,OnModuleRehash(user, param));
-               return CMD_SUCCESS;
-       }
-
-       // Rehash for me. Try to start the rehash thread
-       if (!ServerInstance->ConfigThread)
-       {
-               std::string m = user->nick + " is rehashing config file " + ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()) + " on " + ServerInstance->Config->ServerName;
-               ServerInstance->SNO->WriteGlobalSno('a', m);
-
-               if (IS_LOCAL(user))
-                       user->WriteNumeric(RPL_REHASHING, "%s %s :Rehashing",
-                               user->nick.c_str(),ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()));
-               else
-                       ServerInstance->PI->SendUserNotice(user, std::string("*** Rehashing server ") +
-                               ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()));
-
-               /* Don't do anything with the logs here -- logs are restarted
-                * after the config thread has completed.
-                */
-               ServerInstance->RehashUsersAndChans();
-               FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
-
-
-               ServerInstance->ConfigThread = new ConfigReaderThread(user->uuid);
-               ServerInstance->Threads->Start(ServerInstance->ConfigThread);
-       }
-       else
-       {
-               /*
-                * A rehash is already in progress! ahh shit.
-                * XXX, todo: we should find some way to kill runaway rehashes that are blocking, this is a major problem for unrealircd users
-                */
-               if (IS_LOCAL(user))
-                       user->WriteServ("NOTICE %s :*** Could not rehash: A rehash is already in progress.", user->nick.c_str());
-               else
-                       ServerInstance->PI->SendUserNotice(user, "*** Could not rehash: A rehash is already in progress.");
-       }
-
-       // Always return success so spanningtree forwards an incoming REHASH even if we failed
-       return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandRehash)
diff --git a/src/commands/cmd_reloadmodule.cpp b/src/commands/cmd_reloadmodule.cpp
deleted file mode 100644 (file)
index eac364f..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 CommandReloadmodule : public Command
-{
- public:
-       /** Constructor for reloadmodule.
-        */
-       CommandReloadmodule ( Module* parent) : Command( parent, "RELOADMODULE",1) { flags_needed = 'o'; syntax = "<modulename>"; }
-       /** 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);
-};
-
-class ReloadModuleWorker : public HandlerBase1<void, bool>
-{
- 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);
-               if (user)
-                       user->WriteNumeric(975, "%s %s :Module %ssuccessfully reloaded.",
-                               user->nick.c_str(), name.c_str(), result ? "" : "un");
-               ServerInstance->GlobalCulls.AddItem(this);
-       }
-};
-
-CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       if (parameters[0] == "cmd_reloadmodule.so")
-       {
-               user->WriteNumeric(975, "%s %s :You cannot reload cmd_reloadmodule.so (unload and load it)",
-                       user->nick.c_str(), parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-
-       Module* m = ServerInstance->Modules->Find(parameters[0]);
-       if (m)
-       {
-               ServerInstance->Modules->Reload(m, (creator->dying ? NULL : new ReloadModuleWorker(user->uuid, parameters[0])));
-               return CMD_SUCCESS;
-       }
-       else
-       {
-               user->WriteNumeric(975, "%s %s :Could not find module by that name", user->nick.c_str(), parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-}
-
-COMMAND_INIT(CommandReloadmodule)
diff --git a/src/commands/cmd_restart.cpp b/src/commands/cmd_restart.cpp
deleted file mode 100644 (file)
index 48f902d..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /RESTART
- */
-class CommandRestart : public Command
-{
- public:
-       /** Constructor for restart.
-        */
-       CommandRestart(Module* parent) : Command(parent,"RESTART",1,1) { flags_needed = 'o'; syntax = "<password>"; }
-       /** 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);
-};
-
-CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       ServerInstance->Logs->Log("COMMAND",DEFAULT,"Restart: %s",user->nick.c_str());
-       if (!ServerInstance->PassCompare(user, ServerInstance->Config->restartpass, parameters[0].c_str(), ServerInstance->Config->powerhash))
-       {
-               ServerInstance->SNO->WriteGlobalSno('a', "RESTART command from %s, restarting server.", user->GetFullRealHost().c_str());
-
-               ServerInstance->SendError("Server restarting.");
-
-#ifndef _WIN32
-               /* XXX: This hack sets FD_CLOEXEC on all possible file descriptors, so they're closed if the execvp() below succeeds.
-                * Certainly, this is not a nice way to do things and it's slow when the fd limit is high.
-                *
-                * A better solution would be to set the close-on-exec flag for each fd we create (or create them with O_CLOEXEC),
-                * however there is no guarantee that third party libs will do the same.
-                */
-               for (int i = getdtablesize(); --i > 2;)
-               {
-                       int flags = fcntl(i, F_GETFD);
-                       if (flags != -1)
-                               fcntl(i, F_SETFD, flags | FD_CLOEXEC);
-               }
-#endif
-
-               execvp(ServerInstance->Config->cmdline.argv[0], ServerInstance->Config->cmdline.argv);
-               ServerInstance->SNO->WriteGlobalSno('a', "Failed RESTART - could not execute '%s' (%s)",
-                       ServerInstance->Config->cmdline.argv[0], strerror(errno));
-       }
-       else
-       {
-               ServerInstance->SNO->WriteGlobalSno('a', "Failed RESTART Command from %s.", user->GetFullRealHost().c_str());
-       }
-       return CMD_FAILURE;
-}
-
-
-COMMAND_INIT(CommandRestart)
diff --git a/src/commands/cmd_rules.cpp b/src/commands/cmd_rules.cpp
deleted file mode 100644 (file)
index 7aacf8c..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /RULES. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandRules : public Command
-{
- public:
-       /** Constructor for rules.
-        */
-       CommandRules ( Module* parent) : Command(parent,"RULES",0,0) { syntax = "[<servername>]"; }
-       /** 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);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               if (parameters.size() > 0)
-                       return ROUTE_UNICAST(parameters[0]);
-               return ROUTE_LOCALONLY;
-       }
-};
-
-CmdResult CommandRules::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 /RULES of a remote server
-               LocalUser* localuser = IS_LOCAL(user);
-               if ((localuser) && (!IS_OPER(user)))
-                       localuser->CommandFloodPenalty += 2000;
-               return CMD_SUCCESS;
-       }
-
-       ConfigTag* tag = ServerInstance->Config->EmptyTag;
-       if (IS_LOCAL(user))
-               tag = user->GetClass()->config;
-       std::string rules_name = tag->getString("rules", "rules");
-       ConfigFileCache::iterator rules = ServerInstance->Config->Files.find(rules_name);
-       if (rules == ServerInstance->Config->Files.end())
-       {
-               user->SendText(":%s %03d %s :RULES file is missing.",
-                       ServerInstance->Config->ServerName.c_str(), ERR_NORULES, user->nick.c_str());
-               return CMD_SUCCESS;
-       }
-       user->SendText(":%s %03d %s :%s server rules:", ServerInstance->Config->ServerName.c_str(),
-               RPL_RULESTART, user->nick.c_str(), ServerInstance->Config->ServerName.c_str());
-
-       for (file_cache::iterator i = rules->second.begin(); i != rules->second.end(); i++)
-               user->SendText(":%s %03d %s :- %s", ServerInstance->Config->ServerName.c_str(), RPL_RULES, user->nick.c_str(),i->c_str());
-
-       user->SendText(":%s %03d %s :End of RULES command.", ServerInstance->Config->ServerName.c_str(), RPL_RULESEND, user->nick.c_str());
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandRules)
diff --git a/src/commands/cmd_server.cpp b/src/commands/cmd_server.cpp
deleted file mode 100644 (file)
index 05954f3..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /SERVER. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandServer : public Command
-{
- public:
-       /** Constructor for server.
-        */
-       CommandServer ( Module* parent) : Command(parent,"SERVER") { works_before_reg = true;}
-       /** 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);
-};
-
-CmdResult CommandServer::Handle (const std::vector<std::string>&, User *user)
-{
-       if (user->registered == REG_ALL)
-       {
-               user->WriteNumeric(ERR_ALREADYREGISTERED, "%s :You are already registered. (Perhaps your IRC client does not have a /SERVER command).",user->nick.c_str());
-       }
-       else
-       {
-               user->WriteNumeric(ERR_NOTREGISTERED, "%s %s :You may not register as a server (servers have separate ports from clients, change your config)", user->nick.c_str(), name.c_str());
-       }
-       return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandServer)
diff --git a/src/commands/cmd_squit.cpp b/src/commands/cmd_squit.cpp
deleted file mode 100644 (file)
index ce73ee0..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /SQUIT. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandSquit : public Command
-{
- public:
-       /** Constructor for squit.
-        */
-       CommandSquit ( Module* parent) : Command(parent,"SQUIT",1,2) { flags_needed = 'o'; syntax = "<servername>"; }
-       /** 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);
-};
-
-
-/*
- * This is handled by the server linking module, if necessary. Do not remove this stub.
- */
-
-
-CmdResult CommandSquit::Handle (const std::vector<std::string>&, 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());
-       return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandSquit)
diff --git a/src/commands/cmd_stats.cpp b/src/commands/cmd_stats.cpp
deleted file mode 100644 (file)
index aa5bf44..0000000
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 "xline.h"
-#include "commands/cmd_whowas.h"
-
-#ifdef _WIN32
-#include <psapi.h>
-#pragma comment(lib, "psapi.lib") // For GetProcessMemoryInfo()
-#endif
-
-/** Handle /STATS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandStats : public Command
-{
-       void DoStats(char statschar, User* user, string_list &results);
- public:
-       /** Constructor for stats.
-        */
-       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 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);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               if (parameters.size() > 1)
-                       return ROUTE_UNICAST(parameters[1]);
-               return ROUTE_LOCALONLY;
-       }
-};
-
-void CommandStats::DoStats(char statschar, User* user, string_list &results)
-{
-       std::string sn(ServerInstance->Config->ServerName);
-
-       bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos;
-       bool isRemoteOper = IS_REMOTE(user) && IS_OPER(user);
-       bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
-
-       if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
-       {
-               ServerInstance->SNO->WriteToSnoMask('t',
-                               "%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(sn + " 481 " + user->nick + " :Permission denied - STATS " + statschar + " requires the servers/auspex priv.");
-               return;
-       }
-
-       ModResult MOD_RESULT;
-       FIRST_MOD_RESULT(OnStats, MOD_RESULT, (statschar, user, results));
-       if (MOD_RESULT == MOD_RES_DENY)
-       {
-               results.push_back(sn+" 219 "+user->nick+" "+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;
-       }
-
-       switch (statschar)
-       {
-               /* stats p (show listening ports) */
-               case 'p':
-               {
-                       for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
-                       {
-                               ListenSocket* ls = *i;
-                               std::string ip = ls->bind_addr;
-                               if (ip.empty())
-                                       ip.assign("*");
-                               std::string type = ls->bind_tag->getString("type", "clients");
-                               std::string hook = ls->bind_tag->getString("ssl", "plaintext");
-
-                               results.push_back(sn+" 249 "+user->nick+" :"+ ip + ":"+ConvToStr(ls->bind_port)+
-                                       " (" + type + ", " + hook + ")");
-                       }
-               }
-               break;
-
-               /* These stats symbols must be handled by a linking module */
-               case 'n':
-               case 'c':
-               break;
-
-               case 'i':
-               {
-                       for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
-                       {
-                               ConnectClass* c = *i;
-                               std::stringstream res;
-                               res << sn << " 215 " << user->nick << " I " << c->name << ' ';
-                               if (c->type == CC_ALLOW)
-                                       res << '+';
-                               if (c->type == CC_DENY)
-                                       res << '-';
-
-                               if (c->type == CC_NAMED)
-                                       res << '*';
-                               else
-                                       res << c->host;
-
-                               res << ' ' << c->config->getString("port", "*") << ' ';
-
-                               res << c->GetRecvqMax() << ' ' << c->GetSendqSoftMax() << ' ' << c->GetSendqHardMax()
-                                       << ' ' << c->GetCommandRate() << ' ' << c->GetPenaltyThreshold();
-                               if (c->fakelag)
-                                       res << '*';
-                               results.push_back(res.str());
-                       }
-               }
-               break;
-
-               case 'Y':
-               {
-                       int idx = 0;
-                       for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
-                       {
-                               ConnectClass* c = *i;
-                               results.push_back(sn+" 215 "+user->nick+" i NOMATCH * "+c->GetHost()+" "+ConvToStr(c->limit ? c->limit : ServerInstance->SE->GetMaxFds())+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *");
-                               results.push_back(sn+" 218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqHardMax())+" :"+
-                                               ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
-                               idx++;
-                       }
-               }
-               break;
-
-               case 'U':
-               {
-                       for(std::map<irc::string, bool>::iterator i = ServerInstance->Config->ulines.begin(); i != ServerInstance->Config->ulines.end(); ++i)
-                       {
-                               results.push_back(sn+" 248 "+user->nick+" U "+std::string(i->first.c_str()));
-                       }
-               }
-               break;
-
-               case 'P':
-               {
-                       unsigned int idx = 0;
-                       for (std::list<User*>::const_iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); ++i)
-                       {
-                               User* oper = *i;
-                               if (!ServerInstance->ULine(oper->server))
-                               {
-                                       results.push_back(sn+" 249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
-                                                       (IS_LOCAL(oper) ? ConvToStr(ServerInstance->Time() - oper->idle_lastmsg) + " secs" : "unavailable"));
-                                       idx++;
-                               }
-                       }
-                       results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)");
-               }
-               break;
-
-               case 'k':
-                       ServerInstance->XLines->InvokeStats("K",216,user,results);
-               break;
-               case 'g':
-                       ServerInstance->XLines->InvokeStats("G",223,user,results);
-               break;
-               case 'q':
-                       ServerInstance->XLines->InvokeStats("Q",217,user,results);
-               break;
-               case 'Z':
-                       ServerInstance->XLines->InvokeStats("Z",223,user,results);
-               break;
-               case 'e':
-                       ServerInstance->XLines->InvokeStats("E",223,user,results);
-               break;
-               case 'E':
-                       results.push_back(sn+" 249 "+user->nick+" :Total events: "+ConvToStr(ServerInstance->SE->TotalEvents));
-                       results.push_back(sn+" 249 "+user->nick+" :Read events:  "+ConvToStr(ServerInstance->SE->ReadEvents));
-                       results.push_back(sn+" 249 "+user->nick+" :Write events: "+ConvToStr(ServerInstance->SE->WriteEvents));
-                       results.push_back(sn+" 249 "+user->nick+" :Error events: "+ConvToStr(ServerInstance->SE->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++)
-                       {
-                               if (i->second->use_count)
-                               {
-                                       /* RPL_STATSCOMMANDS */
-                                       results.push_back(sn+" 212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count)+" "+ConvToStr(i->second->total_bytes));
-                               }
-                       }
-               break;
-
-               /* stats z (debug and memory info) */
-               case 'z':
-               {
-                       results.push_back(sn+" 249 "+user->nick+" :Users: "+ConvToStr(ServerInstance->Users->clientlist->size()));
-                       results.push_back(sn+" 249 "+user->nick+" :Channels: "+ConvToStr(ServerInstance->chanlist->size()));
-                       results.push_back(sn+" 249 "+user->nick+" :Commands: "+ConvToStr(ServerInstance->Parser->cmdlist.size()));
-
-                       if (ServerInstance->Config->WhoWasGroupSize && ServerInstance->Config->WhoWasMaxGroups)
-                       {
-                               Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
-                               if (whowas)
-                               {
-                                       WhowasRequest req(NULL, whowas, WhowasRequest::WHOWAS_STATS);
-                                       req.user = user;
-                                       req.Send();
-                                       results.push_back(sn+" 249 "+user->nick+" :"+req.value);
-                               }
-                       }
-
-                       float kbitpersec_in, kbitpersec_out, kbitpersec_total;
-                       char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
-
-                       ServerInstance->SE->GetStats(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(sn+" 249 "+user->nick+" :Bandwidth total:  "+ConvToStr(kbitpersec_total_s)+" kilobits/sec");
-                       results.push_back(sn+" 249 "+user->nick+" :Bandwidth out:    "+ConvToStr(kbitpersec_out_s)+" kilobits/sec");
-                       results.push_back(sn+" 249 "+user->nick+" :Bandwidth in:     "+ConvToStr(kbitpersec_in_s)+" kilobits/sec");
-
-#ifndef _WIN32
-                       /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
-                        * Also cuts out some identical code in both branches of the ifndef. -- Om
-                        */
-                       rusage R;
-
-                       /* 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(sn+" 249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
-                               results.push_back(sn+" 249 "+user->nick+" :Signals:          "+ConvToStr(R.ru_nsignals));
-                               results.push_back(sn+" 249 "+user->nick+" :Page faults:      "+ConvToStr(R.ru_majflt));
-                               results.push_back(sn+" 249 "+user->nick+" :Swaps:            "+ConvToStr(R.ru_nswap));
-                               results.push_back(sn+" 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);
-                               float per = (n_eaten / n_elapsed) * 100;
-
-                               snprintf(percent, 30, "%03.5f%%", per);
-                               results.push_back(sn+" 249 "+user->nick+" :CPU Use (now):    "+percent);
-
-                               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(sn+" 249 "+user->nick+" :CPU Use (total):  "+percent);
-                       }
-#else
-                       PROCESS_MEMORY_COUNTERS MemCounters;
-                       if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
-                       {
-                               results.push_back(sn+" 249 "+user->nick+" :Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
-                               results.push_back(sn+" 249 "+user->nick+" :Pagefile usage:   "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
-                               results.push_back(sn+" 249 "+user->nick+" :Page faults:      "+ConvToStr(MemCounters.PageFaultCount));
-                       }
-
-                       FILETIME CreationTime;
-               FILETIME ExitTime;
-               FILETIME KernelTime;
-               FILETIME UserTime;
-                       LARGE_INTEGER ThisSample;
-                       if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) &&
-                               QueryPerformanceCounter(&ThisSample))
-                       {
-                               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 per = (n_eaten/n_elapsed);
-                               
-                               char percent[30];
-
-                               snprintf(percent, 30, "%03.5f%%", per);
-                               results.push_back(sn+" 249 "+user->nick+" :CPU Use (now):    "+percent);
-
-                               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(sn+" 249 "+user->nick+" :CPU Use (total):  "+percent);
-                       }
-#endif
-               }
-               break;
-
-               case 'T':
-               {
-                       char buffer[MAXBUF];
-                       results.push_back(sn+" 249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats->statsAccept)+" refused "+ConvToStr(ServerInstance->stats->statsRefused));
-                       results.push_back(sn+" 249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats->statsUnknown));
-                       results.push_back(sn+" 249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats->statsCollisions));
-                       results.push_back(sn+" 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(sn+" 249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats->statsConnects));
-                       snprintf(buffer,MAXBUF," 249 %s :bytes sent %5.2fK recv %5.2fK",
-                               user->nick.c_str(),ServerInstance->stats->statsSent / 1024.0,ServerInstance->stats->statsRecv / 1024.0);
-                       results.push_back(sn+buffer);
-               }
-               break;
-
-               /* stats o */
-               case 'o':
-               {
-                       ConfigTagList tags = ServerInstance->Config->ConfTags("oper");
-                       for(ConfigIter i = tags.first; i != tags.second; ++i)
-                       {
-                               ConfigTag* tag = i->second;
-                               results.push_back(sn+" 243 "+user->nick+" O "+tag->getString("host")+" * "+
-                                       tag->getString("name") + " " + tag->getString("type")+" 0");
-                       }
-               }
-               break;
-               case 'O':
-               {
-                       for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
-                       {
-                               // just the types, not the actual oper blocks...
-                               if (i->first[0] != ' ')
-                                       continue;
-                               OperInfo* tag = i->second;
-                               tag->init();
-                               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() && tag->AllowedUserModes[c - 'A'])
-                                               umodes.push_back(c);
-                                       mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
-                                       if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
-                                               cmodes.push_back(c);
-                               }
-                               results.push_back(sn+" 243 "+user->nick+" O "+tag->NameStr() + " " + umodes + " " + cmodes);
-                       }
-               }
-               break;
-
-               /* stats l (show user I/O stats) */
-               case 'l':
-                       results.push_back(sn+" 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(sn+" 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->signon));
-                       }
-               break;
-
-               /* stats L (show user I/O stats with IP addresses) */
-               case 'L':
-                       results.push_back(sn+" 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(sn+" 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->signon));
-                       }
-               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)
-                       {
-                               char buffer[MAXBUF];
-                               snprintf(buffer,MAXBUF," 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);
-                               results.push_back(sn+buffer);
-                       }
-                       else
-                       {
-                               char buffer[MAXBUF];
-                               snprintf(buffer,MAXBUF," 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);
-                               results.push_back(sn+buffer);
-                       }
-               }
-               break;
-
-               default:
-               break;
-       }
-
-       results.push_back(sn+" 219 "+user->nick+" "+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;
-}
-
-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) && (!IS_OPER(user)))
-                       localuser->CommandFloodPenalty += 2000;
-               return CMD_SUCCESS;
-       }
-       string_list values;
-       char search = parameters[0][0];
-       DoStats(search, user, values);
-       for (size_t i = 0; i < values.size(); i++)
-               user->SendText(":%s", values[i].c_str());
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandStats)
diff --git a/src/commands/cmd_time.cpp b/src/commands/cmd_time.cpp
deleted file mode 100644 (file)
index db452d3..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /TIME. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandTime : public Command
-{
- public:
-       /** Constructor for time.
-        */
-       CommandTime ( Module* parent) : Command(parent,"TIME",0,0) { syntax = "[<servername>]"; }
-       /** 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);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
-       {
-               if (parameters.size() > 0)
-                       return ROUTE_UNICAST(parameters[0]);
-               return ROUTE_LOCALONLY;
-       }
-};
-
-CmdResult CommandTime::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
-               return CMD_SUCCESS;
-       struct tm* timeinfo;
-       time_t local = ServerInstance->Time();
-
-       timeinfo = localtime(&local);
-
-       char tms[26];
-       snprintf(tms,26,"%s",asctime(timeinfo));
-       tms[24] = 0;
-
-       user->SendText(":%s %03d %s %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_TIME, user->nick.c_str(),ServerInstance->Config->ServerName.c_str(),tms);
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandTime)
diff --git a/src/commands/cmd_topic.cpp b/src/commands/cmd_topic.cpp
deleted file mode 100644 (file)
index 412ca1c..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2008 Thomas Stagner <aquanight@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"
-
-/** Handle /TOPIC. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandTopic : public Command
-{
- public:
-       /** Constructor for topic.
-        */
-       CommandTopic ( Module* parent) : Command(parent,"TOPIC",1, 2) { syntax = "<channel> [<topic>]"; Penalty = 2; }
-       /** 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);
-};
-
-CmdResult CommandTopic::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       Channel* c;
-
-       c = ServerInstance->FindChan(parameters[0]);
-       if (!c)
-       {
-               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-
-       if (parameters.size() == 1)
-       {
-               if (c)
-               {
-                       if ((c->IsModeSet('s')) && (!c->HasUser(user)))
-                       {
-                               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), c->name.c_str());
-                               return CMD_FAILURE;
-                       }
-
-                       if (c->topic.length())
-                       {
-                               user->WriteNumeric(332, "%s %s :%s", user->nick.c_str(), c->name.c_str(), c->topic.c_str());
-                               user->WriteNumeric(333, "%s %s %s %lu", user->nick.c_str(), c->name.c_str(), c->setby.c_str(), (unsigned long)c->topicset);
-                       }
-                       else
-                       {
-                               user->WriteNumeric(RPL_NOTOPICSET, "%s %s :No topic is set.", user->nick.c_str(), c->name.c_str());
-                       }
-               }
-               return CMD_SUCCESS;
-       }
-       else if (parameters.size()>1)
-       {
-               std::string t = parameters[1]; // needed, in case a module wants to change it
-               c->SetTopic(user, t);
-       }
-
-       return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandTopic)
diff --git a/src/commands/cmd_unloadmodule.cpp b/src/commands/cmd_unloadmodule.cpp
deleted file mode 100644 (file)
index 6d0f5f4..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /UNLOADMODULE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandUnloadmodule : public Command
-{
- public:
-       /** Constructor for unloadmodule.
-        */
-       CommandUnloadmodule ( Module* parent) : Command(parent,"UNLOADMODULE",1) { flags_needed = 'o'; syntax = "<modulename>"; }
-       /** 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);
-};
-
-CmdResult CommandUnloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       if (parameters[0] == "cmd_unloadmodule.so" || parameters[0] == "cmd_loadmodule.so")
-       {
-               user->WriteNumeric(972, "%s %s :You cannot unload module loading commands!", user->nick.c_str(), parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-               
-       Module* m = ServerInstance->Modules->Find(parameters[0]);
-       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(973, "%s %s :Module successfully unloaded.",user->nick.c_str(), parameters[0].c_str());
-       }
-       else
-       {
-               user->WriteNumeric(972, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(),
-                       m ? ServerInstance->Modules->LastError().c_str() : "No such module");
-               return CMD_FAILURE;
-       }
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandUnloadmodule)
diff --git a/src/commands/cmd_user.cpp b/src/commands/cmd_user.cpp
deleted file mode 100644 (file)
index 09e1e33..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /USER. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandUser : public SplitCommand
-{
- public:
-       /** Constructor for user.
-        */
-       CommandUser ( Module* parent) : SplitCommand(parent,"USER",4,4) { works_before_reg = true; Penalty = 0; syntax = "<username> <localhost> <remotehost> <GECOS>"; }
-       /** 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 HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
-};
-
-CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
-{
-       /* A user may only send the USER command once */
-       if (!(user->registered & REG_USER))
-       {
-               if (!ServerInstance->IsIdent(parameters[0].c_str()))
-               {
-                       /*
-                        * 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(461, "%s USER :Your username is not valid",user->nick.c_str());
-                       return CMD_FAILURE;
-               }
-               else
-               {
-                       /*
-                        * The ident field is IDENTMAX+2 in size to account for +1 for the optional
-                        * ~ character, and +1 for null termination, therefore we can safely use up to
-                        * IDENTMAX here.
-                        */
-                       user->ChangeIdent(parameters[0].c_str());
-                       user->fullname.assign(parameters[3].empty() ? "No info" : parameters[3], 0, ServerInstance->Config->Limits.MaxGecos);
-                       user->registered = (user->registered | REG_USER);
-               }
-       }
-       else
-       {
-               user->CommandFloodPenalty += 1000;
-               user->WriteNumeric(462, "%s :You may not reregister", user->nick.c_str());
-               return CMD_FAILURE;
-       }
-
-       /* parameters 2 and 3 are local and remote hosts, and are ignored */
-       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;
-}
-
-COMMAND_INIT(CommandUser)
diff --git a/src/commands/cmd_userhost.cpp b/src/commands/cmd_userhost.cpp
deleted file mode 100644 (file)
index 7e1efd6..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /USERHOST. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandUserhost : public Command
-{
- public:
-       /** Constructor for userhost.
-        */
-       CommandUserhost ( Module* parent) : Command(parent,"USERHOST", 1) {
-               syntax = "<nick> [<nick> ...]";
-       }
-       /** 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);
-};
-
-CmdResult CommandUserhost::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       std::string retbuf = "302 " + user->nick + " :";
-
-       unsigned int max = parameters.size();
-       if (max > 5)
-               max = 5;
-
-       bool has_privs = user->HasPrivPermission("users/auspex");
-       for (unsigned int i = 0; i < max; i++)
-       {
-               User *u = ServerInstance->FindNickOnly(parameters[i]);
-
-               if ((u) && (u->registered == REG_ALL))
-               {
-                       retbuf = retbuf + u->nick;
-
-                       if (IS_OPER(u))
-                       {
-                               // XXX: +H hidden opers must not be shown as opers
-                               ModeHandler* mh = ServerInstance->Modes->FindMode('H', MODETYPE_USER);
-                               if ((u == user) || (has_privs) || (!mh) || (!u->IsModeSet('H')) || (mh->name != "hideoper"))
-                                       retbuf += '*';
-                       }
-
-                       retbuf = retbuf + "=";
-
-                       if (IS_AWAY(u))
-                               retbuf += "-";
-                       else
-                               retbuf += "+";
-
-                       retbuf = retbuf + u->ident + "@";
-
-                       if (has_privs)
-                       {
-                               retbuf = retbuf + u->host;
-                       }
-                       else
-                       {
-                               retbuf = retbuf + u->dhost;
-                       }
-
-                       retbuf = retbuf + " ";
-               }
-       }
-
-       user->WriteServ(retbuf);
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandUserhost)
diff --git a/src/commands/cmd_version.cpp b/src/commands/cmd_version.cpp
deleted file mode 100644 (file)
index 7620197..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /VERSION. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandVersion : public Command
-{
- public:
-       /** Constructor for version.
-        */
-       CommandVersion ( Module* parent) : Command(parent,"VERSION",0,0) { syntax = "[<servername>]"; }
-       /** 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);
-};
-
-CmdResult CommandVersion::Handle (const std::vector<std::string>&, User *user)
-{
-       std::string version = ServerInstance->GetVersionString(IS_OPER(user));
-       user->WriteNumeric(RPL_VERSION, "%s :%s", user->nick.c_str(), version.c_str());
-       ServerInstance->Config->Send005(user);
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandVersion)
diff --git a/src/commands/cmd_wallops.cpp b/src/commands/cmd_wallops.cpp
deleted file mode 100644 (file)
index 198997a..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /WALLOPS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandWallops : public Command
-{
- public:
-       /** Constructor for wallops.
-        */
-       CommandWallops ( Module* parent) : Command(parent,"WALLOPS",1,1) { flags_needed = 'o'; syntax = "<any-text>"; }
-       /** 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);
-};
-
-CmdResult CommandWallops::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       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++)
-       {
-               User* t = *i;
-               if (t->IsModeSet('w'))
-                       user->WriteTo(t,wallop);
-       }
-
-       FOREACH_MOD(I_OnWallops,OnWallops(user,parameters[0]));
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandWallops)
diff --git a/src/commands/cmd_who.cpp b/src/commands/cmd_who.cpp
deleted file mode 100644 (file)
index 1a18ab4..0000000
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /WHO. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandWho : public Command
-{
-       bool CanView(Channel* chan, User* user);
-       bool opt_viewopersonly;
-       bool opt_showrealhost;
-       bool opt_realname;
-       bool opt_mode;
-       bool opt_ident;
-       bool opt_metadata;
-       bool opt_port;
-       bool opt_away;
-       bool opt_local;
-       bool opt_far;
-       bool opt_time;
-
- public:
-       /** Constructor for who.
-        */
-       CommandWho ( Module* parent) : Command(parent,"WHO", 1) {
-               syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [afhilMmoprt]";
-       }
-       void SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string &initial, Channel* ch, User* u, std::vector<std::string> &whoresults);
-       /** 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);
-       bool whomatch(User* cuser, User* user, const char* matchtext);
-};
-
-
-static Channel* get_first_visible_channel(User *source, User *u)
-{
-       UCListIter i = u->chans.begin();
-       while (i != u->chans.end())
-       {
-               Channel* c = *i++;
-
-               /* XXX move the +I check into m_hidechans */
-               if (source == u || !(c->IsModeSet('s') || c->IsModeSet('p') || u->IsModeSet('I')) || c->HasUser(source))
-                       return c;
-       }
-       return NULL;
-}
-
-bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext)
-{
-       bool match = false;
-       bool positive = false;
-
-       if (user->registered != REG_ALL)
-               return false;
-
-       if (opt_local && !IS_LOCAL(user))
-               return false;
-       else if (opt_far && IS_LOCAL(user))
-               return false;
-
-       if (opt_mode)
-       {
-               for (const char* n = matchtext; *n; n++)
-               {
-                       if (*n == '+')
-                       {
-                               positive = true;
-                               continue;
-                       }
-                       else if (*n == '-')
-                       {
-                               positive = false;
-                               continue;
-                       }
-                       if (user->IsModeSet(*n) != positive)
-                               return false;
-               }
-               return true;
-       }
-       else
-       {
-               /*
-                * This was previously one awesome pile of ugly nested if, when really, it didn't need
-                * to be, since only one condition was ever checked, a chained if works just fine.
-                * -- w00t
-                */
-               if (opt_metadata)
-               {
-                       match = false;
-                       const Extensible::ExtensibleStore& list = user->GetExtList();
-                       for(Extensible::ExtensibleStore::const_iterator i = list.begin(); i != list.end(); ++i)
-                               if (InspIRCd::Match(i->first->name, matchtext))
-                                       match = true;
-               }
-               else if (opt_realname)
-                       match = InspIRCd::Match(user->fullname, matchtext);
-               else if (opt_showrealhost)
-                       match = InspIRCd::Match(user->host, matchtext, ascii_case_insensitive_map);
-               else if (opt_ident)
-                       match = InspIRCd::Match(user->ident, matchtext, ascii_case_insensitive_map);
-               else if (opt_port)
-               {
-                       irc::portparser portrange(matchtext, false);
-                       long portno = -1;
-                       while ((portno = portrange.GetToken()))
-                               if (IS_LOCAL(user) && portno == IS_LOCAL(user)->GetServerPort())
-                               {
-                                       match = true;
-                                       break;
-                               }
-               }
-               else if (opt_away)
-                       match = InspIRCd::Match(user->awaymsg, matchtext);
-               else if (opt_time)
-               {
-                       long seconds = ServerInstance->Duration(matchtext);
-
-                       // Okay, so time matching, we want all users connected `seconds' ago
-                       if (user->signon >= ServerInstance->Time() - seconds)
-                               match = true;
-               }
-
-               /*
-                * Once the conditionals have been checked, only check dhost/nick/server
-                * if they didn't match this user -- and only match if we don't find a match.
-                *
-                * This should make things minutely faster, and again, less ugly.
-                * -- w00t
-                */
-               if (!match)
-                       match = InspIRCd::Match(user->dhost, matchtext, ascii_case_insensitive_map);
-
-               if (!match)
-                       match = InspIRCd::Match(user->nick, matchtext);
-
-               /* Don't allow server name matches if HideWhoisServer is enabled, unless the command user has the priv */
-               if (!match && (ServerInstance->Config->HideWhoisServer.empty() || cuser->HasPrivPermission("users/auspex")))
-                       match = InspIRCd::Match(user->server, matchtext);
-
-               return match;
-       }
-}
-
-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
-        */
-       if (chan->HasUser(user))
-               return true;
-       /* Opers see all */
-       if (user->HasPrivPermission("users/auspex"))
-               return true;
-       /* Cant see inside a +s or a +p channel unless we are a member (see above) */
-       else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
-               return true;
-
-       return false;
-}
-
-void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string &initial, Channel* ch, User* u, std::vector<std::string> &whoresults)
-{
-       if (!ch)
-               ch = get_first_visible_channel(user, u);
-
-       std::string wholine = initial + (ch ? ch->name : "*") + " " + u->ident + " " +
-               (opt_showrealhost ? u->host : u->dhost) + " ";
-       if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
-               wholine.append(ServerInstance->Config->HideWhoisServer);
-       else
-               wholine.append(u->server);
-       
-       wholine.append(" " + u->nick + " ");
-
-       /* away? */
-       if (IS_AWAY(u))
-       {
-               wholine.append("G");
-       }
-       else
-       {
-               wholine.append("H");
-       }
-
-       /* oper? */
-       if (IS_OPER(u))
-       {
-               wholine.push_back('*');
-       }
-
-       if (ch)
-               wholine.append(ch->GetPrefixChar(u));
-
-       wholine.append(" :0 " + u->fullname);
-
-       FOREACH_MOD(I_OnSendWhoLine, OnSendWhoLine(user, parms, u, wholine));
-
-       if (!wholine.empty())
-               whoresults.push_back(wholine);
-}
-
-CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       /*
-        * XXX - RFC says:
-        *   The <name> passed to WHO is matched against users' host, server, real
-        *   name and nickname
-        * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
-        */
-
-       /* WHO options */
-       opt_viewopersonly = false;
-       opt_showrealhost = false;
-       opt_realname = false;
-       opt_mode = false;
-       opt_ident = false;
-       opt_metadata = false;
-       opt_port = false;
-       opt_away = false;
-       opt_local = false;
-       opt_far = false;
-       opt_time = false;
-
-       Channel *ch = NULL;
-       std::vector<std::string> whoresults;
-       std::string initial = "352 " + user->nick + " ";
-
-       char matchtext[MAXBUF];
-       bool usingwildcards = false;
-
-       /* Change '0' into '*' so the wildcard matcher can grok it */
-       if (parameters[0] == "0")
-               strlcpy(matchtext, "*", MAXBUF);
-       else
-               strlcpy(matchtext, parameters[0].c_str(), MAXBUF);
-
-       for (const char* check = matchtext; *check; check++)
-       {
-               if (*check == '*' || *check == '?' || *check == '.')
-               {
-                       usingwildcards = true;
-                       break;
-               }
-       }
-
-       if (parameters.size() > 1)
-       {
-               /* Fix for bug #444, WHO flags count as a wildcard */
-               usingwildcards = true;
-
-               for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
-               {
-                       switch (*iter)
-                       {
-                               case 'o':
-                                       opt_viewopersonly = true;
-                                       break;
-                               case 'h':
-                                       if (user->HasPrivPermission("users/auspex"))
-                                               opt_showrealhost = true;
-                                       break;
-                               case 'r':
-                                       opt_realname = true;
-                                       break;
-                               case 'm':
-                                       if (user->HasPrivPermission("users/auspex"))
-                                               opt_mode = true;
-                                       break;
-                               case 'M':
-                                       if (user->HasPrivPermission("users/auspex"))
-                                               opt_metadata = true;
-                                       break;
-                               case 'i':
-                                       opt_ident = true;
-                                       break;
-                               case 'p':
-                                       if (user->HasPrivPermission("users/auspex"))
-                                               opt_port = true;
-                                       break;
-                               case 'a':
-                                       opt_away = true;
-                                       break;
-                               case 'l':
-                                       if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
-                                               opt_local = true;
-                                       break;
-                               case 'f':
-                                       if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
-                                               opt_far = true;
-                                       break;
-                               case 't':
-                                       opt_time = true;
-                                       break;
-                       }
-               }
-       }
-
-
-       /* who on a channel? */
-       ch = ServerInstance->FindChan(matchtext);
-
-       if (ch)
-       {
-               if (CanView(ch,user))
-               {
-                       bool inside = ch->HasUser(user);
-
-                       /* who on a channel. */
-                       const UserMembList *cu = ch->GetUsers();
-
-                       for (UserMembCIter i = cu->begin(); i != cu->end(); i++)
-                       {
-                               {
-                                       /* opers only, please */
-                                       if (opt_viewopersonly && !IS_OPER(i->first))
-                                               continue;
-
-                                       /* If we're not inside the channel, hide +i users */
-                                       if (!inside && user != i->first && i->first->IsModeSet('i') && !user->HasPrivPermission("users/auspex"))
-                                               continue;
-                               }
-
-                               SendWhoLine(user, parameters, initial, ch, i->first, whoresults);
-                       }
-               }
-       }
-       else
-       {
-               /* Match against wildcard of nick, server or host */
-               if (opt_viewopersonly)
-               {
-                       /* Showing only opers */
-                       for (std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); i++)
-                       {
-                               User* oper = *i;
-
-                               if (whomatch(user, oper, matchtext))
-                               {
-                                       if (!user->SharesChannelWith(oper))
-                                       {
-                                               if (usingwildcards && (oper->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
-                                                       continue;
-                                       }
-
-                                       SendWhoLine(user, parameters, initial, NULL, oper, whoresults);
-                               }
-                       }
-               }
-               else
-               {
-                       for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); i++)
-                       {
-                               if (whomatch(user, i->second, matchtext))
-                               {
-                                       if (!user->SharesChannelWith(i->second))
-                                       {
-                                               if (usingwildcards && (i->second->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
-                                                       continue;
-                                       }
-
-                                       SendWhoLine(user, parameters, initial, 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(315, "%s %s :End of /WHO list.",user->nick.c_str(), *parameters[0].c_str() ? parameters[0].c_str() : "*");
-
-       // Penalize the user a bit for large queries
-       // (add one unit of penalty per 200 results)
-       if (IS_LOCAL(user))
-               IS_LOCAL(user)->CommandFloodPenalty += whoresults.size() * 5;
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandWho)
diff --git a/src/commands/cmd_whois.cpp b/src/commands/cmd_whois.cpp
deleted file mode 100644 (file)
index ab0b82f..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/** Handle /WHOIS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandWhois : public Command
-{
- public:
-       /** Constructor for whois.
-        */
-       CommandWhois ( Module* parent) : Command(parent,"WHOIS",1) { Penalty = 2; syntax = "<nick>{,<nick>}"; }
-       /** 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);
-};
-
-
-CmdResult CommandWhois::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       User *dest;
-       int userindex = 0;
-       unsigned long idle = 0, signon = 0;
-
-       if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
-               return CMD_SUCCESS;
-
-
-       /*
-        * If 2 paramters are specified (/whois nick nick), ignore the first one like spanningtree
-        * does, and use the second one, otherwise, use the only paramter. -- djGrrr
-        */
-       if (parameters.size() > 1)
-               userindex = 1;
-
-       if (IS_LOCAL(user))
-               dest = ServerInstance->FindNickOnly(parameters[userindex]);
-       else
-               dest = ServerInstance->FindNick(parameters[userindex]);
-
-       if ((dest) && (dest->registered == REG_ALL))
-       {
-               /*
-                * Okay. Umpteenth attempt at doing this, so let's re-comment...
-                * For local users (/w localuser), we show idletime if hidewhois is disabled
-                * For local users (/w localuser localuser), we always show idletime, hence parameters.size() > 1 check.
-                * For remote users (/w remoteuser), we do NOT show idletime
-                * For remote users (/w remoteuser remoteuser), spanningtree will handle calling do_whois, so we can ignore this case.
-                * Thanks to djGrrr for not being impatient while I have a crap day coding. :p -- w00t
-                */
-               if (IS_LOCAL(dest) && (ServerInstance->Config->HideWhoisServer.empty() || parameters.size() > 1))
-               {
-                       idle = labs((long)((dest->idle_lastmsg)-ServerInstance->Time()));
-                       signon = dest->signon;
-               }
-
-               ServerInstance->DoWhois(user,dest,signon,idle,parameters[userindex].c_str());
-       }
-       else
-       {
-               /* no such nick/channel */
-               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
-               user->WriteNumeric(318, "%s %s :End of /WHOIS list.",user->nick.c_str(), !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
-               return CMD_FAILURE;
-       }
-
-       return CMD_SUCCESS;
-}
-
-
-
-COMMAND_INIT(CommandWhois)
diff --git a/src/commands/cmd_whowas.cpp b/src/commands/cmd_whowas.cpp
deleted file mode 100644 (file)
index 3a6444b..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 "commands/cmd_whowas.h"
-
-WhoWasMaintainTimer * timer;
-
-CommandWhowas::CommandWhowas( Module* parent) : Command(parent, "WHOWAS", 1)
-{
-       syntax = "<nick>{,<nick>}";
-       Penalty = 2;
-       timer = new WhoWasMaintainTimer(3600);
-       ServerInstance->Timers->AddTimer(timer);
-}
-
-CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user)
-{
-       /* if whowas disabled in config */
-       if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
-       {
-               user->WriteNumeric(421, "%s %s :This command has been disabled.",user->nick.c_str(),name.c_str());
-               return CMD_FAILURE;
-       }
-
-       whowas_users::iterator i = whowas.find(assign(parameters[0]));
-
-       if (i == whowas.end())
-       {
-               user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
-               user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
-               return CMD_FAILURE;
-       }
-       else
-       {
-               whowas_set* grp = i->second;
-               if (grp->size())
-               {
-                       for (whowas_set::iterator ux = grp->begin(); ux != grp->end(); ux++)
-                       {
-                               WhoWasGroup* u = *ux;
-
-                               user->WriteNumeric(314, "%s %s %s %s * :%s",user->nick.c_str(),parameters[0].c_str(),
-                                       u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
-
-                               if (user->HasPrivPermission("users/auspex"))
-                                       user->WriteNumeric(379, "%s %s :was connecting from *@%s",
-                                               user->nick.c_str(), parameters[0].c_str(), u->host.c_str());
-
-                               std::string signon = ServerInstance->TimeString(u->signon);
-                               if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
-                                       user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), ServerInstance->Config->HideWhoisServer.c_str(), signon.c_str());
-                               else
-                                       user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), u->server.c_str(), signon.c_str());
-                       }
-               }
-               else
-               {
-                       user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
-                       user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
-                       return CMD_FAILURE;
-               }
-       }
-
-       user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
-       return CMD_SUCCESS;
-}
-
-std::string CommandWhowas::GetStats()
-{
-       int whowas_size = 0;
-       int whowas_bytes = 0;
-       whowas_users_fifo::iterator iter;
-       for (iter = whowas_fifo.begin(); iter != whowas_fifo.end(); iter++)
-       {
-               whowas_set* n = (whowas_set*)whowas.find(iter->second)->second;
-               if (n->size())
-               {
-                       whowas_size += n->size();
-                       whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
-               }
-       }
-       return "Whowas entries: " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)";
-}
-
-void CommandWhowas::AddToWhoWas(User* user)
-{
-       /* if whowas disabled */
-       if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
-       {
-               return;
-       }
-
-       whowas_users::iterator iter = whowas.find(irc::string(user->nick.c_str()));
-
-       if (iter == whowas.end())
-       {
-               whowas_set* n = new whowas_set;
-               WhoWasGroup *a = new WhoWasGroup(user);
-               n->push_back(a);
-               whowas[user->nick.c_str()] = n;
-               whowas_fifo.push_back(std::make_pair(ServerInstance->Time(),user->nick.c_str()));
-
-               if ((int)(whowas.size()) > ServerInstance->Config->WhoWasMaxGroups)
-               {
-                       whowas_users::iterator iter2 = whowas.find(whowas_fifo[0].second);
-                       if (iter2 != whowas.end())
-                       {
-                               whowas_set* n2 = (whowas_set*)iter2->second;
-
-                               if (n2->size())
-                               {
-                                       while (n2->begin() != n2->end())
-                                       {
-                                               WhoWasGroup *a2 = *(n2->begin());
-                                               delete a2;
-                                               n2->pop_front();
-                                       }
-                               }
-
-                               delete n2;
-                               whowas.erase(iter2);
-                       }
-                       whowas_fifo.pop_front();
-               }
-       }
-       else
-       {
-               whowas_set* group = (whowas_set*)iter->second;
-               WhoWasGroup *a = new WhoWasGroup(user);
-               group->push_back(a);
-
-               if ((int)(group->size()) > ServerInstance->Config->WhoWasGroupSize)
-               {
-                       WhoWasGroup *a2 = (WhoWasGroup*)*(group->begin());
-                       delete a2;
-                       group->pop_front();
-               }
-       }
-}
-
-/* on rehash, refactor maps according to new conf values */
-void CommandWhowas::PruneWhoWas(time_t t)
-{
-       /* config values */
-       int groupsize = ServerInstance->Config->WhoWasGroupSize;
-       int maxgroups = ServerInstance->Config->WhoWasMaxGroups;
-       int maxkeep =   ServerInstance->Config->WhoWasMaxKeep;
-
-       /* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
-       whowas_users::iterator iter;
-       int fifosize;
-       while ((fifosize = (int)whowas_fifo.size()) > 0)
-       {
-               if (fifosize > maxgroups || whowas_fifo[0].first < t - maxkeep)
-               {
-                       iter = whowas.find(whowas_fifo[0].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",DEFAULT, "BUG: Whowas maps got corrupted! (1)");
-                               return;
-                       }
-
-                       whowas_set* n = (whowas_set*)iter->second;
-
-                       if (n->size())
-                       {
-                               while (n->begin() != n->end())
-                               {
-                                       WhoWasGroup *a = *(n->begin());
-                                       delete a;
-                                       n->pop_front();
-                               }
-                       }
-
-                       delete n;
-                       whowas.erase(iter);
-                       whowas_fifo.pop_front();
-               }
-               else
-                       break;
-       }
-
-       /* Then cut the whowas sets to new size (groupsize) */
-       fifosize = (int)whowas_fifo.size();
-       for (int i = 0; i < fifosize; i++)
-       {
-               iter = whowas.find(whowas_fifo[0].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",DEFAULT, "BUG: Whowas maps got corrupted! (2)");
-                       return;
-               }
-               whowas_set* n = (whowas_set*)iter->second;
-               if (n->size())
-               {
-                       int nickcount = n->size();
-                       while (n->begin() != n->end() && nickcount > groupsize)
-                       {
-                               WhoWasGroup *a = *(n->begin());
-                               delete a;
-                               n->pop_front();
-                               nickcount--;
-                       }
-               }
-       }
-}
-
-/* call maintain once an hour to remove expired nicks */
-void CommandWhowas::MaintainWhoWas(time_t t)
-{
-       for (whowas_users::iterator iter = whowas.begin(); iter != whowas.end(); iter++)
-       {
-               whowas_set* n = (whowas_set*)iter->second;
-               if (n->size())
-               {
-                       while ((n->begin() != n->end()) && ((*n->begin())->signon < t - ServerInstance->Config->WhoWasMaxKeep))
-                       {
-                               WhoWasGroup *a = *(n->begin());
-                               delete a;
-                               n->erase(n->begin());
-                       }
-               }
-       }
-}
-
-CommandWhowas::~CommandWhowas()
-{
-       if (timer)
-       {
-               ServerInstance->Timers->DelTimer(timer);
-       }
-
-       whowas_users::iterator iter;
-       int fifosize;
-       while ((fifosize = (int)whowas_fifo.size()) > 0)
-       {
-               iter = whowas.find(whowas_fifo[0].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",DEFAULT, "BUG: Whowas maps got corrupted! (3)");
-                       return;
-               }
-
-               whowas_set* n = (whowas_set*)iter->second;
-
-               if (n->size())
-               {
-                       while (n->begin() != n->end())
-                       {
-                               WhoWasGroup *a = *(n->begin());
-                               delete a;
-                               n->pop_front();
-                       }
-               }
-
-               delete n;
-               whowas.erase(iter);
-               whowas_fifo.pop_front();
-       }
-}
-
-WhoWasGroup::WhoWasGroup(User* user) : host(user->host), dhost(user->dhost), ident(user->ident),
-       server(user->server), gecos(user->fullname), signon(user->signon)
-{
-}
-
-WhoWasGroup::~WhoWasGroup()
-{
-}
-
-/* every hour, run this function which removes all entries older than Config->WhoWasMaxKeep */
-void WhoWasMaintainTimer::Tick(time_t)
-{
-       Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
-       if (whowas)
-       {
-               WhowasRequest(whowas, whowas, WhowasRequest::WHOWAS_MAINTAIN).Send();
-       }
-}
-
-class ModuleWhoWas : public Module
-{
-       CommandWhowas cmd;
- public:
-       ModuleWhoWas() : cmd(this)
-       {
-       }
-
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       void OnRequest(Request& request)
-       {
-               WhowasRequest& req = static_cast<WhowasRequest&>(request);
-               switch (req.type)
-               {
-                       case WhowasRequest::WHOWAS_ADD:
-                               cmd.AddToWhoWas(req.user);
-                               break;
-                       case WhowasRequest::WHOWAS_STATS:
-                               req.value = cmd.GetStats();
-                               break;
-                       case WhowasRequest::WHOWAS_PRUNE:
-                               cmd.PruneWhoWas(ServerInstance->Time());
-                               break;
-                       case WhowasRequest::WHOWAS_MAINTAIN:
-                               cmd.MaintainWhoWas(ServerInstance->Time());
-                               break;
-               }
-       }
-
-       Version GetVersion()
-       {
-               return Version("WHOWAS Command", VF_VENDOR);
-       }
-};
-
-MODULE_INIT(ModuleWhoWas)
diff --git a/src/commands/cmd_zline.cpp b/src/commands/cmd_zline.cpp
deleted file mode 100644 (file)
index 91d9c62..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2009 Matt Smith <dz@inspircd.org>
- *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 "xline.h"
-/** Handle /ZLINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandZline : public Command
-{
- public:
-       /** Constructor for zline.
-        */
-       CommandZline ( Module* parent) : Command(parent,"ZLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<ipmask> [<duration> :<reason>]"; }
-       /** 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);
-};
-
-CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User *user)
-{
-       std::string target = parameters[0];
-
-       if (parameters.size() >= 3)
-       {
-               if (target.find('!') != std::string::npos)
-               {
-                       user->WriteServ("NOTICE %s :*** You cannot include a nickname in a zline, a zline must ban only an IP mask",user->nick.c_str());
-                       return CMD_FAILURE;
-               }
-
-               User *u = ServerInstance->FindNick(target);
-
-               if ((u) && (u->registered == REG_ALL))
-               {
-                       target = u->GetIPString();
-               }
-
-               const char* ipaddr = target.c_str();
-
-               if (strchr(ipaddr,'@'))
-               {
-                       while (*ipaddr != '@')
-                               ipaddr++;
-                       ipaddr++;
-               }
-
-               if (ServerInstance->IPMatchesEveryone(ipaddr,user))
-                       return CMD_FAILURE;
-
-               long duration = ServerInstance->Duration(parameters[1].c_str());
-
-               ZLine* zl = new ZLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ipaddr);
-               if (ServerInstance->XLines->AddLine(zl,user))
-               {
-                       if (!duration)
-                       {
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Z-line for %s: %s", user->nick.c_str(), ipaddr, parameters[2].c_str());
-                       }
-                       else
-                       {
-                               time_t c_requires_crap = duration + ServerInstance->Time();
-                               std::string timestr = ServerInstance->TimeString(c_requires_crap);
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Z-line for %s, expires on %s: %s",user->nick.c_str(),ipaddr,
-                                               timestr.c_str(), parameters[2].c_str());
-                       }
-                       ServerInstance->XLines->ApplyLines();
-               }
-               else
-               {
-                       delete zl;
-                       user->WriteServ("NOTICE %s :*** Z-Line for %s already exists",user->nick.c_str(),ipaddr);
-               }
-       }
-       else
-       {
-               if (ServerInstance->XLines->DelLine(target.c_str(),"Z",user))
-               {
-                       ServerInstance->SNO->WriteToSnoMask('x',"%s removed Z-line on %s",user->nick.c_str(),target.c_str());
-               }
-               else
-               {
-                       user->WriteServ("NOTICE %s :*** Z-Line %s not found in list, try /stats Z.",user->nick.c_str(),target.c_str());
-                       return CMD_FAILURE;
-               }
-       }
-
-       return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandZline)
index 409ebdd395e67fe38de058edd0e823e73d28d3f2..abdf6f3de07e2f0cb58c3f989e750bd6704445e6 100644 (file)
 #include <fstream>
 #include "configparser.h"
 
+enum ParseFlags
+{
+       // Legacy config parsing should be used.
+       FLAG_USE_COMPAT = 1,
+
+       // Executable includes are disabled.
+       FLAG_NO_EXEC = 2,
+
+       // All includes are disabled.
+       FLAG_NO_INC = 4
+};
+
+// Represents the position within a config file.
+struct FilePosition
+{
+       // The name of the file which is being read.
+       std::string name;
+
+       // The line of the file that this position points to.
+       unsigned int line;
+
+       // The column of the file that this position points to.
+       unsigned int column;
+
+       FilePosition(const std::string& Name)
+               : name(Name)
+               , line(1)
+               , column(1)
+       {
+       }
+
+       /** Returns a string that represents this file position. */
+       std::string str()
+       {
+               return name + ":" + ConvToStr(line) + ":" + ConvToStr(column);
+       }
+};
+
+// RAII wrapper for FILE* which closes the file when it goes out of scope.
+class FileWrapper
+{
+ private:
+       // Whether this file handle should be closed with pclose.
+       bool close_with_pclose;
+
+       // The file handle which is being wrapped.
+       FILE* const file;
+
+ public:
+       FileWrapper(FILE* File, bool CloseWithPClose = false)
+               : close_with_pclose(CloseWithPClose)
+               , file(File)
+       {
+       }
+
+       // Operator which determines whether the file is open.
+       operator bool() { return (file != NULL); }
+
+       // Operator which retrieves the underlying FILE pointer.
+       operator FILE*() { return file; }
+
+       ~FileWrapper()
+       {
+               if (!file)
+                       return;
+
+               if (close_with_pclose)
+                       pclose(file);
+               else
+                       fclose(file);
+       }
+};
+
+
 struct Parser
 {
        ParseStack& stack;
        int flags;
        FILE* const file;
-       fpos current;
-       fpos last_tag;
+       FilePosition current;
+       FilePosition last_tag;
        reference<ConfigTag> tag;
        int ungot;
        std::string mandatory_tag;
@@ -52,11 +126,11 @@ struct Parser
                else if (ch == '\n')
                {
                        current.line++;
-                       current.col = 0;
+                       current.column = 0;
                }
                else
                {
-                       current.col++;
+                       current.column++;
                }
                return ch;
        }
@@ -91,7 +165,7 @@ struct Parser
                unget(ch);
        }
 
-       bool kv(std::vector<KeyVal>* items, std::set<std::string>& seen)
+       bool kv(ConfigItems* items)
        {
                std::string key;
                nextword(key);
@@ -119,13 +193,13 @@ struct Parser
                while (1)
                {
                        ch = next();
-                       if (ch == '&' && (flags & FLAG_USE_XML))
+                       if (ch == '&' && !(flags & FLAG_USE_COMPAT))
                        {
                                std::string varname;
                                while (1)
                                {
                                        ch = next();
-                                       if (isalnum(ch))
+                                       if (isalnum(ch) || (varname.empty() && ch == '#'))
                                                varname.push_back(ch);
                                        else if (ch == ';')
                                                break;
@@ -136,12 +210,32 @@ struct Parser
                                                throw CoreException("Parse error");
                                        }
                                }
-                               std::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);
+                               if (varname.empty())
+                                       throw CoreException("Empty XML entity reference");
+                               else if (varname[0] == '#' && (varname.size() == 1 || (varname.size() == 2 && varname[1] == 'x')))
+                                       throw CoreException("Empty numeric character reference");
+                               else if (varname[0] == '#')
+                               {
+                                       const char* cvarname = varname.c_str();
+                                       char* endptr;
+                                       unsigned long lvalue;
+                                       if (cvarname[1] == 'x')
+                                               lvalue = strtoul(cvarname + 2, &endptr, 16);
+                                       else
+                                               lvalue = strtoul(cvarname + 1, &endptr, 10);
+                                       if (*endptr != '\0' || lvalue > 255)
+                                               throw CoreException("Invalid numeric character reference '&" + varname + ";'");
+                                       value.push_back(static_cast<char>(lvalue));
+                               }
+                               else
+                               {
+                                       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);
+                               }
                        }
-                       else if (ch == '\\' && !(flags & FLAG_USE_XML))
+                       else if (ch == '\\' && (flags & FLAG_USE_COMPAT))
                        {
                                int esc = next();
                                if (esc == 'n')
@@ -153,14 +247,14 @@ struct Parser
                        }
                        else if (ch == '"')
                                break;
-                       else
+                       else if (ch != '\r')
                                value.push_back(ch);
                }
 
-               if (!seen.insert(key).second)
+               if (items->find(key) != items->end())
                        throw CoreException("Duplicate key '" + key + "' found");
 
-               items->push_back(KeyVal(key, value));
+               (*items)[key] = value;
                return true;
        }
 
@@ -179,11 +273,13 @@ struct Parser
                if (name.empty())
                        throw CoreException("Empty tag name");
 
-               std::vector<KeyVal>* items;
-               std::set<std::string> seen;
-               tag = ConfigTag::create(name, current.filename, current.line, items);
+               ConfigItems* items;
+               tag = ConfigTag::create(name, current.name, current.line, items);
 
-               while (kv(items, seen));
+               while (kv(items))
+               {
+                       // Do nothing here (silences a GCC warning).
+               }
 
                if (name == mandatory_tag)
                {
@@ -191,27 +287,27 @@ struct Parser
                        mandatory_tag.clear();
                }
 
-               if (name == "include")
+               if (stdalgo::string::equalsci(name, "include"))
                {
                        stack.DoInclude(tag, flags);
                }
-               else if (name == "files")
+               else if (stdalgo::string::equalsci(name, "files"))
                {
-                       for(std::vector<KeyVal>::iterator i = items->begin(); i != items->end(); i++)
+                       for(ConfigItems::iterator i = items->begin(); i != items->end(); i++)
                        {
                                stack.DoReadFile(i->first, i->second, flags, false);
                        }
                }
-               else if (name == "execfiles")
+               else if (stdalgo::string::equalsci(name, "execfiles"))
                {
-                       for(std::vector<KeyVal>::iterator i = items->begin(); i != items->end(); i++)
+                       for(ConfigItems::iterator i = items->begin(); i != items->end(); i++)
                        {
                                stack.DoReadFile(i->first, i->second, flags, true);
                        }
                }
-               else if (name == "define")
+               else if (stdalgo::string::equalsci(name, "define"))
                {
-                       if (!(flags & FLAG_USE_XML))
+                       if (flags & FLAG_USE_COMPAT)
                                throw CoreException("<define> tags may only be used in XML-style config (add <config format=\"xml\">)");
                        std::string varname = tag->getString("name");
                        std::string value = tag->getString("value");
@@ -219,13 +315,13 @@ struct Parser
                                throw CoreException("Variable definition must include variable name");
                        stack.vars[varname] = value;
                }
-               else if (name == "config")
+               else if (stdalgo::string::equalsci(name, "config"))
                {
                        std::string format = tag->getString("format");
-                       if (format == "xml")
-                               flags |= FLAG_USE_XML;
-                       else if (format == "compat")
-                               flags &= ~FLAG_USE_XML;
+                       if (stdalgo::string::equalsci(format, "xml"))
+                               flags &= ~FLAG_USE_COMPAT;
+                       else if (stdalgo::string::equalsci(format, "compat"))
+                               flags |= FLAG_USE_COMPAT;
                        else if (!format.empty())
                                throw CoreException("Unknown configuration format " + format);
                }
@@ -264,7 +360,8 @@ struct Parser
                                                break;
                                        case 0xFE:
                                        case 0xFF:
-                                               stack.errstr << "Do not save your files as UTF-16; use ASCII!\n";
+                                               stack.errstr << "Do not save your files as UTF-16 or UTF-32, use UTF-8!\n";
+                                               /*@fallthrough@*/
                                        default:
                                                throw CoreException("Syntax error - start of tag expected");
                                }
@@ -297,7 +394,7 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags)
                        flags |= FLAG_NO_INC;
                if (tag->getBool("noexec", false))
                        flags |= FLAG_NO_EXEC;
-               if (!ParseFile(name, flags, mandatorytag))
+               if (!ParseFile(ServerInstance->Config->Paths.PrependConfig(name), flags, mandatorytag))
                        throw CoreException("Included");
        }
        else if (tag->readString("executable", name))
@@ -308,7 +405,7 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags)
                        flags |= FLAG_NO_INC;
                if (tag->getBool("noexec", true))
                        flags |= FLAG_NO_EXEC;
-               if (!ParseExec(name, flags, mandatorytag))
+               if (!ParseFile(name, flags, mandatorytag, true))
                        throw CoreException("Included");
        }
 }
@@ -320,14 +417,15 @@ void ParseStack::DoReadFile(const std::string& key, const std::string& name, int
        if (exec && (flags & FLAG_NO_EXEC))
                throw CoreException("Invalid <execfiles> tag in file included with noexec=\"yes\"");
 
-       FileWrapper file(exec ? popen(name.c_str(), "r") : fopen(name.c_str(), "r"), exec);
+       std::string path = ServerInstance->Config->Paths.PrependConfig(name);
+       FileWrapper file(exec ? popen(name.c_str(), "r") : fopen(path.c_str(), "r"), exec);
        if (!file)
-               throw CoreException("Could not read \"" + name + "\" for \"" + key + "\" file");
+               throw CoreException("Could not read \"" + path + "\" for \"" + key + "\" file");
 
        file_cache& cache = FilesOutput[key];
        cache.clear();
 
-       char linebuf[MAXBUF*10];
+       char linebuf[5120];
        while (fgets(linebuf, sizeof(linebuf), file))
        {
                size_t len = strlen(linebuf);
@@ -340,76 +438,35 @@ void ParseStack::DoReadFile(const std::string& key, const std::string& name, int
        }
 }
 
-bool ParseStack::ParseFile(const std::string& name, int flags, const std::string& mandatory_tag)
-{
-       ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading file %s", name.c_str());
-       for (unsigned int t = 0; t < reading.size(); t++)
-       {
-               if (std::string(name) == reading[t])
-               {
-                       throw CoreException("File " + name + " is included recursively (looped inclusion)");
-               }
-       }
-
-       /* It's not already included, add it to the list of files we've loaded */
-
-       FileWrapper file(fopen(name.c_str(), "r"));
-       if (!file)
-               throw CoreException("Could not read \"" + name + "\" for include");
-
-       reading.push_back(name);
-       Parser p(*this, flags, file, name, mandatory_tag);
-       bool ok = p.outer_parse();
-       reading.pop_back();
-       return ok;
-}
-
-bool ParseStack::ParseExec(const std::string& name, int flags, const std::string& mandatory_tag)
+bool ParseStack::ParseFile(const std::string& path, int flags, const std::string& mandatory_tag, bool isexec)
 {
-       ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading executable %s", name.c_str());
-       for (unsigned int t = 0; t < reading.size(); t++)
-       {
-               if (std::string(name) == reading[t])
-               {
-                       throw CoreException("Executable " + name + " is included recursively (looped inclusion)");
-               }
-       }
+       ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Reading (isexec=%d) %s", isexec, path.c_str());
+       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 */
 
-       FileWrapper file(popen(name.c_str(), "r"), true);
+       FileWrapper file((isexec ? popen(path.c_str(), "r") : fopen(path.c_str(), "r")), isexec);
        if (!file)
-               throw CoreException("Could not open executable \"" + name + "\" for include");
+               throw CoreException("Could not read \"" + path + "\" for include");
 
-       reading.push_back(name);
-       Parser p(*this, flags, file, name, mandatory_tag);
+       reading.push_back(path);
+       Parser p(*this, flags, file, path, mandatory_tag);
        bool ok = p.outer_parse();
        reading.pop_back();
        return ok;
 }
 
-#ifdef __clang__
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wunknown-pragmas"
-# pragma clang diagnostic ignored "-Wundefined-bool-conversion"
-#elif defined __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wpragmas"
-# pragma GCC diagnostic ignored "-Wnonnull-compare"
-#endif
 bool ConfigTag::readString(const std::string& key, std::string& value, bool allow_lf)
 {
-       // TODO: this is undefined behaviour but changing the API is too risky for 2.0.
-       if (!this)
-               return false;
-       for(std::vector<KeyVal>::iterator j = items.begin(); j != items.end(); ++j)
+       for(ConfigItems::iterator j = items.begin(); j != items.end(); ++j)
        {
                if(j->first != key)
                        continue;
                value = j->second;
                if (!allow_lf && (value.find('\n') != std::string::npos))
                {
-                       ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
+                       ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
                                " contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");
                        for (std::string::iterator n = value.begin(); n != value.end(); n++)
                                if (*n == '\n')
@@ -419,20 +476,100 @@ bool ConfigTag::readString(const std::string& key, std::string& value, bool allo
        }
        return false;
 }
-#ifdef __clang__
-# pragma clang diagnostic pop
-#elif defined __GNUC__
-# pragma GCC diagnostic pop
-#endif
 
-std::string ConfigTag::getString(const std::string& key, const std::string& def)
+std::string ConfigTag::getString(const std::string& key, const std::string& def, const TR1NS::function<bool(const std::string&)>& validator)
+{
+       std::string res;
+       if (!readString(key, res))
+               return def;
+
+       if (!validator(res))
+       {
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: The value of <%s:%s> is not valid; value set to %s.",
+                       tag.c_str(), key.c_str(), def.c_str());
+               return def;
+       }
+       return res;
+}
+
+std::string ConfigTag::getString(const std::string& key, const std::string& def, size_t minlen, size_t maxlen)
 {
-       std::string res = def;
-       readString(key, res);
+       std::string res;
+       if (!readString(key, res))
+               return def;
+
+       if (res.length() < minlen || res.length() > maxlen)
+       {
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: The length of <%s:%s> is not between %ld and %ld; value set to %s.",
+                       tag.c_str(), key.c_str(), minlen, maxlen, def.c_str());
+               return def;
+       }
        return res;
 }
 
-long ConfigTag::getInt(const std::string &key, long def)
+namespace
+{
+       /** Check for an invalid magnitude specifier. If one is found a warning is logged and the
+        * value is corrected (set to \p def).
+        * @param tag The tag name; used in the warning message.
+        * @param key The key name; used in the warning message.
+        * @param val The full value set in the config as a string.
+        * @param num The value to verify and modify if needed.
+        * @param def The default value, \p res will be set to this if \p tail does not contain a.
+        *            valid magnitude specifier.
+        * @param tail The location in the config value at which the magnifier is located.
+        */
+       template <typename Numeric>
+       void CheckMagnitude(const std::string& tag, const std::string& key, const std::string& val, Numeric& num, Numeric def, const char* tail)
+       {
+               // If this is NULL then no magnitude specifier was given.
+               if (!*tail)
+                       return;
+
+               switch (toupper(*tail))
+               {
+                       case 'K':
+                               num *= 1024;
+                               return;
+
+                       case 'M':
+                               num *= 1024 * 1024;
+                               return;
+
+                       case 'G':
+                               num *= 1024 * 1024 * 1024;
+                               return;
+               }
+
+               const std::string message = "WARNING: <" + tag + ":" + key + "> value of " + val + " contains an invalid magnitude specifier '"
+                       + tail + "'; value set to " + ConvToStr(def) + ".";
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, message);
+               num = def;
+       }
+
+       /** Check for an out of range value. If the value falls outside the boundaries a warning is
+        * logged and the value is corrected (set to \p def).
+        * @param tag The tag name; used in the warning message.
+        * @param key The key name; used in the warning message.
+        * @param num The value to verify and modify if needed.
+        * @param def The default value, \p res will be set to this if (min <= res <= max) doesn't hold true.
+        * @param min Minimum accepted value for \p res.
+        * @param max Maximum accepted value for \p res.
+        */
+       template <typename Numeric>
+       void CheckRange(const std::string& tag, const std::string& key, Numeric& num, Numeric def, Numeric min, Numeric max)
+       {
+               if (num >= min && num <= max)
+                       return;
+
+               const std::string message = "WARNING: <" + tag + ":" + key + "> value of " + ConvToStr(num) + " is not between "
+                       + ConvToStr(min) + " and " + ConvToStr(max) + "; value set to " + ConvToStr(def) + ".";
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, message);
+               num = def;
+       }
+}
+
+long ConfigTag::getInt(const std::string &key, long def, long min, long max)
 {
        std::string result;
        if(!readString(key, result))
@@ -443,27 +580,56 @@ long ConfigTag::getInt(const std::string &key, long def)
        long res = strtol(res_cstr, &res_tail, 0);
        if (res_tail == res_cstr)
                return def;
-       switch (toupper(*res_tail))
+
+       CheckMagnitude(tag, key, result, res, def, res_tail);
+       CheckRange(tag, key, res, def, min, max);
+       return res;
+}
+
+unsigned long ConfigTag::getUInt(const std::string& key, unsigned long def, unsigned long min, unsigned long max)
+{
+       std::string result;
+       if (!readString(key, result))
+               return def;
+
+       const char* res_cstr = result.c_str();
+       char* res_tail = NULL;
+       unsigned long res = strtoul(res_cstr, &res_tail, 0);
+       if (res_tail == res_cstr)
+               return def;
+
+       CheckMagnitude(tag, key, result, res, def, res_tail);
+       CheckRange(tag, key, res, def, min, max);
+       return res;
+}
+
+unsigned long ConfigTag::getDuration(const std::string& key, unsigned long def, unsigned long min, unsigned long max)
+{
+       std::string duration;
+       if (!readString(key, duration))
+               return def;
+
+       unsigned long ret;
+       if (!InspIRCd::Duration(duration, ret))
        {
-               case 'K':
-                       res= res* 1024;
-                       break;
-               case 'M':
-                       res= res* 1024 * 1024;
-                       break;
-               case 'G':
-                       res= res* 1024 * 1024 * 1024;
-                       break;
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
+                       " is not a duration; value set to " + ConvToStr(def) + ".");
+               return def;
        }
-       return res;
+
+       CheckRange(tag, key, ret, def, min, max);
+       return ret;
 }
 
-double ConfigTag::getFloat(const std::string &key, double def)
+double ConfigTag::getFloat(const std::string& key, double def, double min, double max)
 {
        std::string result;
        if (!readString(key, result))
                return def;
-       return strtod(result.c_str(), NULL);
+
+       double res = strtod(result.c_str(), NULL);
+       CheckRange(tag, key, res, def, min, max);
+       return res;
 }
 
 bool ConfigTag::getBool(const std::string &key, bool def)
@@ -477,7 +643,7 @@ bool ConfigTag::getBool(const std::string &key, bool def)
        if (result == "no" || result == "false" || result == "0" || result == "off")
                return false;
 
-       ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
+       ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
                " is not valid, ignoring");
        return def;
 }
@@ -487,7 +653,7 @@ std::string ConfigTag::getTagLocation()
        return src_name + ":" + ConvToStr(src_line);
 }
 
-ConfigTag* ConfigTag::create(const std::string& Tag, const std::string& file, int line, std::vector<KeyVal>*& Items)
+ConfigTag* ConfigTag::create(const std::string& Tag, const std::string& file, int line, ConfigItems*& Items)
 {
        ConfigTag* rv = new ConfigTag(Tag, file, line);
        Items = &rv->items;
@@ -499,6 +665,11 @@ ConfigTag::ConfigTag(const std::string& Tag, const std::string& file, int line)
 {
 }
 
+OperInfo::OperInfo(const std::string& Name)
+       : name(Name)
+{
+}
+
 std::string OperInfo::getConfig(const std::string& key)
 {
        std::string rv;
index 301db14e87187c849db0150bd8221461ee7f1386..0318dd602cb9f071dd9ca5a09369117046e6f23e 100644 (file)
 
 
 #include "inspircd.h"
-#include <fstream>
 #include "xline.h"
+#include "listmode.h"
 #include "exitcodes.h"
-#include "commands/cmd_whowas.h"
 #include "configparser.h"
 #include <iostream>
-#ifdef _WIN32
-#include <Iphlpapi.h>
-#pragma comment(lib, "Iphlpapi.lib")
-#endif
 
-ServerConfig::ServerConfig()
-       : NoSnoticeStack(false)
+ServerLimits::ServerLimits(ConfigTag* tag)
+       : NickMax(tag->getUInt("maxnick", 30))
+       , ChanMax(tag->getUInt("maxchan", 64))
+       , MaxModes(tag->getUInt("maxmodes", 20))
+       , IdentMax(tag->getUInt("maxident", 10))
+       , MaxQuit(tag->getUInt("maxquit", 255))
+       , MaxTopic(tag->getUInt("maxtopic", 307))
+       , MaxKick(tag->getUInt("maxkick", 255))
+       , MaxReal(tag->getUInt("maxreal", tag->getUInt("maxgecos", 128)))
+       , MaxAway(tag->getUInt("maxaway", 200))
+       , MaxLine(tag->getUInt("maxline", 512))
+       , MaxHost(tag->getUInt("maxhost", 64))
 {
-       WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
-       RawLog = NoUserDns = HideBans = HideSplits = UndernetMsgPrefix = false;
-       WildcardIPv6 = CycleHosts = InvBypassModes = true;
-       dns_timeout = 5;
-       MaxTargets = 20;
-       NetBufferSize = 10240;
-       SoftLimit = ServerInstance->SE->GetMaxFds();
-       MaxConn = SOMAXCONN;
-       MaxChans = 20;
-       OperMaxChans = 30;
-       c_ipv4_range = 32;
-       c_ipv6_range = 128;
-
-       std::vector<KeyVal>* items;
-       EmptyTag = ConfigTag::create("empty", "<auto>", 0, items);
 }
 
-ServerConfig::~ServerConfig()
+ServerConfig::ServerPaths::ServerPaths(ConfigTag* tag)
+       : Config(tag->getString("configdir", INSPIRCD_CONFIG_PATH))
+       , Data(tag->getString("datadir", INSPIRCD_DATA_PATH))
+       , Log(tag->getString("logdir", INSPIRCD_LOG_PATH))
+       , Module(tag->getString("moduledir", INSPIRCD_MODULE_PATH))
 {
-       delete EmptyTag;
 }
 
-void ServerConfig::Update005()
+static ConfigTag* CreateEmptyTag()
 {
-       std::stringstream out(data005);
-       std::vector<std::string> data;
-       std::string token;
-       while (out >> token)
-               data.push_back(token);
-       sort(data.begin(), data.end());
-
-       std::string line5;
-       isupport.clear();
-       for(unsigned int i=0; i < data.size(); i++)
-       {
-               token = data[i];
-               line5 = line5 + token + " ";
-               if (i % 13 == 12)
-               {
-                       line5.append(":are supported by this server");
-                       isupport.push_back(line5);
-                       line5.clear();
-               }
-       }
-       if (!line5.empty())
-       {
-               line5.append(":are supported by this server");
-               isupport.push_back(line5);
-       }
-}
-
-void ServerConfig::Send005(User* user)
-{
-       for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
-               user->WriteNumeric(RPL_ISUPPORT, "%s %s", user->nick.c_str(), line->c_str());
-}
-
-template<typename T, typename V>
-static void range(T& value, V min, V max, V def, const char* msg)
-{
-       if (value >= (T)min && value <= (T)max)
-               return;
-       ServerInstance->Logs->Log("CONFIG", DEFAULT,
-               "WARNING: %s value of %ld is not between %ld and %ld; set to %ld.",
-               msg, (long)value, (long)min, (long)max, (long)def);
-       value = def;
+       ConfigItems* items;
+       return ConfigTag::create("empty", "<auto>", 0, items);
 }
 
-
-static void ValidIP(const std::string& ip, const std::string& key)
-{
-       irc::sockets::sockaddrs dummy;
-       if (!irc::sockets::aptosa(ip, 0, dummy))
-               throw CoreException("The value of "+key+" is not an IP address");
-}
-
-static void ValidHost(const std::string& p, const std::string& msg)
-{
-       int num_dots = 0;
-       if (p.empty() || p[0] == '.')
-               throw CoreException("The value of "+msg+" is not a valid hostname");
-       for (unsigned int i=0;i < p.length();i++)
-       {
-               switch (p[i])
-               {
-                       case ' ':
-                               throw CoreException("The value of "+msg+" is not a valid hostname");
-                       case '.':
-                               num_dots++;
-                       break;
-               }
-       }
-       if (num_dots == 0)
-               throw CoreException("The value of "+msg+" is not a valid hostname");
-}
-
-bool ServerConfig::ApplyDisabledCommands(const std::string& data)
+ServerConfig::ServerConfig()
+       : EmptyTag(CreateEmptyTag())
+       , Limits(EmptyTag)
+       , Paths(EmptyTag)
+       , RawLog(false)
+       , NoSnoticeStack(false)
 {
-       std::stringstream dcmds(data);
-       std::string thiscmd;
-
-       /* Enable everything first */
-       for (Commandtable::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.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);
-               }
-       }
-       return true;
 }
 
-static void FindDNS(std::string& server)
+ServerConfig::~ServerConfig()
 {
-       if (!server.empty())
-               return;
-#ifdef _WIN32
-       // attempt to look up their nameserver from the system
-       ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
-
-       PFIXED_INFO pFixedInfo;
-       DWORD dwBufferSize = sizeof(FIXED_INFO);
-       pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
-
-       if(pFixedInfo)
-       {
-               if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW) {
-                       HeapFree(GetProcessHeap(), 0, pFixedInfo);
-                       pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
-               }
-
-               if(pFixedInfo) {
-                       if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
-                               server = pFixedInfo->DnsServerList.IpAddress.String;
-
-                       HeapFree(GetProcessHeap(), 0, pFixedInfo);
-               }
-
-               if(!server.empty())
-               {
-                       ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first active resolver in the system settings.", server.c_str());
-                       return;
-               }
-       }
-
-       ServerInstance->Logs->Log("CONFIG",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",DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
-
-       std::ifstream resolv("/etc/resolv.conf");
-
-       while (resolv >> server)
-       {
-               if (server == "nameserver")
-               {
-                       resolv >> server;
-                       if (server.find_first_not_of("0123456789.") == std::string::npos)
-                       {
-                               ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",server.c_str());
-                               return;
-                       }
-               }
-       }
-
-       ServerInstance->Logs->Log("CONFIG",DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
-#endif
-       server = "127.0.0.1";
+       delete EmptyTag;
 }
 
 static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
 {
+       insp::flat_set<std::string> configlines;
+
        ConfigTagList tags = conf->ConfTags(tag);
        for(ConfigIter i = tags.first; i != tags.second; ++i)
        {
@@ -223,9 +85,13 @@ static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::str
                        throw CoreException("<"+tag+":"+key+"> missing at " + ctag->getTagLocation());
                std::string reason = ctag->getString("reason", "<Config>");
                XLine* xl = make->Generate(ServerInstance->Time(), 0, "<Config>", reason, mask);
+               xl->from_config = true;
+               configlines.insert(xl->Displayable());
                if (!ServerInstance->XLines->AddLine(xl, NULL))
                        delete xl;
        }
+
+       ServerInstance->XLines->ExpireRemovedConfigLines(make->GetType(), configlines);
 }
 
 typedef std::map<std::string, ConfigTag*> LocalIndex;
@@ -250,14 +116,11 @@ void ServerConfig::CrossCheckOperClassType()
                std::string name = tag->getString("name");
                if (name.empty())
                        throw CoreException("<type:name> is missing from tag at " + tag->getTagLocation());
-               if (!ServerInstance->IsNick(name.c_str(), Limits.NickMax))
-                       throw CoreException("<type:name> is invalid (value '" + name + "')");
-               if (oper_blocks.find(" " + name) != oper_blocks.end())
+               if (OperTypes.find(name) != OperTypes.end())
                        throw CoreException("Duplicate type block with name " + name + " at " + tag->getTagLocation());
 
-               OperInfo* ifo = new OperInfo;
-               oper_blocks[" " + name] = ifo;
-               ifo->name = name;
+               OperInfo* ifo = new OperInfo(name);
+               OperTypes[name] = ifo;
                ifo->type_block = tag;
 
                std::string classname;
@@ -281,14 +144,13 @@ void ServerConfig::CrossCheckOperClassType()
                        throw CoreException("<oper:name> missing from tag at " + tag->getTagLocation());
 
                std::string type = tag->getString("type");
-               OperIndex::iterator tblk = oper_blocks.find(" " + type);
-               if (tblk == oper_blocks.end())
+               OperIndex::iterator tblk = OperTypes.find(type);
+               if (tblk == OperTypes.end())
                        throw CoreException("Oper block " + name + " has missing type " + type);
                if (oper_blocks.find(name) != oper_blocks.end())
                        throw CoreException("Duplicate oper block with name " + name + " at " + tag->getTagLocation());
 
-               OperInfo* ifo = new OperInfo;
-               ifo->name = type;
+               OperInfo* ifo = new OperInfo(type);
                ifo->oper_block = tag;
                ifo->type_block = tblk->second->type_block;
                ifo->class_blocks.assign(tblk->second->class_blocks.begin(), tblk->second->class_blocks.end());
@@ -305,7 +167,7 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
                for(ClassVector::iterator i = current->Classes.begin(); i != current->Classes.end(); ++i)
                {
                        ConnectClass* c = *i;
-                       if (c->name.substr(0, 8) != "unnamed-")
+                       if (c->name.compare(0, 8, "unnamed-", 8))
                        {
                                oldBlocksByMask["n" + c->name] = c;
                        }
@@ -318,26 +180,26 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
                }
        }
 
-       int blk_count = config_data.count("connect");
+       size_t blk_count = config_data.count("connect");
        if (blk_count == 0)
        {
                // No connect blocks found; make a trivial default block
-               std::vector<KeyVal>* items;
+               ConfigItems* items;
                ConfigTag* tag = ConfigTag::create("connect", "<auto>", 0, items);
-               items->push_back(std::make_pair("allow", "*"));
+               (*items)["allow"] = "*";
                config_data.insert(std::make_pair("connect", tag));
                blk_count = 1;
        }
 
        Classes.resize(blk_count);
-       std::map<std::string, int> names;
+       std::map<std::string, size_t> names;
 
        bool try_again = true;
-       for(int tries=0; try_again; tries++)
+       for(size_t tries = 0; try_again; tries++)
        {
                try_again = false;
                ConfigTagList tags = ConfTags("connect");
-               int i=0;
+               size_t i = 0;
                for(ConfigIter it = tags.first; it != tags.second; ++it, ++i)
                {
                        ConfigTag* tag = it->second;
@@ -348,7 +210,7 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
                        std::string parentName = tag->getString("parent");
                        if (!parentName.empty())
                        {
-                               std::map<std::string,int>::iterator parentIter = names.find(parentName);
+                               std::map<std::string, size_t>::const_iterator parentIter = names.find(parentName);
                                if (parentIter == names.end())
                                {
                                        try_again = true;
@@ -404,30 +266,39 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
 
                        me->name = name;
 
-                       me->registration_timeout = tag->getInt("timeout", me->registration_timeout);
-                       me->pingtime = tag->getInt("pingfreq", me->pingtime);
+                       me->registration_timeout = tag->getDuration("timeout", me->registration_timeout);
+                       me->pingtime = tag->getDuration("pingfreq", me->pingtime);
                        std::string sendq;
                        if (tag->readString("sendq", sendq))
                        {
                                // attempt to guess a good hard/soft sendq from a single value
-                               long value = atol(sendq.c_str());
+                               unsigned long value = strtoul(sendq.c_str(), NULL, 10);
                                if (value > 16384)
                                        me->softsendqmax = value / 16;
                                else
                                        me->softsendqmax = value;
                                me->hardsendqmax = value * 8;
                        }
-                       me->softsendqmax = tag->getInt("softsendq", me->softsendqmax);
-                       me->hardsendqmax = tag->getInt("hardsendq", me->hardsendqmax);
-                       me->recvqmax = tag->getInt("recvq", me->recvqmax);
-                       me->penaltythreshold = tag->getInt("threshold", me->penaltythreshold);
-                       me->commandrate = tag->getInt("commandrate", me->commandrate);
+                       me->softsendqmax = tag->getUInt("softsendq", me->softsendqmax);
+                       me->hardsendqmax = tag->getUInt("hardsendq", me->hardsendqmax);
+                       me->recvqmax = tag->getUInt("recvq", me->recvqmax);
+                       me->penaltythreshold = tag->getUInt("threshold", me->penaltythreshold);
+                       me->commandrate = tag->getUInt("commandrate", me->commandrate);
                        me->fakelag = tag->getBool("fakelag", me->fakelag);
-                       me->maxlocal = tag->getInt("localmax", me->maxlocal);
-                       me->maxglobal = tag->getInt("globalmax", me->maxglobal);
-                       me->maxchans = tag->getInt("maxchans", me->maxchans);
+                       me->maxlocal = tag->getUInt("localmax", me->maxlocal);
+                       me->maxglobal = tag->getUInt("globalmax", me->maxglobal);
+                       me->maxchans = tag->getUInt("maxchans", me->maxchans);
                        me->maxconnwarn = tag->getBool("maxconnwarn", me->maxconnwarn);
-                       me->limit = tag->getInt("limit", me->limit);
+                       me->limit = tag->getUInt("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())
@@ -443,143 +314,89 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
        }
 }
 
-/** Represents a deprecated configuration tag.
- */
-struct Deprecated
+static std::string GetServerName()
 {
-       /** Tag name
-        */
-       const char* tag;
-       /** Tag value
-        */
-       const char* value;
-       /** Reason for deprecation
-        */
-       const char* reason;
-};
-
-static const Deprecated ChangedConfig[] = {
-       {"options", "hidelinks",                "has been moved to <security:hidelinks> as of 1.2a3"},
-       {"options", "hidewhois",                "has been moved to <security:hidewhois> as of 1.2a3"},
-       {"options", "userstats",                "has been moved to <security:userstats> as of 1.2a3"},
-       {"options", "customversion",    "has been moved to <security:customversion> as of 1.2a3"},
-       {"options", "hidesplits",               "has been moved to <security:hidesplits> as of 1.2a3"},
-       {"options", "hidebans",         "has been moved to <security:hidebans> as of 1.2a3"},
-       {"options", "hidekills",                "has been moved to <security:hidekills> as of 1.2a3"},
-       {"options", "operspywhois",             "has been moved to <security:operspywhois> as of 1.2a3"},
-       {"options", "announceinvites",  "has been moved to <security:announceinvites> as of 1.2a3"},
-       {"options", "hidemodes",                "has been moved to <security:hidemodes> as of 1.2a3"},
-       {"options", "maxtargets",               "has been moved to <security:maxtargets> as of 1.2a3"},
-       {"options",     "nouserdns",            "has been moved to <performance:nouserdns> as of 1.2a3"},
-       {"options",     "maxwho",               "has been moved to <performance:maxwho> as of 1.2a3"},
-       {"options",     "softlimit",            "has been moved to <performance:softlimit> as of 1.2a3"},
-       {"options", "somaxconn",                "has been moved to <performance:somaxconn> as of 1.2a3"},
-       {"options", "netbuffersize",    "has been moved to <performance:netbuffersize> as of 1.2a3"},
-       {"options", "maxwho",           "has been moved to <performance:maxwho> as of 1.2a3"},
-       {"options",     "loglevel",             "1.2+ does not use the loglevel value. Please define <log> tags instead."},
-       {"die",     "value",            "you need to reread your config"},
-       {"bind",    "transport",                "has been moved to <bind:ssl> as of 2.0a1"},
-       {"link",    "transport",                "has been moved to <link:ssl> as of 2.0a1"},
-       {"link",        "autoconnect",          "2.0+ does not use the autoconnect value. Please define <autoconnect> tags instead."},
-};
+#ifndef _WIN32
+       char hostname[256];
+       if (gethostname(hostname, sizeof(hostname)) == 0)
+       {
+               std::string name(hostname);
+               if (name.find('.') == std::string::npos)
+                       name.push_back('.');
+
+               if (name.length() <= ServerInstance->Config->Limits.MaxHost && InspIRCd::IsHost(name))
+                       return name;
+       }
+#endif
+       return "irc.example.com";
+}
 
 void ServerConfig::Fill()
 {
        ConfigTag* options = ConfValue("options");
        ConfigTag* security = ConfValue("security");
+       ConfigTag* server = ConfValue("server");
        if (sid.empty())
        {
-               ServerName = ConfValue("server")->getString("name");
-               sid = ConfValue("server")->getString("id");
-               ValidHost(ServerName, "<server:name>");
-               if (!sid.empty() && !ServerInstance->IsSID(sid))
+               ServerName = server->getString("name", GetServerName(), InspIRCd::IsHost);
+
+               sid = server->getString("id");
+               if (!sid.empty() && !InspIRCd::IsSID(sid))
                        throw CoreException(sid + " is not a valid server ID. A server ID must be 3 characters long, with the first character a digit and the next two characters a digit or letter.");
+
+               CaseMapping = options->getString("casemapping", "rfc1459");
+               if (CaseMapping == "ascii")
+                       national_case_insensitive_map = ascii_case_insensitive_map;
+               else if (CaseMapping == "rfc1459")
+                       national_case_insensitive_map = rfc_case_insensitive_map;
+               else
+                       throw CoreException("<options:casemapping> must be set to 'ascii', or 'rfc1459'");
        }
        else
        {
-               if (ServerName != ConfValue("server")->getString("name"))
-                       throw CoreException("You must restart to change the server name or SID");
-               std::string nsid = ConfValue("server")->getString("id");
+               std::string name = server->getString("name");
+               if (!name.empty() && name != ServerName)
+                       throw CoreException("You must restart to change the server name");
+
+               std::string nsid = server->getString("id");
                if (!nsid.empty() && nsid != sid)
-                       throw CoreException("You must restart to change the server name or SID");
-       }
-       diepass = ConfValue("power")->getString("diepass");
-       restartpass = ConfValue("power")->getString("restartpass");
-       powerhash = ConfValue("power")->getString("hash");
-       PrefixQuit = options->getString("prefixquit");
-       SuffixQuit = options->getString("suffixquit");
-       FixedQuit = options->getString("fixedquit");
-       PrefixPart = options->getString("prefixpart");
-       SuffixPart = options->getString("suffixpart");
-       FixedPart = options->getString("fixedpart");
-       SoftLimit = ConfValue("performance")->getInt("softlimit", ServerInstance->SE->GetMaxFds());
-       MaxConn = ConfValue("performance")->getInt("somaxconn", SOMAXCONN);
-       MoronBanner = options->getString("moronbanner", "You're banned!");
-       ServerDesc = ConfValue("server")->getString("description", "Configure Me");
-       Network = ConfValue("server")->getString("network", "Network");
-       AdminName = ConfValue("admin")->getString("name", "");
-       AdminEmail = ConfValue("admin")->getString("email", "null@example.com");
-       AdminNick = ConfValue("admin")->getString("nick", "admin");
-       ModPath = ConfValue("path")->getString("moduledir", MOD_PATH);
-       NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240);
-       dns_timeout = ConfValue("dns")->getInt("timeout", 5);
-       DisabledCommands = ConfValue("disabled")->getString("commands", "");
-       DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
-       UserStats = security->getString("userstats");
-       CustomVersion = security->getString("customversion", Network + " IRCd");
-       HideSplits = security->getBool("hidesplits");
+                       throw CoreException("You must restart to change the server id");
+
+               std::string casemapping = options->getString("casemapping");
+               if (!casemapping.empty() && casemapping != CaseMapping)
+                       throw CoreException("You must restart to change the server casemapping");
+
+       }
+       SoftLimit = ConfValue("performance")->getUInt("softlimit", (SocketEngine::GetMaxFds() > 0 ? SocketEngine::GetMaxFds() : LONG_MAX), 10);
+       CCOnConnect = ConfValue("performance")->getBool("clonesonconnect", true);
+       MaxConn = ConfValue("performance")->getUInt("somaxconn", SOMAXCONN);
+       TimeSkipWarn = ConfValue("performance")->getDuration("timeskipwarn", 2, 0, 30);
+       XLineMessage = options->getString("xlinemessage", options->getString("moronbanner", "You're banned!"));
+       ServerDesc = server->getString("description", "Configure Me");
+       Network = server->getString("network", "Network");
+       NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240, 1024, 65534);
+       CustomVersion = security->getString("customversion");
        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");
-       NoUserDns = ConfValue("performance")->getBool("nouserdns");
+       HideServer = security->getString("hideserver", security->getString("hidewhois"));
        SyntaxHints = options->getBool("syntaxhints");
-       CycleHosts = options->getBool("cyclehosts");
-       CycleHostsFromUser = options->getBool("cyclehostsfromuser");
-       UndernetMsgPrefix = options->getBool("ircumsgprefix");
        FullHostInTopic = options->getBool("hostintopic");
-       MaxTargets = security->getInt("maxtargets", 20);
-       DefaultModes = options->getString("defaultmodes", "nt");
+       MaxTargets = security->getUInt("maxtargets", 20, 1, 31);
+       DefaultModes = options->getString("defaultmodes", "not");
        PID = ConfValue("pid")->getString("file");
-       WhoWasGroupSize = ConfValue("whowas")->getInt("groupsize");
-       WhoWasMaxGroups = ConfValue("whowas")->getInt("maxgroups");
-       WhoWasMaxKeep = ServerInstance->Duration(ConfValue("whowas")->getString("maxkeep"));
-       MaxChans = ConfValue("channels")->getInt("users", 20);
-       OperMaxChans = ConfValue("channels")->getInt("opers", 60);
-       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.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);
-       InvBypassModes = options->getBool("invitebypassmodes", true);
+       MaxChans = ConfValue("channels")->getUInt("users", 20);
+       OperMaxChans = ConfValue("channels")->getUInt("opers", 0);
+       c_ipv4_range = ConfValue("cidr")->getUInt("ipv4clone", 32, 1, 32);
+       c_ipv6_range = ConfValue("cidr")->getUInt("ipv6clone", 128, 1, 128);
+       Limits = ServerLimits(ConfValue("limits"));
+       Paths = ServerPaths(ConfValue("path"));
        NoSnoticeStack = options->getBool("nosnoticestack", false);
-       WelcomeNotice = options->getBool("welcomenotice", true);
-
-       range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>");
-       if (ConfValue("performance")->getBool("limitsomaxconn", true))
-               range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>");
-       range(MaxTargets, 1, 31, 20, "<security:maxtargets>");
-       range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>");
-       range(WhoWasGroupSize, 0, 10000, 10, "<whowas:groupsize>");
-       range(WhoWasMaxGroups, 0, 1000000, 10240, "<whowas:maxgroups>");
-       range(WhoWasMaxKeep, 3600, INT_MAX, 3600, "<whowas:maxkeep>");
-
-       ValidIP(DNSServer, "<dns:server>");
 
        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;
        }
@@ -590,26 +407,7 @@ void ServerConfig::Fill()
                if (socktest < 0)
                        WildcardIPv6 = false;
                else
-                       ServerInstance->SE->Close(socktest);
-       }
-       ConfigTagList tags = ConfTags("uline");
-       for(ConfigIter i = tags.first; i != tags.second; ++i)
-       {
-               ConfigTag* tag = i->second;
-               std::string server;
-               if (!tag->readString("server", server))
-                       throw CoreException("<uline> tag missing server at " + tag->getTagLocation());
-               ulines[assign(server)] = tag->getBool("silent");
-       }
-
-       tags = ConfTags("banlist");
-       for(ConfigIter i = tags.first; i != tags.second; ++i)
-       {
-               ConfigTag* tag = i->second;
-               std::string chan;
-               if (!tag->readString("chan", chan))
-                       throw CoreException("<banlist> tag missing chan at " + tag->getTagLocation());
-               maxbans[chan] = tag->getInt("limit");
+                       SocketEngine::Close(socktest);
        }
 
        ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
@@ -617,48 +415,15 @@ void ServerConfig::Fill()
        ReadXLine(this, "badhost", "host", ServerInstance->XLines->GetFactory("K"));
        ReadXLine(this, "exception", "host", ServerInstance->XLines->GetFactory("E"));
 
-       memset(DisabledUModes, 0, sizeof(DisabledUModes));
-       std::string modes = ConfValue("disabled")->getString("usermodes");
-       for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
-       {
-               // Complain when the character is not a-z or A-Z
-               if ((*p < 'A') || (*p > 'z') || ((*p < 'a') && (*p > 'Z')))
-                       throw CoreException("Invalid usermode " + std::string(1, *p) + " was found.");
-               DisabledUModes[*p - 'A'] = 1;
-       }
-
-       memset(DisabledCModes, 0, sizeof(DisabledCModes));
-       modes = ConfValue("disabled")->getString("chanmodes");
-       for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
-       {
-               if ((*p < 'A') || (*p > 'z') || ((*p < 'a') && (*p > 'Z')))
-                       throw CoreException("Invalid chanmode " + std::string(1, *p) + " was found.");
-               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")
-               AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_OPS;
-       else if (v == "all")
-               AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_ALL;
-       else if (v == "dynamic")
-               AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_DYNAMIC;
+       const std::string restrictbannedusers = options->getString("restrictbannedusers", "yes");
+       if (stdalgo::string::equalsci(restrictbannedusers, "no"))
+               RestrictBannedUsers = ServerConfig::BUT_NORMAL;
+       else if (stdalgo::string::equalsci(restrictbannedusers, "silent"))
+               RestrictBannedUsers = ServerConfig::BUT_RESTRICT_SILENT;
+       else if (stdalgo::string::equalsci(restrictbannedusers, "yes"))
+               RestrictBannedUsers =  ServerConfig::BUT_RESTRICT_NOTIFY;
        else
-               AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_NONE;
-
-       v = security->getString("operspywhois");
-       if (v == "splitmsg")
-               OperSpyWhois = SPYWHOIS_SPLITMSG;
-       else if (v == "on" || v == "yes")
-               OperSpyWhois = SPYWHOIS_SINGLEMSG;
-       else
-               OperSpyWhois = SPYWHOIS_NONE;
+               throw CoreException(restrictbannedusers + " is an invalid <options:restrictbannedusers> value, at " + options->getTagLocation());
 }
 
 // WARNING: it is not safe to use most of the codebase in this function, as it
@@ -675,12 +440,7 @@ void ServerConfig::Read()
        catch (CoreException& err)
        {
                valid = false;
-               errstr << err.GetReason();
-       }
-       if (valid)
-       {
-               DNSServer = ConfValue("dns")->getString("server");
-               FindDNS(DNSServer);
+               errstr << err.GetReason() << std::endl;
        }
 }
 
@@ -692,6 +452,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
                /*
                 * These values can only be set on boot. Keep their old values. Do it before we send messages so we actually have a servername.
                 */
+               this->CaseMapping = old->CaseMapping;
                this->ServerName = old->ServerName;
                this->sid = old->sid;
                this->cmdline = old->cmdline;
@@ -700,16 +461,16 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
        /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
        try
        {
-               for (int Index = 0; Index * sizeof(Deprecated) < sizeof(ChangedConfig); Index++)
+               // Ensure the user has actually edited ther config.
+               ConfigTagList dietags = ConfTags("die");
+               if (dietags.first != dietags.second)
                {
-                       std::string dummy;
-                       ConfigTagList tags = ConfTags(ChangedConfig[Index].tag);
-                       for(ConfigIter i = tags.first; i != tags.second; ++i)
+                       errstr << "Your configuration has not been edited correctly!" << std::endl;
+                       for (ConfigIter iter = dietags.first; iter != dietags.second; ++iter)
                        {
-                               if (i->second->readString(ChangedConfig[Index].value, dummy, true))
-                                       errstr << "Your configuration contains a deprecated value: <"
-                                               << ChangedConfig[Index].tag << ":" << ChangedConfig[Index].value << "> - " << ChangedConfig[Index].reason
-                                               << " (at " << i->second->getTagLocation() << ")\n";
+                               ConfigTag* tag = iter->second;
+                               const std::string reason = tag->getString("reason", "You left a <die> tag in your config", 1);
+                               errstr << reason <<  " (at " << tag->getTagLocation() << ")" << std::endl;
                        }
                }
 
@@ -721,7 +482,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
        }
        catch (CoreException &ce)
        {
-               errstr << ce.GetReason();
+               errstr << ce.GetReason() << std::endl;
        }
 
        // Check errors before dealing with failed binds, since continuing on failed bind is wanted in some circumstances.
@@ -731,6 +492,11 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
        if (valid)
                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 && valid)
        {
                // On first run, ports are bound later on
@@ -738,14 +504,13 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
                ServerInstance->BindPorts(pl);
                if (pl.size())
                {
-                       errstr << "Not all your client ports could be bound.\nThe following port(s) failed to bind:\n";
+                       errstr << "Not all your client ports could be bound." << std::endl
+                               << "The following port(s) failed to bind:" << std::endl;
 
                        int j = 1;
                        for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
                        {
-                               char buf[MAXBUF];
-                               snprintf(buf, MAXBUF, "%d.   Address: %s   Reason: %s\n", j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str());
-                               errstr << buf;
+                               errstr << j << ".\tAddress: " << i->first.str() << "\tReason: " << strerror(i->second) << std::endl;
                        }
                }
        }
@@ -754,7 +519,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
 
        if (!valid)
        {
-               ServerInstance->Logs->Log("CONFIG",DEFAULT, "There were errors in your configuration file:");
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "There were errors in your configuration file:");
                Classes.clear();
        }
 
@@ -769,7 +534,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);
        }
@@ -777,23 +542,6 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
        errstr.clear();
        errstr.str(std::string());
 
-       // Re-parse our MOTD and RULES files for colors -- Justasic
-       for (ClassVector::const_iterator it = this->Classes.begin(), it_end = this->Classes.end(); it != it_end; ++it)
-       {
-               ConfigTag *tag = (*it)->config;
-               // Make sure our connection class allows motd colors
-               if(!tag->getBool("allowmotdcolors"))
-                     continue;
-
-               ConfigFileCache::iterator file = this->Files.find(tag->getString("motd", "motd"));
-               if (file != this->Files.end())
-                     InspIRCd::ProcessColors(file->second);
-
-               file = this->Files.find(tag->getString("rules", "rules"));
-               if (file != this->Files.end())
-                     InspIRCd::ProcessColors(file->second);
-       }
-
        /* No old configuration -> initial boot, nothing more to do here */
        if (!old)
        {
@@ -813,20 +561,14 @@ 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.");
 }
 
 void ServerConfig::ApplyModules(User* user)
 {
-       Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
-       if (whowas)
-               WhowasRequest(NULL, whowas, WhowasRequest::WHOWAS_PRUNE).Send();
-
-       const std::vector<std::string> v = ServerInstance->Modules->GetAllModuleNames(0);
        std::vector<std::string> added_modules;
-       std::set<std::string> removed_modules(v.begin(), v.end());
+       ModuleManager::ModuleMap removed_modules = ServerInstance->Modules->GetModules();
 
        ConfigTagList tags = ConfTags("module");
        for(ConfigIter i = tags.first; i != tags.second; ++i)
@@ -835,6 +577,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)
@@ -842,58 +585,54 @@ void ServerConfig::ApplyModules(User* user)
                }
        }
 
-       if (ConfValue("options")->getBool("allowhalfop") && removed_modules.erase("m_halfop.so") == 0)
-               added_modules.push_back("m_halfop.so");
-
-       for (std::set<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
+       for (ModuleManager::ModuleMap::iterator i = removed_modules.begin(); i != removed_modules.end(); ++i)
        {
-               // Don't remove cmd_*.so, just remove m_*.so
-               if (removing->c_str()[0] == 'c')
+               const std::string& modname = i->first;
+               // Don't remove core_*.so, just remove m_*.so
+               if (InspIRCd::Match(modname, "core_*.so", ascii_case_insensitive_map))
                        continue;
-               Module* m = ServerInstance->Modules->Find(*removing);
-               if (m && ServerInstance->Modules->Unload(m))
+               if (ServerInstance->Modules->Unload(i->second))
                {
-                       ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s",removing->c_str());
+                       ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s", modname.c_str());
 
                        if (user)
-                               user->WriteNumeric(RPL_UNLOADEDMODULE, "%s %s :Module %s successfully unloaded.",user->nick.c_str(), removing->c_str(), removing->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.", removing->c_str());
+                               ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", modname.c_str());
                }
                else
                {
                        if (user)
-                               user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s %s :Failed to unload module %s: %s",user->nick.c_str(), removing->c_str(), removing->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", removing->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()))
+               // Skip modules which are already loaded.
+               if (ServerInstance->Modules->Find(*adding))
+                       continue;
+
+               if (ServerInstance->Modules->Load(*adding))
                {
                        ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
                        if (user)
-                               user->WriteNumeric(RPL_LOADEDMODULE, "%s %s :Module %s successfully loaded.",user->nick.c_str(), 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 %s :Failed to load module %s: %s",user->nick.c_str(), 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());
                }
        }
 }
 
-bool ServerConfig::StartsWithWindowsDriveLetter(const std::string &path)
-{
-       return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
-}
-
 ConfigTag* ServerConfig::ConfValue(const std::string &tag)
 {
        ConfigTagList found = config_data.equal_range(tag);
@@ -902,7 +641,7 @@ ConfigTag* ServerConfig::ConfValue(const std::string &tag)
        ConfigTag* rv = found.first->second;
        found.first++;
        if (found.first != found.second)
-               ServerInstance->Logs->Log("CONFIG",DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
                        "(first at " + rv->getTagLocation() + "; second at " + found.first->second->getTagLocation() + ")");
        return rv;
 }
@@ -912,35 +651,28 @@ ConfigTagList ServerConfig::ConfTags(const std::string& tag)
        return config_data.equal_range(tag);
 }
 
-bool ServerConfig::FileExists(const char* file)
+std::string ServerConfig::Escape(const std::string& str, bool xml)
 {
-       struct stat sb;
-       if (stat(file, &sb) == -1)
-               return false;
-
-       if ((sb.st_mode & S_IFDIR) > 0)
-               return false;
-
-       FILE *input = fopen(file, "r");
-       if (input == NULL)
-               return false;
-       else
+       std::string escaped;
+       for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
        {
-               fclose(input);
-               return true;
+               switch (*it)
+               {
+                       case '"':
+                               escaped += xml ? "&quot;" : "\"";
+                               break;
+                       case '&':
+                               escaped += xml ? "&amp;" : "&";
+                               break;
+                       case '\\':
+                               escaped += xml ? "\\" : "\\\\";
+                               break;
+                       default:
+                               escaped += *it;
+                               break;
+               }
        }
-}
-
-const char* ServerConfig::CleanFilename(const char* name)
-{
-       const char* p = name + strlen(name);
-       while ((p != name) && (*p != '/') && (*p != '\\')) p--;
-       return (p != name ? ++p : p);
-}
-
-const std::string& ServerConfig::GetSID()
-{
-       return sid;
+       return escaped;
 }
 
 void ConfigReaderThread::Run()
@@ -952,7 +684,7 @@ void ConfigReaderThread::Run()
 void ConfigReaderThread::Finish()
 {
        ServerConfig* old = ServerInstance->Config;
-       ServerInstance->Logs->Log("CONFIG",DEBUG,"Switching to new configuration...");
+       ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Switching to new configuration...");
        ServerInstance->Config = this->Config;
        Config->Apply(old, TheUserUID);
 
@@ -964,15 +696,32 @@ void ConfigReaderThread::Finish()
                 * XXX: The order of these is IMPORTANT, do not reorder them without testing
                 * thoroughly!!!
                 */
-               ServerInstance->Users->RehashCloneCounts();
+               ServerInstance->Users.RehashCloneCounts();
                ServerInstance->XLines->CheckELines();
                ServerInstance->XLines->ApplyLines();
-               ServerInstance->Res->Rehash();
-               ServerInstance->ResetMaxBans();
-               Config->ApplyDisabledCommands(Config->DisabledCommands);
                User* user = ServerInstance->FindNick(TheUserUID);
-               FOREACH_MOD(I_OnRehash, OnRehash(user));
-               ServerInstance->BuildISupport();
+
+               ConfigStatus status(user);
+               const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+               for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+               {
+                       try
+                       {
+                               ServerInstance->Logs->Log("MODULE", LOG_DEBUG, "Rehashing " + i->first);
+                               i->second->ReadConfig(status);
+                       }
+                       catch (CoreException& modex)
+                       {
+                               ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + modex.GetReason());
+                               if (user)
+                                       user->WriteNotice(i->first + ": " + modex.GetReason());
+                       }
+               }
+
+               // 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();
                ServerInstance->Logs->OpenFileLogs();
diff --git a/src/coremods/core_channel/cmd_invite.cpp b/src/coremods/core_channel/cmd_invite.cpp
new file mode 100644 (file)
index 0000000..732894a
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@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"
+#include "core_channel.h"
+#include "invite.h"
+
+CommandInvite::CommandInvite(Module* parent, Invite::APIImpl& invapiimpl)
+       : Command(parent, "INVITE", 0, 0)
+       , invapi(invapiimpl)
+{
+       Penalty = 4;
+       syntax = "[<nick> <channel> [<time>]]";
+}
+
+/** Handle /INVITE
+ */
+CmdResult CommandInvite::Handle(User* user, const Params& parameters)
+{
+       ModResult MOD_RESULT;
+
+       if (parameters.size() >= 2)
+       {
+               User* u;
+               if (IS_LOCAL(user))
+                       u = ServerInstance->FindNickOnly(parameters[0]);
+               else
+                       u = ServerInstance->FindNick(parameters[0]);
+
+               Channel* c = ServerInstance->FindChan(parameters[1]);
+               time_t timeout = 0;
+               if (parameters.size() >= 3)
+               {
+                       if (IS_LOCAL(user))
+                       {
+                               unsigned long duration;
+                               if (!InspIRCd::Duration(parameters[2], duration))
+                               {
+                                       user->WriteNotice("*** Invalid duration for invite");
+                                       return CMD_FAILURE;
+                               }
+                               timeout = ServerInstance->Time() + duration;
+                       }
+                       else if (parameters.size() > 3)
+                               timeout = ConvToNum<time_t>(parameters[3]);
+               }
+
+               if (!c)
+               {
+                       user->WriteNumeric(Numerics::NoSuchChannel(parameters[1]));
+                       return CMD_FAILURE;
+               }
+               if ((!u) || (u->registered != REG_ALL))
+               {
+                       user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+                       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 = ConvToNum<time_t>(parameters[2]);
+                       if (c->age < RemoteTS)
+                               return CMD_FAILURE;
+               }
+
+               if ((IS_LOCAL(user)) && (!c->HasUser(user)))
+               {
+                       user->WriteNumeric(ERR_NOTONCHANNEL, c->name, "You're not on that channel!");
+                       return CMD_FAILURE;
+               }
+
+               if (c->HasUser(u))
+               {
+                       user->WriteNumeric(ERR_USERONCHANNEL, u->nick, c->name, "is already on channel");
+                       return CMD_FAILURE;
+               }
+
+               FIRST_MOD_RESULT(OnUserPreInvite, MOD_RESULT, (user,u,c,timeout));
+
+               if (MOD_RESULT == MOD_RES_DENY)
+               {
+                       return CMD_FAILURE;
+               }
+               else if (MOD_RESULT == MOD_RES_PASSTHRU)
+               {
+                       if (IS_LOCAL(user))
+                       {
+                               unsigned int rank = c->GetPrefixValue(user);
+                               if (rank < HALFOP_VALUE)
+                               {
+                                       // Check whether halfop mode is available and phrase error message accordingly
+                                       ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
+                                       user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, InspIRCd::Format("You must be a channel %soperator",
+                                               (mh && mh->name == "halfop" ? "half-" : "")));
+                                       return CMD_FAILURE;
+                               }
+                       }
+               }
+
+               LocalUser* const localtargetuser = IS_LOCAL(u);
+               if (localtargetuser)
+               {
+                       invapi.Create(localtargetuser, c, timeout);
+                       ClientProtocol::Messages::Invite invitemsg(user, localtargetuser, c);
+                       localtargetuser->Send(ServerInstance->GetRFCEvents().invite, invitemsg);
+               }
+
+               if (IS_LOCAL(user))
+               {
+                       user->WriteNumeric(RPL_INVITING, u->nick, c->name);
+                       if (u->IsAway())
+                               user->WriteNumeric(RPL_AWAY, u->nick, u->awaymsg);
+               }
+
+               char prefix = 0;
+               unsigned int minrank = 0;
+               switch (announceinvites)
+               {
+                       case Invite::ANNOUNCE_OPS:
+                       {
+                               prefix = '@';
+                               minrank = OP_VALUE;
+                               break;
+                       }
+                       case Invite::ANNOUNCE_DYNAMIC:
+                       {
+                               PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
+                               if ((mh) && (mh->name == "halfop"))
+                               {
+                                       prefix = mh->GetPrefix();
+                                       minrank = mh->GetPrefixRank();
+                               }
+                               break;
+                       }
+                       default:
+                       {
+                       }
+               }
+
+               CUList excepts;
+               FOREACH_MOD(OnUserInvite, (user, u, c, timeout, minrank, excepts));
+
+               if (announceinvites != Invite::ANNOUNCE_NONE)
+               {
+                       excepts.insert(user);
+                       ClientProtocol::Messages::Privmsg privmsg(ServerInstance->FakeClient, c, InspIRCd::Format("*** %s invited %s into the channel", user->nick.c_str(), u->nick.c_str()), MSG_NOTICE);
+                       c->Write(ServerInstance->GetRFCEvents().privmsg, privmsg, prefix, excepts);
+               }
+       }
+       else if (IS_LOCAL(user))
+       {
+               // pinched from ircu - invite with not enough parameters shows channels
+               // youve been invited to but haven't joined yet.
+               const Invite::List* list = invapi.GetList(IS_LOCAL(user));
+               if (list)
+               {
+                       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");
+       }
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandInvite::GetRouting(User* user, const Params& parameters)
+{
+       return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_channel/cmd_join.cpp b/src/coremods/core_channel/cmd_join.cpp
new file mode 100644 (file)
index 0000000..0e783d2
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_channel.h"
+
+CommandJoin::CommandJoin(Module* parent)
+       : SplitCommand(parent, "JOIN", 1, 2)
+{
+       syntax = "<channel>[,<channel>]+ [<key>[,<key>]+]";
+       Penalty = 2;
+}
+
+/** Handle /JOIN
+ */
+CmdResult CommandJoin::HandleLocal(LocalUser* user, const Params& parameters)
+{
+       if (parameters.size() > 1)
+       {
+               if (CommandParser::LoopCall(user, this, parameters, 0, 1, false))
+                       return CMD_SUCCESS;
+
+               if (ServerInstance->IsChannel(parameters[0]))
+               {
+                       Channel::JoinUser(user, parameters[0], false, parameters[1]);
+                       return CMD_SUCCESS;
+               }
+       }
+       else
+       {
+               if (CommandParser::LoopCall(user, this, parameters, 0, -1, false))
+                       return CMD_SUCCESS;
+
+               if (ServerInstance->IsChannel(parameters[0]))
+               {
+                       Channel::JoinUser(user, parameters[0]);
+                       return CMD_SUCCESS;
+               }
+       }
+
+       user->WriteNumeric(ERR_BADCHANMASK, 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
new file mode 100644 (file)
index 0000000..77ac36a
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_channel.h"
+
+CommandKick::CommandKick(Module* parent)
+       : Command(parent, "KICK", 2, 3)
+{
+       syntax = "<channel> <nick>[,<nick>]+ [:<reason>]";
+}
+
+/** Handle /KICK
+ */
+CmdResult CommandKick::Handle(User* user, const Params& parameters)
+{
+       Channel* c = ServerInstance->FindChan(parameters[0]);
+       User* u;
+
+       if (CommandParser::LoopCall(user, this, parameters, 1))
+               return CMD_SUCCESS;
+
+       if (IS_LOCAL(user))
+               u = ServerInstance->FindNickOnly(parameters[1]);
+       else
+               u = ServerInstance->FindNick(parameters[1]);
+
+       if (!c)
+       {
+               user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+               return CMD_FAILURE;
+       }
+       if ((!u) || (u->registered != REG_ALL))
+       {
+               user->WriteNumeric(Numerics::NoSuchNick(parameters[1]));
+               return CMD_FAILURE;
+       }
+
+       Membership* srcmemb = NULL;
+       if (IS_LOCAL(user))
+       {
+               srcmemb = c->GetUser(user);
+               if (!srcmemb)
+               {
+                       user->WriteNumeric(ERR_NOTONCHANNEL, parameters[0], "You're not on that channel!");
+                       return CMD_FAILURE;
+               }
+
+               if (u->server->IsULine())
+               {
+                       user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, "You may not kick a U-lined client");
+                       return CMD_FAILURE;
+               }
+       }
+
+       const Channel::MemberMap::iterator victimiter = c->userlist.find(u);
+       if (victimiter == c->userlist.end())
+       {
+               user->WriteNumeric(ERR_USERNOTINCHANNEL, u->nick, c->name, "They are not on that channel");
+               return CMD_FAILURE;
+       }
+       Membership* const memb = victimiter->second;
+
+       // KICKs coming from servers can carry a membership id
+       if ((!IS_LOCAL(user)) && (parameters.size() > 3))
+       {
+               // 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(true) > req)
+                                       req = mh->GetLevelRequired(true);
+                       }
+
+                       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, victimiter, reason);
+
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandKick::GetRouting(User* user, const Params& parameters)
+{
+       return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_channel/cmd_names.cpp b/src/coremods/core_channel/cmd_names.cpp
new file mode 100644 (file)
index 0000000..b5cd98e
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_channel.h"
+#include "modules/names.h"
+
+CommandNames::CommandNames(Module* parent)
+       : SplitCommand(parent, "NAMES", 0, 0)
+       , secretmode(parent, "secret")
+       , privatemode(parent, "private")
+       , invisiblemode(parent, "invisible")
+       , namesevprov(parent, "event/names")
+{
+       syntax = "<channel>[,<channel>]+";
+}
+
+/** Handle /NAMES
+ */
+CmdResult CommandNames::HandleLocal(LocalUser* user, const Params& parameters)
+{
+       Channel* c;
+
+       if (parameters.empty())
+       {
+               user->WriteNumeric(RPL_ENDOFNAMES, '*', "End of /NAMES list.");
+               return CMD_SUCCESS;
+       }
+
+       if (CommandParser::LoopCall(user, this, parameters, 0))
+               return CMD_SUCCESS;
+
+       c = ServerInstance->FindChan(parameters[0]);
+       if (c)
+       {
+               // Show the NAMES list if one of the following is true:
+               // - the channel is not secret
+               // - the user doing the /NAMES is inside the channel
+               // - the user doing the /NAMES has the channels/auspex privilege
+
+               // 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)))
+               {
+                       SendNames(user, c, show_invisible);
+                       return CMD_SUCCESS;
+               }
+       }
+
+       user->WriteNumeric(Numerics::NoSuchChannel(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_CUSTOM(namesevprov, Names::EventListener, OnNamesListItem, res, (user, memb, prefixlist, nick));
+               if (res != MOD_RES_DENY)
+                       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
new file mode 100644 (file)
index 0000000..f0af2a0
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@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"
+#include "core_channel.h"
+
+CommandTopic::CommandTopic(Module* parent)
+       : SplitCommand(parent, "TOPIC", 1, 2)
+       , exemptionprov(parent)
+       , secretmode(parent, "secret")
+       , topiclockmode(parent, "topiclock")
+{
+       syntax = "<channel> [:<topic>]";
+       Penalty = 2;
+}
+
+CmdResult CommandTopic::HandleLocal(LocalUser* user, const Params& parameters)
+{
+       Channel* c = ServerInstance->FindChan(parameters[0]);
+       if (!c)
+       {
+               user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+               return CMD_FAILURE;
+       }
+
+       if (parameters.size() == 1)
+       {
+               if ((c->IsModeSet(secretmode)) && (!c->HasUser(user)))
+               {
+                       user->WriteNumeric(Numerics::NoSuchChannel(c->name));
+                       return CMD_FAILURE;
+               }
+
+               if (c->topic.length())
+               {
+                       Topic::ShowTopic(user, c);
+               }
+               else
+               {
+                       user->WriteNumeric(RPL_NOTOPICSET, c->name, "No topic is set.");
+               }
+               return CMD_SUCCESS;
+       }
+
+       std::string t = parameters[1]; // needed, in case a module wants to change it
+       ModResult res;
+       FIRST_MOD_RESULT(OnPreTopicChange, res, (user,c,t));
+
+       if (res == MOD_RES_DENY)
+               return CMD_FAILURE;
+       if (res != MOD_RES_ALLOW)
+       {
+               if (!c->HasUser(user))
+               {
+                       user->WriteNumeric(ERR_NOTONCHANNEL, c->name, "You're not on that channel!");
+                       return CMD_FAILURE;
+               }
+               if (c->IsModeSet(topiclockmode))
+               {
+                       ModResult MOD_RESULT = CheckExemption::Call(exemptionprov, user, c, "topiclock");
+                       if (!MOD_RESULT.check(c->GetPrefixValue(user) >= HALFOP_VALUE))
+                       {
+                               user->WriteNumeric(ERR_CHANOPRIVSNEEDED, c->name, "You do not have access to change the topic on this channel");
+                               return CMD_FAILURE;
+                       }
+               }
+       }
+
+       // 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/cmode_k.cpp b/src/coremods/core_channel/cmode_k.cpp
new file mode 100644 (file)
index 0000000..43f41dd
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   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/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+const std::string::size_type ModeChannelKey::maxkeylen = 32;
+
+ModeChannelKey::ModeChannelKey(Module* Creator)
+       : ParamMode<ModeChannelKey, LocalStringExt>(Creator, "key", 'k', PARAM_ALWAYS)
+{
+}
+
+ModeAction ModeChannelKey::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
+{
+       const std::string* key = ext.get(channel);
+       bool exists = (key != NULL);
+       if (IS_LOCAL(source))
+       {
+               if (exists == adding)
+                       return MODEACTION_DENY;
+               if (exists && (parameter != *key))
+               {
+                       /* Key is currently set and the correct key wasnt given */
+                       return MODEACTION_DENY;
+               }
+       } else {
+               if (exists && adding && parameter == *key)
+               {
+                       /* no-op, don't show */
+                       return MODEACTION_DENY;
+               }
+       }
+
+       if (adding)
+       {
+               // When joining a channel multiple keys are delimited with a comma so we strip
+               // them out here to avoid creating channels that are unjoinable.
+               size_t commapos;
+               while ((commapos = parameter.find(',')) != std::string::npos)
+                       parameter.erase(commapos, 1);
+
+               // Truncate the parameter to the maximum key length.
+               if (parameter.length() > maxkeylen)
+                       parameter.erase(maxkeylen);
+
+               // If the password is empty here then it only consisted of commas. This is not
+               // acceptable so we reject the mode change.
+               if (parameter.empty())
+                       return MODEACTION_DENY;
+
+               ext.set(channel, parameter);
+       }
+       else
+               ext.unset(channel);
+
+       channel->SetMode(this, adding);
+       return MODEACTION_ALLOW;
+}
+
+void ModeChannelKey::SerializeParam(Channel* chan, const std::string* key, std::string& out)
+{
+       out += *key;
+}
+
+ModeAction ModeChannelKey::OnSet(User* source, Channel* chan, std::string& param)
+{
+       // Dummy function, never called
+       return MODEACTION_DENY;
+}
+
+bool ModeChannelKey::IsParameterSecret()
+{
+       return true;
+}
diff --git a/src/coremods/core_channel/cmode_l.cpp b/src/coremods/core_channel/cmode_l.cpp
new file mode 100644 (file)
index 0000000..e71eb50
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   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/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+ModeChannelLimit::ModeChannelLimit(Module* Creator)
+       : ParamMode<ModeChannelLimit, LocalIntExt>(Creator, "limit", 'l')
+       , minlimit(0)
+{
+}
+
+bool ModeChannelLimit::ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel*)
+{
+       /* When TS is equal, the higher channel limit wins */
+       return (atoi(their_param.c_str()) < atoi(our_param.c_str()));
+}
+
+ModeAction ModeChannelLimit::OnSet(User* user, Channel* chan, std::string& parameter)
+{
+       size_t limit = ConvToNum<size_t>(parameter);
+       if (limit < minlimit)
+               return MODEACTION_DENY;
+
+       ext.set(chan, limit);
+       return MODEACTION_ALLOW;
+}
+
+void ModeChannelLimit::SerializeParam(Channel* chan, intptr_t n, std::string& out)
+{
+       out += ConvToStr(n);
+}
diff --git a/src/coremods/core_channel/core_channel.cpp b/src/coremods/core_channel/core_channel.cpp
new file mode 100644 (file)
index 0000000..e95736c
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   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
+ * 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_channel.h"
+#include "invite.h"
+#include "listmode.h"
+
+namespace
+{
+/** Hook that sends a MODE after a JOIN if the user in the JOIN has some modes prefix set.
+ * This happens e.g. when modules such as operprefix explicitly set prefix modes on the joining
+ * user, or when a member with prefix modes does a host cycle.
+ */
+class JoinHook : public ClientProtocol::EventHook
+{
+       ClientProtocol::Messages::Mode modemsg;
+       Modes::ChangeList modechangelist;
+       const User* joininguser;
+
+ public:
+       /** If true, MODE changes after JOIN will be sourced from the user, rather than the server
+        */
+       bool modefromuser;
+
+       JoinHook(Module* mod)
+               : ClientProtocol::EventHook(mod, "JOIN")
+       {
+       }
+
+       void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
+       {
+               const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+               const Membership& memb = *join.GetMember();
+
+               modechangelist.clear();
+               for (std::string::const_iterator i = memb.modes.begin(); i != memb.modes.end(); ++i)
+               {
+                       PrefixMode* const pm = ServerInstance->Modes.FindPrefixMode(*i);
+                       if (!pm)
+                               continue; // Shouldn't happen
+                       modechangelist.push_add(pm, memb.user->nick);
+               }
+
+               if (modechangelist.empty())
+               {
+                       // Member got no modes on join
+                       joininguser = NULL;
+                       return;
+               }
+
+               joininguser = memb.user;
+
+               // Prepare a mode protocol event that we can append to the message list in OnPreEventSend()
+               modemsg.SetParams(memb.chan, NULL, modechangelist);
+               if (modefromuser)
+                       modemsg.SetSource(join);
+               else
+                       modemsg.SetSourceUser(ServerInstance->FakeClient);
+       }
+
+       ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
+       {
+               // If joininguser is NULL then they didn't get any modes on join, skip.
+               // Also don't show their own modes to them, they get that in the NAMES list not via MODE.
+               if ((joininguser) && (user != joininguser))
+                       messagelist.push_back(&modemsg);
+               return MOD_RES_PASSTHRU;
+       }
+};
+
+}
+
+class CoreModChannel : public Module, public CheckExemption::EventListener
+{
+       Invite::APIImpl invapi;
+       CommandInvite cmdinvite;
+       CommandJoin cmdjoin;
+       CommandKick cmdkick;
+       CommandNames cmdnames;
+       CommandTopic cmdtopic;
+       Events::ModuleEventProvider evprov;
+       JoinHook joinhook;
+
+       ModeChannelBan banmode;
+       SimpleChannelModeHandler inviteonlymode;
+       ModeChannelKey keymode;
+       ModeChannelLimit limitmode;
+       SimpleChannelModeHandler moderatedmode;
+       SimpleChannelModeHandler noextmsgmode;
+       ModeChannelOp opmode;
+       SimpleChannelModeHandler privatemode;
+       SimpleChannelModeHandler secretmode;
+       SimpleChannelModeHandler topiclockmode;
+       ModeChannelVoice voicemode;
+
+       insp::flat_map<std::string, char> exemptions;
+
+       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()
+               : CheckExemption::EventListener(this, UINT_MAX)
+               , invapi(this)
+               , cmdinvite(this, invapi)
+               , cmdjoin(this)
+               , cmdkick(this)
+               , cmdnames(this)
+               , cmdtopic(this)
+               , evprov(this, "event/channel")
+               , joinhook(this)
+               , banmode(this)
+               , inviteonlymode(this, "inviteonly", 'i')
+               , keymode(this)
+               , limitmode(this)
+               , moderatedmode(this, "moderated", 'm')
+               , noextmsgmode(this, "noextmsg", 'n')
+               , opmode(this)
+               , privatemode(this, "private", 'p')
+               , secretmode(this, "secret", 's')
+               , topiclockmode(this, "topiclock", 't')
+               , voicemode(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* optionstag = ServerInstance->Config->ConfValue("options");
+
+               std::string current;
+               irc::spacesepstream defaultstream(optionstag->getString("exemptchanops"));
+               insp::flat_map<std::string, char> exempts;
+               while (defaultstream.GetToken(current))
+               {
+                       std::string::size_type pos = current.find(':');
+                       if (pos == std::string::npos || (pos + 2) > current.size())
+                               throw ModuleException("Invalid exemptchanops value '" + current + "' at " + optionstag->getTagLocation());
+
+                       const std::string restriction = current.substr(0, pos);
+                       const char prefix = current[pos + 1];
+
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Exempting prefix %c from %s", prefix, restriction.c_str());
+                       exempts[restriction] = prefix;
+               }
+
+               ConfigTag* securitytag = ServerInstance->Config->ConfValue("security");
+               const std::string announceinvites = securitytag->getString("announceinvites", "dynamic");
+               Invite::AnnounceState newannouncestate;
+               if (stdalgo::string::equalsci(announceinvites, "none"))
+                       newannouncestate = Invite::ANNOUNCE_NONE;
+               else if (stdalgo::string::equalsci(announceinvites, "all"))
+                       newannouncestate = Invite::ANNOUNCE_ALL;
+               else if (stdalgo::string::equalsci(announceinvites, "ops"))
+                       newannouncestate = Invite::ANNOUNCE_OPS;
+               else if (stdalgo::string::equalsci(announceinvites, "dynamic"))
+                       newannouncestate = Invite::ANNOUNCE_DYNAMIC;
+               else
+                       throw ModuleException(announceinvites + " is an invalid <security:announceinvites> value, at " + securitytag->getTagLocation());
+
+               // Config is valid, apply it
+
+               // Validates and applies <maxlist> tags, so do it first
+               banmode.DoRehash();
+
+               exemptions.swap(exempts);
+               // In 2.0 we allowed limits of 0 to be set. This is non-standard behaviour
+               // and will be removed in the next major release.
+               limitmode.minlimit = optionstag->getBool("allowzerolimit", true) ? 0 : 1;;
+               cmdinvite.announceinvites = newannouncestate;
+               joinhook.modefromuser = optionstag->getBool("cyclehostsfromuser");
+
+               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 On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+       {
+               tokens["KEYLEN"] = ConvToStr(ModeChannelKey::maxkeylen);
+
+               // Build a map of limits to their mode character.
+               insp::flat_map<int, std::string> limits;
+               const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+               for (ModeParser::ListModeList::const_iterator iter = listmodes.begin(); iter != listmodes.end(); ++iter)
+               {
+                       const unsigned int limit = (*iter)->GetLowerLimit();
+                       limits[limit].push_back((*iter)->GetModeChar());
+               }
+
+               // Generate the MAXLIST token from the limits map.
+               std::string& buffer = tokens["MAXLIST"];
+               for (insp::flat_map<int, std::string>::const_iterator iter = limits.begin(); iter != limits.end(); ++iter)
+               {
+                       if (!buffer.empty())
+                               buffer.push_back(',');
+
+                       std::string modes(iter->second);
+                       std::sort(modes.begin(), modes.end());
+
+                       buffer.append(modes);
+                       buffer.push_back(':');
+                       buffer.append(ConvToStr(iter->first));
+               }
+       }
+
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string&, std::string&, const std::string& keygiven) CXX11_OVERRIDE
+       {
+               if (!chan)
+                       return MOD_RES_PASSTHRU;
+
+               // Check whether the channel key is correct.
+               const std::string ckey = chan->GetModeParameter(&keymode);
+               if (!ckey.empty())
+               {
+                       ModResult MOD_RESULT;
+                       FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, chan, keygiven));
+                       if (!MOD_RESULT.check(InspIRCd::TimingSafeCompare(ckey, keygiven)))
+                       {
+                               // 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, chan->name, "Cannot join channel (incorrect channel key)");
+                               return MOD_RES_DENY;
+                       }
+               }
+
+               // Check whether the invite only mode is set.
+               if (chan->IsModeSet(inviteonlymode))
+               {
+                       ModResult MOD_RESULT;
+                       FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, chan));
+                       if (MOD_RESULT != MOD_RES_ALLOW)
+                       {
+                               user->WriteNumeric(ERR_INVITEONLYCHAN, chan->name, "Cannot join channel (invite only)");
+                               return MOD_RES_DENY;
+                       }
+               }
+
+               // Check whether the limit would be exceeded by this user joining.
+               if (chan->IsModeSet(limitmode))
+               {
+                       ModResult MOD_RESULT;
+                       FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, chan));
+                       if (!MOD_RESULT.check(chan->GetUserCounter() < static_cast<size_t>(limitmode.ext.get(chan))))
+                       {
+                               user->WriteNumeric(ERR_CHANNELISFULL, chan->name, "Cannot join channel (channel is full)");
+                               return MOD_RES_DENY;
+                       }
+               }
+
+               // Everything looks okay.
+               return MOD_RES_PASSTHRU;
+       }
+
+       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->topic.length())
+                               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);
+       }
+
+       ModResult OnCheckExemption(User* user, Channel* chan, const std::string& restriction) CXX11_OVERRIDE
+       {
+               if (!exemptions.count(restriction))
+                       return MOD_RES_PASSTHRU;
+
+               unsigned int mypfx = chan->GetPrefixValue(user);
+               char minmode = exemptions[restriction];
+
+               PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(minmode);
+               if (mh && mypfx >= mh->GetPrefixRank())
+                       return MOD_RES_ALLOW;
+               if (mh || minmode == '*')
+                       return MOD_RES_DENY;
+               return MOD_RES_PASSTHRU;
+       }
+
+       void Prioritize() CXX11_OVERRIDE
+       {
+               ServerInstance->Modules.SetPriority(this, I_OnPostJoin, PRIORITY_FIRST);
+               ServerInstance->Modules.SetPriority(this, I_OnUserPreJoin, PRIORITY_LAST);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the INVITE, JOIN, KICK, NAMES, and TOPIC commands", VF_VENDOR|VF_CORE);
+       }
+};
+
+MODULE_INIT(CoreModChannel)
diff --git a/src/coremods/core_channel/core_channel.h b/src/coremods/core_channel/core_channel.h
new file mode 100644 (file)
index 0000000..c054d52
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   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
+
+#include "inspircd.h"
+#include "listmode.h"
+#include "modules/exemption.h"
+
+namespace Topic
+{
+       void ShowTopic(LocalUser* user, Channel* chan);
+}
+
+namespace Invite
+{
+       class APIImpl;
+
+       /** Used to indicate who we announce invites to on a channel. */
+       enum AnnounceState
+       {
+               /** Don't send invite announcements. */
+               ANNOUNCE_NONE,
+
+               /** Send invite announcements to all users. */
+               ANNOUNCE_ALL,
+
+               /** Send invite announcements to channel operators and higher. */
+               ANNOUNCE_OPS,
+
+               /** Send invite announcements to channel half-operators (if available) and higher. */
+               ANNOUNCE_DYNAMIC
+       };
+}
+
+/** Handle /INVITE.
+ */
+class CommandInvite : public Command
+{
+       Invite::APIImpl& invapi;
+
+ public:
+       Invite::AnnounceState announceinvites;
+
+       /** Constructor for invite.
+        */
+       CommandInvite(Module* parent, Invite::APIImpl& invapiimpl);
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /JOIN.
+ */
+class CommandJoin : public SplitCommand
+{
+ public:
+       /** Constructor for join.
+        */
+       CommandJoin(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 HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /TOPIC.
+ */
+class CommandTopic : public SplitCommand
+{
+       CheckExemption::EventProvider exemptionprov;
+       ChanModeReference secretmode;
+       ChanModeReference topiclockmode;
+
+ public:
+       /** Constructor for topic.
+        */
+       CommandTopic(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 HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /NAMES.
+ */
+class CommandNames : public SplitCommand
+{
+ private:
+       ChanModeReference secretmode;
+       ChanModeReference privatemode;
+       UserModeReference invisiblemode;
+       Events::ModuleEventProvider namesevprov;
+
+ public:
+       /** Constructor for names.
+        */
+       CommandNames(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 HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+
+       /** 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.
+ */
+class CommandKick : public Command
+{
+ public:
+       /** Constructor for kick.
+        */
+       CommandKick(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Channel mode +b
+ */
+class ModeChannelBan : public ListModeBase
+{
+ public:
+       ModeChannelBan(Module* Creator)
+               : ListModeBase(Creator, "ban", 'b', "End of channel ban list", 367, 368, true)
+       {
+       }
+};
+
+/** Channel mode +k
+ */
+class ModeChannelKey : public ParamMode<ModeChannelKey, LocalStringExt>
+{
+ public:
+       static const std::string::size_type maxkeylen;
+       ModeChannelKey(Module* Creator);
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE;
+       void SerializeParam(Channel* chan, const std::string* key, std::string& out)    ;
+       ModeAction OnSet(User* source, Channel* chan, std::string& param) CXX11_OVERRIDE;
+       bool IsParameterSecret() CXX11_OVERRIDE;
+};
+
+/** Channel mode +l
+ */
+class ModeChannelLimit : public ParamMode<ModeChannelLimit, LocalIntExt>
+{
+ public:
+       size_t minlimit;
+       ModeChannelLimit(Module* Creator);
+       bool ResolveModeConflict(std::string& their_param, const std::string& our_param, Channel* channel) CXX11_OVERRIDE;
+       void SerializeParam(Channel* chan, intptr_t n, std::string& out);
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE;
+};
+
+/** Channel mode +o
+ */
+class ModeChannelOp : public PrefixMode
+{
+ public:
+       ModeChannelOp(Module* Creator)
+               : PrefixMode(Creator, "op", 'o', OP_VALUE, '@')
+       {
+               ranktoset = ranktounset = OP_VALUE;
+       }
+};
+
+/** Channel mode +v
+ */
+class ModeChannelVoice : public PrefixMode
+{
+ public:
+       ModeChannelVoice(Module* Creator)
+               : PrefixMode(Creator, "voice", 'v', VOICE_VALUE, '+')
+       {
+               selfremove = false;
+               ranktoset = ranktounset = HALFOP_VALUE;
+       }
+};
diff --git a/src/coremods/core_channel/invite.cpp b/src/coremods/core_channel/invite.cpp
new file mode 100644 (file)
index 0000000..51fb638
--- /dev/null
@@ -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, ConvToNum<time_t>(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 (file)
index 0000000..19e3861
--- /dev/null
@@ -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(ext, store);
+       }
+
+       void free(Extensible* container, 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
new file mode 100644 (file)
index 0000000..8532179
--- /dev/null
@@ -0,0 +1,850 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Adam <Adam@anope.org>
+ *   Copyright (C) 2003-2013 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
+ * 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/dns.h"
+#include <iostream>
+#include <fstream>
+
+#ifdef _WIN32
+#include <Iphlpapi.h>
+#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 received to/from the nameserver
+ */
+class Packet : public Query
+{
+       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(MODNAME, LOG_DEBUG, "Packing name " + name);
+
+               irc::sepstream sep(name, '.');
+               std::string token;
+
+               while (sep.GetToken(token))
+               {
+                       output[pos++] = token.length();
+                       memcpy(&output[pos], token.data(), token.length());
+                       pos += token.length();
+               }
+
+               output[pos++] = 0;
+       }
+
+       std::string UnpackName(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+       {
+               std::string name;
+               unsigned short pos_ptr = pos, lowest_ptr = input_size;
+               bool compressed = false;
+
+               if (pos_ptr >= input_size)
+                       throw Exception("Unable to unpack name - no input");
+
+               while (input[pos_ptr] > 0)
+               {
+                       unsigned short offset = input[pos_ptr];
+
+                       if (offset & POINTER)
+                       {
+                               if ((offset & POINTER) != POINTER)
+                                       throw Exception("Unable to unpack name - bogus compression header");
+                               if (pos_ptr + 1 >= input_size)
+                                       throw Exception("Unable to unpack name - bogus compression header");
+
+                               /* Place pos at the second byte of the first (farthest) compression pointer */
+                               if (compressed == false)
+                               {
+                                       ++pos;
+                                       compressed = true;
+                               }
+
+                               pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1];
+
+                               /* Pointers can only go back */
+                               if (pos_ptr >= lowest_ptr)
+                                       throw Exception("Unable to unpack name - bogus compression pointer");
+                               lowest_ptr = pos_ptr;
+                       }
+                       else
+                       {
+                               if (pos_ptr + offset + 1 >= input_size)
+                                       throw Exception("Unable to unpack name - offset too large");
+                               if (!name.empty())
+                                       name += ".";
+                               for (unsigned i = 1; i <= offset; ++i)
+                                       name += input[pos_ptr + i];
+
+                               pos_ptr += offset + 1;
+                               if (compressed == false)
+                                       /* Move up pos */
+                                       pos = pos_ptr;
+                       }
+               }
+
+               /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */
+               ++pos;
+
+               if (name.empty())
+                       throw Exception("Unable to unpack name - no 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 q;
+
+               q.name = this->UnpackName(input, input_size, pos);
+
+               if (pos + 4 > input_size)
+                       throw Exception("Unable to unpack question");
+
+               q.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]);
+               pos += 2;
+
+               // Skip over query class code
+               pos += 2;
+
+               return q;
+       }
+
+       ResourceRecord UnpackResourceRecord(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+       {
+               ResourceRecord record = static_cast<ResourceRecord>(this->UnpackQuestion(input, input_size, pos));
+
+               if (pos + 6 > input_size)
+                       throw Exception("Unable to unpack resource record");
+
+               record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3];
+               pos += 4;
+
+               uint16_t rdlength = input[pos] << 8 | input[pos + 1];
+               pos += 2;
+
+               switch (record.type)
+               {
+                       case QUERY_A:
+                       {
+                               if (pos + 4 > input_size)
+                                       throw Exception("Unable to unpack resource record");
+
+                               irc::sockets::sockaddrs addrs;
+                               memset(&addrs, 0, sizeof(addrs));
+
+                               addrs.in4.sin_family = AF_INET;
+                               addrs.in4.sin_addr.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16)  | (input[pos + 3] << 24);
+                               pos += 4;
+
+                               record.rdata = addrs.addr();
+                               break;
+                       }
+                       case QUERY_AAAA:
+                       {
+                               if (pos + 16 > input_size)
+                                       throw Exception("Unable to unpack resource record");
+
+                               irc::sockets::sockaddrs addrs;
+                               memset(&addrs, 0, sizeof(addrs));
+
+                               addrs.in6.sin6_family = AF_INET6;
+                               for (int j = 0; j < 16; ++j)
+                                       addrs.in6.sin6_addr.s6_addr[j] = input[pos + j];
+                               pos += 16;
+
+                               record.rdata = addrs.addr();
+
+                               break;
+                       }
+                       case QUERY_CNAME:
+                       case QUERY_PTR:
+                       {
+                               record.rdata = this->UnpackName(input, input_size, pos);
+                               if (!InspIRCd::IsHost(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:
+                               break;
+               }
+
+               if (!record.name.empty() && !record.rdata.empty())
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, record.name + " -> " + record.rdata);
+
+               return record;
+       }
+
+ public:
+       static const int POINTER = 0xC0;
+       static const int LABEL = 0x3F;
+       static const int HEADER_LENGTH = 12;
+
+       /* ID for this packet */
+       RequestId id;
+       /* Flags on the packet */
+       unsigned short flags;
+
+       Packet() : id(0), flags(0)
+       {
+       }
+
+       void Fill(const unsigned char* input, const unsigned short len)
+       {
+               if (len < HEADER_LENGTH)
+                       throw Exception("Unable to fill packet");
+
+               unsigned short packet_pos = 0;
+
+               this->id = (input[packet_pos] << 8) | input[packet_pos + 1];
+               packet_pos += 2;
+
+               this->flags = (input[packet_pos] << 8) | input[packet_pos + 1];
+               packet_pos += 2;
+
+               unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+               packet_pos += 2;
+
+               unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1];
+               packet_pos += 2;
+
+               unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1];
+               packet_pos += 2;
+
+               unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+               packet_pos += 2;
+
+               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");
+
+               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));
+       }
+
+       unsigned short Pack(unsigned char* output, unsigned short output_size)
+       {
+               if (output_size < HEADER_LENGTH)
+                       throw Exception("Unable to pack packet");
+
+               unsigned short pos = 0;
+
+               output[pos++] = this->id >> 8;
+               output[pos++] = this->id & 0xFF;
+               output[pos++] = this->flags >> 8;
+               output[pos++] = this->flags & 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;
+
+               {
+                       Question& q = this->question;
+
+                       if (q.type == QUERY_PTR)
+                       {
+                               irc::sockets::sockaddrs ip;
+                               irc::sockets::aptosa(q.name, 0, ip);
+
+                               if (q.name.find(':') != std::string::npos)
+                               {
+                                       static const char* const hex = "0123456789abcdef";
+                                       char reverse_ip[128];
+                                       unsigned reverse_ip_count = 0;
+                                       for (int j = 15; j >= 0; --j)
+                                       {
+                                               reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] & 0xF];
+                                               reverse_ip[reverse_ip_count++] = '.';
+                                               reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] >> 4];
+                                               reverse_ip[reverse_ip_count++] = '.';
+                                       }
+                                       reverse_ip[reverse_ip_count++] = 0;
+
+                                       q.name = reverse_ip;
+                                       q.name += "ip6.arpa";
+                               }
+                               else
+                               {
+                                       unsigned long forward = ip.in4.sin_addr.s_addr;
+                                       ip.in4.sin_addr.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24;
+
+                                       q.name = ip.addr() + ".in-addr.arpa";
+                               }
+                       }
+
+                       this->PackName(output, output_size, pos, q.name);
+
+                       if (pos + 4 >= output_size)
+                               throw Exception("Unable to pack packet");
+
+                       short s = htons(q.type);
+                       memcpy(&output[pos], &s, 2);
+                       pos += 2;
+
+                       // Query class, always IN
+                       output[pos++] = 0;
+                       output[pos++] = 1;
+               }
+
+               return pos;
+       }
+};
+
+class MyManager : public Manager, public Timer, public EventHandler
+{
+       typedef TR1NS::unordered_map<Question, Query, Question::hash> cache_map;
+       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())
+       {
+               const ResourceRecord& req = record.answers[0];
+               return (req.created + static_cast<time_t>(req.ttl) < now);
+       }
+
+       /** Check the DNS cache to see if request can be handled by a cached result
+        * @return true if a cached result was found.
+        */
+       bool CheckCache(DNS::Request* req, const DNS::Question& question)
+       {
+               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())
+                       return false;
+
+               Query& record = it->second;
+               if (IsExpired(record))
+               {
+                       this->cache.erase(it);
+                       return false;
+               }
+
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: Using cached result for " + question.name);
+               record.cached = true;
+               req->OnLookupComplete(&record);
+               return true;
+       }
+
+       /** Add a record to the dns cache
+        * @param r The record
+        */
+       void AddCache(Query& 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+1];
+
+       MyManager(Module* c) : Manager(c), Timer(5*60, true)
+               , unloading(false)
+       {
+               for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
+                       requests[i] = NULL;
+               ServerInstance->Timers.AddTimer(this);
+       }
+
+       ~MyManager()
+       {
+               // 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->question);
+                       rr.error = ERROR_UNKNOWN;
+                       request->OnError(&rr);
+
+                       delete request;
+               }
+       }
+
+       void Process(DNS::Request* req) CXX11_OVERRIDE
+       {
+               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
+               {
+                       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
+                               id = -1;
+                               for (unsigned int i = 0; i <= DNS::MAX_REQUEST_ID; i++)
+                               {
+                                       if (!this->requests[i])
+                                       {
+                                               id = i;
+                                               break;
+                                       }
+                               }
+
+                               if (id == -1)
+                                       throw Exception("DNS: All ids are in use");
+
+                               break;
+                       }
+               }
+               while (this->requests[id]);
+
+               req->id = id;
+               this->requests[req->id] = req;
+
+               Packet p;
+               p.flags = QUERYFLAGS_RD;
+               p.id = req->id;
+               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.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.question))
+               {
+                       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) != len)
+                       throw Exception("DNS: Unable to send query");
+
+               // Add timer for timeout
+               ServerInstance->Timers.AddTimer(req);
+       }
+
+       void RemoveRequest(DNS::Request* req) CXX11_OVERRIDE
+       {
+               if (requests[req->id] == req)
+                       requests[req->id] = NULL;
+       }
+
+       std::string GetErrorStr(Error e) CXX11_OVERRIDE
+       {
+               switch (e)
+               {
+                       case ERROR_UNLOADED:
+                               return "Module is unloading";
+                       case ERROR_TIMEDOUT:
+                               return "Request timed out";
+                       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:
+                       case ERROR_REFUSED:
+                       case ERROR_INVALIDTYPE:
+                               return "Nameserver failure";
+                       case ERROR_DOMAIN_NOT_FOUND:
+                       case ERROR_NO_RECORDS:
+                               return "Domain not found";
+                       case ERROR_NONE:
+                       case ERROR_UNKNOWN:
+                       default:
+                               return "Unknown error";
+               }
+       }
+
+       void OnEventHandlerError(int errcode) CXX11_OVERRIDE
+       {
+               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);
+
+               int length = SocketEngine::RecvFrom(this, buffer, sizeof(buffer), 0, &from.sa, &x);
+
+               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());
+               }
+
+               // recv_packet.id must be filled in here
+               DNS::Request* request = this->requests[recv_packet.id];
+               if (request == NULL)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received an answer for something we didn't request");
+                       return;
+               }
+
+               if (request->question != recv_packet.question)
+               {
+                       // 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 (!valid)
+               {
+                       ServerInstance->stats.DnsBad++;
+                       recv_packet.error = ERROR_MALFORMED;
+                       request->OnError(&recv_packet);
+               }
+               else if (recv_packet.flags & QUERYFLAGS_OPCODE)
+               {
+                       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_QR) || (recv_packet.flags & QUERYFLAGS_RCODE))
+               {
+                       Error error = ERROR_UNKNOWN;
+
+                       switch (recv_packet.flags & QUERYFLAGS_RCODE)
+                       {
+                               case 1:
+                                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "format error");
+                                       error = ERROR_FORMAT_ERROR;
+                                       break;
+                               case 2:
+                                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "server error");
+                                       error = ERROR_SERVER_FAILURE;
+                                       break;
+                               case 3:
+                                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "domain not found");
+                                       error = ERROR_DOMAIN_NOT_FOUND;
+                                       break;
+                               case 4:
+                                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "not implemented");
+                                       error = ERROR_NOT_IMPLEMENTED;
+                                       break;
+                               case 5:
+                                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "refused");
+                                       error = ERROR_REFUSED;
+                                       break;
+                               default:
+                                       break;
+                       }
+
+                       ServerInstance->stats.DnsBad++;
+                       recv_packet.error = error;
+                       request->OnError(&recv_packet);
+               }
+               else if (recv_packet.answers.empty())
+               {
+                       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(MODNAME, LOG_DEBUG, "Lookup complete for " + request->question.name);
+                       ServerInstance->stats.DnsGood++;
+                       request->OnLookupComplete(&recv_packet);
+                       this->AddCache(recv_packet);
+               }
+
+               ServerInstance->stats.Dns++;
+
+               /* Request's destructor removes it from the request map */
+               delete request;
+       }
+
+       bool Tick(time_t now) CXX11_OVERRIDE
+       {
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: purging DNS cache");
+
+               for (cache_map::iterator it = this->cache.begin(); it != this->cache.end(); )
+               {
+                       const Query& query = it->second;
+                       if (IsExpired(query, now))
+                               this->cache.erase(it++);
+                       else
+                               ++it;
+               }
+               return true;
+       }
+
+       void Rehash(const std::string& dnsserver, std::string sourceaddr, unsigned int sourceport)
+       {
+               if (this->GetFd() > -1)
+               {
+                       SocketEngine::Shutdown(this, 2);
+                       SocketEngine::Close(this);
+
+                       /* Remove expired entries from the cache */
+                       this->Tick(ServerInstance->Time());
+               }
+
+               irc::sockets::aptosa(dnsserver, DNS::PORT, myserver);
+
+               /* Initialize mastersocket */
+               int s = socket(myserver.family(), SOCK_DGRAM, 0);
+               this->SetFd(s);
+
+               /* Have we got a socket? */
+               if (this->GetFd() != -1)
+               {
+                       SocketEngine::SetReuse(s);
+                       SocketEngine::NonBlocking(s);
+
+                       irc::sockets::sockaddrs bindto;
+                       if (sourceaddr.empty())
+                       {
+                               // set a sourceaddr for irc::sockets::aptosa() based on the servers af type
+                               if (myserver.family() == AF_INET)
+                                       sourceaddr = "0.0.0.0";
+                               else if (myserver.family() == AF_INET6)
+                                       sourceaddr = "::";
+                       }
+                       irc::sockets::aptosa(sourceaddr, sourceport, bindto);
+
+                       if (SocketEngine::Bind(this->GetFd(), bindto) < 0)
+                       {
+                               /* Failed to bind */
+                               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(MODNAME, LOG_SPARSE, "Internal error starting DNS - hostnames will NOT resolve.");
+                               SocketEngine::Close(this->GetFd());
+                               this->SetFd(-1);
+                       }
+
+                       if (bindto.family() != myserver.family())
+                               ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Nameserver address family differs from source address family - hostnames might not resolve");
+               }
+               else
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error creating DNS socket - hostnames will NOT resolve");
+               }
+       }
+};
+
+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(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);
+               pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
+
+               if (pFixedInfo)
+               {
+                       if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW)
+                       {
+                               HeapFree(GetProcessHeap(), 0, pFixedInfo);
+                               pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
+                       }
+
+                       if (pFixedInfo)
+                       {
+                               if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
+                                       DNSServer = pFixedInfo->DnsServerList.IpAddress.String;
+
+                               HeapFree(GetProcessHeap(), 0, pFixedInfo);
+                       }
+
+                       if (!DNSServer.empty())
+                       {
+                               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(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(MODNAME, LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
+
+               std::ifstream resolv("/etc/resolv.conf");
+
+               while (resolv >> DNSServer)
+               {
+                       if (DNSServer == "nameserver")
+                       {
+                               resolv >> DNSServer;
+                               if (DNSServer.find_first_not_of("0123456789.") == std::string::npos || DNSServer.find_first_not_of("0123456789ABCDEFabcdef:") == std::string::npos)
+                               {
+                                       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(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)
+               , SourcePort(0)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               std::string oldserver = DNSServer;
+               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->getUInt("sourceport", 0, 0, UINT16_MAX);
+
+               if (DNSServer.empty())
+                       FindDNSServer();
+
+               if (oldserver != DNSServer || oldip != SourceIP || oldport != SourcePort)
+                       this->manager.Rehash(DNSServer, SourceIP, SourcePort);
+       }
+
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE
+       {
+               for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
+               {
+                       DNS::Request* req = this->manager.requests[i];
+                       if (!req)
+                               continue;
+
+                       if (req->creator == mod)
+                       {
+                               Query rr(req->question);
+                               rr.error = ERROR_UNLOADED;
+                               req->OnError(&rr);
+
+                               delete req;
+                       }
+               }
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides support for DNS lookups", VF_CORE|VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleDNS)
+
diff --git a/src/coremods/core_hostname_lookup.cpp b/src/coremods/core_hostname_lookup.cpp
new file mode 100644 (file)
index 0000000..85d0f53
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   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
+ * 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/dns.h"
+
+namespace
+{
+       LocalIntExt* dl;
+       LocalStringExt* ph;
+}
+
+/** Derived from Resolver, and performs user forward/reverse lookups.
+ */
+class UserResolver : public DNS::Request
+{
+       /** UUID we are looking up */
+       const std::string uuid;
+
+       /** True if the lookup is forward, false if is a reverse lookup
+        */
+       const bool fwd;
+
+ public:
+       /** Create a resolver.
+        * @param mgr DNS Manager
+        * @param me this module
+        * @param user The user to begin lookup on
+        * @param to_resolve The IP or host to resolve
+        * @param qt The query type
+        */
+       UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt)
+               : DNS::Request(mgr, me, to_resolve, qt)
+               , uuid(user->uuid)
+               , fwd(qt == DNS::QUERY_A || qt == DNS::QUERY_AAAA)
+       {
+       }
+
+       /** Called on successful lookup
+        * if a previous result has already come back.
+        * @param r The finished query
+        */
+       void OnLookupComplete(const DNS::Query* r) CXX11_OVERRIDE
+       {
+               LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
+               if (!bound_user)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
+                       return;
+               }
+
+               const DNS::ResourceRecord* ans_record = r->FindAnswerOfType(this->question.type);
+               if (ans_record == NULL)
+               {
+                       OnError(r);
+                       return;
+               }
+
+               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);
+
+                       UserResolver* res_forward;
+                       if (bound_user->client_sa.family() == AF_INET6)
+                       {
+                               /* IPV6 forward lookup */
+                               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);
+                       }
+                       try
+                       {
+                               this->manager->Process(res_forward);
+                       }
+                       catch (DNS::Exception& e)
+                       {
+                               delete res_forward;
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
+
+                               bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
+                               dl->set(bound_user, 0);
+                       }
+               }
+               else
+               {
+                       /* Both lookups completed */
+
+                       irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
+                       bool rev_match = false;
+                       if (user_ip->family() == AF_INET6)
+                       {
+                               struct in6_addr 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));
+                               }
+                       }
+                       else
+                       {
+                               struct in_addr 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));
+                               }
+                       }
+
+                       dl->set(bound_user, 0);
+
+                       if (rev_match)
+                       {
+                               std::string* hostname = ph->get(bound_user);
+
+                               if (hostname == NULL)
+                               {
+                                       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;
+                               }
+                               else if (hostname->length() <= ServerInstance->Config->Limits.MaxHost)
+                               {
+                                       /* Hostnames starting with : are not a good thing (tm) */
+                                       if ((*hostname)[0] == ':')
+                                               hostname->insert(0, "0");
+
+                                       bound_user->WriteNotice("*** Found your hostname (" + *hostname + (r->cached ? ") -- cached" : ")"));
+                                       bound_user->ChangeRealHost(hostname->substr(0, ServerInstance->Config->Limits.MaxHost), true);
+                               }
+                               else
+                               {
+                                       bound_user->WriteNotice("*** Your hostname is longer than the maximum of " + ConvToStr(ServerInstance->Config->Limits.MaxHost) + " characters, using your IP address (" + bound_user->GetIPString() + ") instead.");
+                               }
+
+                               ph->unset(bound_user);
+                       }
+                       else
+                       {
+                               bound_user->WriteNotice("*** Your hostname does not match up with your IP address. Sorry, using your IP address (" + bound_user->GetIPString() + ") instead.");
+                       }
+               }
+       }
+
+       /** Called on failed lookup
+        * @param query The errored query
+        */
+       void OnError(const DNS::Query* query) CXX11_OVERRIDE
+       {
+               LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
+               if (bound_user)
+               {
+                       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);
+               }
+       }
+};
+
+class ModuleHostnameLookup : public Module
+{
+       LocalIntExt dnsLookup;
+       LocalStringExt ptrHosts;
+       dynamic_reference<DNS::Manager> DNS;
+
+ public:
+       ModuleHostnameLookup()
+               : dnsLookup("dnsLookup", ExtensionItem::EXT_USER, this)
+               , ptrHosts("ptrHosts", ExtensionItem::EXT_USER, this)
+               , DNS(this, "DNS")
+       {
+               dl = &dnsLookup;
+               ph = &ptrHosts;
+       }
+
+       void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
+       {
+               // If core_dns is not loaded or hostname resolution is disabled for the user's
+               // connect class then the logic in this function does not apply.
+               if (!DNS || !user->MyClass->resolvehostnames)
+                       return;
+
+               // Clients can't have a DNS hostname if they aren't connected via IPv4 or IPv6.
+               if (user->client_sa.family() != AF_INET && user->client_sa.family() != AF_INET6)
+                       return;
+
+               user->WriteNotice("*** Looking up your hostname...");
+
+               UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
+               try
+               {
+                       /* If both the reverse and forward queries are cached, the user will be able to pass DNS completely
+                        * before Process() completes, which is why dnsLookup.set() is here, before Process()
+                        */
+                       this->dnsLookup.set(user, 1);
+                       this->DNS->Process(res_reverse);
+               }
+               catch (DNS::Exception& e)
+               {
+                       this->dnsLookup.set(user, 0);
+                       delete res_reverse;
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
+               }
+       }
+
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+       {
+               return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleHostnameLookup)
diff --git a/src/coremods/core_info/cmd_admin.cpp b/src/coremods/core_info/cmd_admin.cpp
new file mode 100644 (file)
index 0000000..233c7f2
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_info.h"
+
+CommandAdmin::CommandAdmin(Module* parent)
+       : ServerTargetCommand(parent, "ADMIN")
+{
+       Penalty = 2;
+       syntax = "[<servername>]";
+}
+
+/** Handle /ADMIN
+ */
+CmdResult CommandAdmin::Handle(User* user, const Params& parameters)
+{
+       if (parameters.size() > 0 && !irc::equals(parameters[0], ServerInstance->Config->ServerName))
+               return CMD_SUCCESS;
+       user->WriteRemoteNumeric(RPL_ADMINME, ServerInstance->Config->ServerName, "Administrative info");
+       if (!AdminName.empty())
+               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("Email: %s", AdminEmail.c_str()));
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_info/cmd_commands.cpp b/src/coremods/core_info/cmd_commands.cpp
new file mode 100644 (file)
index 0000000..0a3efdf
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_info.h"
+
+enum
+{
+       // InspIRCd-specific.
+       RPL_COMMANDS = 700,
+       RPL_COMMANDSEND = 701
+};
+
+CommandCommands::CommandCommands(Module* parent)
+       : Command(parent, "COMMANDS", 0, 0)
+{
+       Penalty = 3;
+}
+
+/** Handle /COMMANDS
+ */
+CmdResult CommandCommands::Handle(User* user, const Params& parameters)
+{
+       const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+       std::vector<std::string> list;
+       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 %s %d %d", i->second->name.c_str(), src->ModuleSourceFile.c_str(),
+                       i->second->min_params, i->second->Penalty));
+       }
+       std::sort(list.begin(), list.end());
+       for(unsigned int i=0; i < list.size(); i++)
+               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
new file mode 100644 (file)
index 0000000..29518e3
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2011 Jackmcbarn <jackmcbarn@jackmcbarn.no-ip.org>
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   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
+ * 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_info.h"
+
+CommandInfo::CommandInfo(Module* parent)
+       : ServerTargetCommand(parent, "INFO")
+{
+       Penalty = 4;
+       syntax = "[<servername>]";
+}
+
+static const char* const lines[] = {
+       "                   -/\\- \002InspIRCd\002 -\\/-",
+       "                 November 2002 - Present",
+       " ",
+       "\002Core Developers\002:",
+       "    Attila Molnar,          Attila,     <attilamolnar@hush.com>",
+       "    Peter Powell,           SaberUK,    <petpow@saberuk.com>",
+       " ",
+       "\002Former Developers\002:",
+       "    Oliver Lupton,          Om,         <om@inspircd.org>",
+       "    John Brooks,            Special,    <special@inspircd.org>",
+       "    Dennis Friis,           peavey,     <peavey@inspircd.org>",
+       "    Thomas Stagner,         aquanight,  <aquanight@inspircd.org>",
+       "    Uli Schlachter,         psychon,    <psychon@inspircd.org>",
+       "    Matt Smith,             dz,         <dz@inspircd.org>",
+       "    Daniel De Graaf,        danieldg,   <danieldg@inspircd.org>",
+       " ",
+       "\002Founding Developers\002:",
+       "    Craig Edwards,          Brain,      <brain@inspircd.org>",
+       "    Craig McLure,           Craig,      <craig@inspircd.org>",
+       "    Robin Burchell,         w00t,       <w00t@inspircd.org>",
+       " ",
+       "\002Active Contributors\002:",
+       "    Adam           linuxdaemon     Sheogorath",
+       " ",
+       "\002Former Contributors\002:",
+       "   dmb             Zaba            skenmy         GreenReaper",
+       "   Dan             Jason           satmd          owine",
+       "   Adremelech      John2           jilles         HiroP",
+       "   eggy            Bricker         AnMaster       djGrrr",
+       "   nenolod         Quension        praetorian     pippijn",
+       "   CC              jamie           typobox43      Burlex",
+       "   Stskeeps        ThaPrince       BuildSmart     Thunderhacker",
+       "   Skip            LeaChim         Majic          MacGyver",
+       "   Namegduf        Ankit           Phoenix        Taros",
+       "   jackmcbarn      ChrisTX         Shawn          Shutter",
+       " ",
+       "\002Thanks To\002:",
+       "   Asmo            Brik            fraggeln       genius3000",
+       " ",
+       " Best experienced with: \002An IRC client\002",
+       NULL
+};
+
+/** Handle /INFO
+ */
+CmdResult CommandInfo::Handle(User* user, const Params& parameters)
+{
+       if (parameters.size() > 0 && !irc::equals(parameters[0], ServerInstance->Config->ServerName))
+               return CMD_SUCCESS;
+
+       int i=0;
+       while (lines[i])
+               user->WriteRemoteNumeric(RPL_INFO, lines[i++]);
+
+       user->WriteRemoteNumeric(RPL_ENDOFINFO, "End of /INFO list");
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_info/cmd_modules.cpp b/src/coremods/core_info/cmd_modules.cpp
new file mode 100644 (file)
index 0000000..d68c8b9
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_info.h"
+
+enum
+{
+       // From ircd-ratbox with an InspIRCd-specific format.
+       RPL_MODLIST = 702,
+       RPL_ENDOFMODLIST = 703
+};
+
+CommandModules::CommandModules(Module* parent)
+       : ServerTargetCommand(parent, "MODULES")
+{
+       Penalty = 4;
+       syntax = "[<servername>]";
+}
+
+/** Handle /MODULES
+ */
+CmdResult CommandModules::Handle(User* user, const Params& parameters)
+{
+       // Don't ask remote servers about their modules unless the local user asking is an oper
+       // 2.0 asks anyway, so let's handle that the same way
+       bool for_us = (parameters.empty() || irc::equals(parameters[0], ServerInstance->Config->ServerName));
+       if ((!for_us) || (!IS_LOCAL(user)))
+       {
+               if (!user->IsOper())
+               {
+                       user->WriteNotice("*** You cannot check what modules other servers have loaded.");
+                       return CMD_FAILURE;
+               }
+
+               // From an oper and not for us, forward
+               if (!for_us)
+                       return CMD_SUCCESS;
+       }
+
+       const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+
+       for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+       {
+               Module* m = i->second;
+               Version V = m->GetVersion();
+
+               if (IS_LOCAL(user) && user->HasPrivPermission("servers/auspex"))
+               {
+                       std::string flags("VCO");
+                       size_t pos = 0;
+                       for (int mult = 2; mult <= VF_OPTCOMMON; mult *= 2, ++pos)
+                               if (!(V.Flags & mult))
+                                       flags[pos] = '-';
+
+                       std::string srcrev = m->ModuleDLLManager->GetVersion();
+                       user->WriteRemoteNumeric(RPL_MODLIST, m->ModuleSourceFile, srcrev.empty() ? "*" : srcrev, flags, V.description);
+               }
+               else
+               {
+                       user->WriteRemoteNumeric(RPL_MODLIST, m->ModuleSourceFile, '*', '*', V.description);
+               }
+       }
+       user->WriteRemoteNumeric(RPL_ENDOFMODLIST, "End of MODULES list");
+
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_info/cmd_motd.cpp b/src/coremods/core_info/cmd_motd.cpp
new file mode 100644 (file)
index 0000000..eb6460f
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_info.h"
+
+CommandMotd::CommandMotd(Module* parent)
+       : ServerTargetCommand(parent, "MOTD")
+{
+       syntax = "[<servername>]";
+}
+
+/** Handle /MOTD
+ */
+CmdResult CommandMotd::Handle(User* user, const Params& parameters)
+{
+       if (parameters.size() > 0 && !irc::equals(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 = ServerInstance->Config->EmptyTag;
+       LocalUser* localuser = IS_LOCAL(user);
+       if (localuser)
+               tag = localuser->GetClass()->config;
+       std::string motd_name = tag->getString("motd", "motd");
+       ConfigFileCache::iterator motd = motds.find(motd_name);
+       if (motd == motds.end())
+       {
+               user->WriteRemoteNumeric(ERR_NOMOTD, "Message of the day file is missing.");
+               return CMD_SUCCESS;
+       }
+
+       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->WriteRemoteNumeric(RPL_MOTD, InspIRCd::Format("- %s", i->c_str()));
+
+       user->WriteRemoteNumeric(RPL_ENDOFMOTD, "End of message of the day.");
+
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_info/cmd_time.cpp b/src/coremods/core_info/cmd_time.cpp
new file mode 100644 (file)
index 0000000..725ef6c
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_info.h"
+
+CommandTime::CommandTime(Module* parent)
+       : ServerTargetCommand(parent, "TIME")
+{
+       syntax = "[<servername>]";
+}
+
+CmdResult CommandTime::Handle(User* user, const Params& parameters)
+{
+       if (parameters.size() > 0 && !irc::equals(parameters[0], ServerInstance->Config->ServerName))
+               return CMD_SUCCESS;
+
+       user->WriteRemoteNumeric(RPL_TIME, ServerInstance->Config->ServerName, InspIRCd::TimeString(ServerInstance->Time()));
+
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_info/cmd_version.cpp b/src/coremods/core_info/cmd_version.cpp
new file mode 100644 (file)
index 0000000..57f9b05
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_info.h"
+
+CommandVersion::CommandVersion(Module* parent)
+       : Command(parent, "VERSION", 0, 0)
+{
+       syntax = "[<servername>]";
+}
+
+CmdResult CommandVersion::Handle(User* user, const Params& parameters)
+{
+       Numeric::Numeric numeric(RPL_VERSION);
+       irc::tokenstream tokens(ServerInstance->GetVersionString(user->IsOper()));
+       for (std::string token; tokens.GetTrailing(token); )
+               numeric.push(token);
+       user->WriteNumeric(numeric);
+
+       LocalUser *lu = IS_LOCAL(user);
+       if (lu != NULL)
+       {
+               ServerInstance->ISupport.SendTo(lu);
+       }
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_info/core_info.cpp b/src/coremods/core_info/core_info.cpp
new file mode 100644 (file)
index 0000000..23b1683
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * 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 "core_info.h"
+
+enum
+{
+       // From RFC 2812.
+       RPL_WELCOME = 1,
+       RPL_YOURHOST = 2,
+       RPL_CREATED = 3,
+       RPL_MYINFO = 4
+};
+
+RouteDescriptor ServerTargetCommand::GetRouting(User* user, const Params& 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;
+       CommandCommands cmdcommands;
+       CommandInfo cmdinfo;
+       CommandModules cmdmodules;
+       CommandMotd cmdmotd;
+       CommandTime cmdtime;
+       CommandVersion cmdversion;
+       Numeric::Numeric numeric004;
+
+       /** Returns a list of user or channel mode characters.
+        * Used for constructing the parts of the mode list in the 004 numeric.
+        * @param mt Controls whether to list user modes or channel modes
+        * @param needparam Return modes only if they require a parameter to be set
+        * @return The available mode letters that satisfy the given conditions
+       */
+       static std::string CreateModeList(ModeType mt, bool needparam = false)
+       {
+               std::string modestr;
+               for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+               {
+                       ModeHandler* mh = ServerInstance->Modes.FindMode(mode, mt);
+                       if ((mh) && ((!needparam) || (mh->NeedsParam(true))))
+                               modestr.push_back(mode);
+               }
+               return modestr;
+       }
+
+       void OnServiceChange(const ServiceProvider& prov)
+       {
+               if (prov.service != SERVICE_MODE)
+                       return;
+
+               std::vector<std::string>& params = numeric004.GetParams();
+               params.erase(params.begin()+2, params.end());
+
+               // Create lists of modes
+               // 1. User modes
+               // 2. Channel modes
+               // 3. Channel modes that require a parameter when set
+               numeric004.push(CreateModeList(MODETYPE_USER));
+               numeric004.push(CreateModeList(MODETYPE_CHANNEL));
+               numeric004.push(CreateModeList(MODETYPE_CHANNEL, true));
+       }
+ public:
+       CoreModInfo()
+               : cmdadmin(this)
+               , cmdcommands(this)
+               , cmdinfo(this)
+               , cmdmodules(this)
+               , cmdmotd(this)
+               , cmdtime(this)
+               , cmdversion(this)
+               , numeric004(RPL_MYINFO)
+       {
+               numeric004.push(ServerInstance->Config->ServerName);
+               numeric004.push(INSPIRCD_BRANCH);
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               // Process the escape codes in the MOTDs.
+               ConfigFileCache newmotds;
+               for (ServerConfig::ClassVector::const_iterator iter = ServerInstance->Config->Classes.begin(); iter != ServerInstance->Config->Classes.end(); ++iter)
+               {
+                       ConfigTag* tag = (*iter)->config;
+                       // Don't process the file if it has already been processed.
+                       const std::string motd = tag->getString("motd", "motd");
+                       if (newmotds.find(motd) != newmotds.end())
+                               continue;
+
+                       // We can't process the file if it doesn't exist.
+                       ConfigFileCache::iterator file = ServerInstance->Config->Files.find(motd);
+                       if (file == ServerInstance->Config->Files.end())
+                               continue;
+
+                       // Process escape codes.
+                       newmotds[file->first] = file->second;
+                       InspIRCd::ProcessColors(newmotds[file->first]);
+               }
+
+               cmdmotd.motds.swap(newmotds);
+
+               ConfigTag* tag = ServerInstance->Config->ConfValue("admin");
+               cmdadmin.AdminName = tag->getString("name");
+               cmdadmin.AdminEmail = tag->getString("email", "null@example.com");
+               cmdadmin.AdminNick = tag->getString("nick", "admin");
+       }
+
+       void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
+       {
+               user->WriteNumeric(RPL_WELCOME, InspIRCd::Format("Welcome to the %s IRC Network %s", ServerInstance->Config->Network.c_str(), user->GetFullRealHost().c_str()));
+               user->WriteNumeric(RPL_YOURHOST, InspIRCd::Format("Your host is %s, running version %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH));
+               user->WriteNumeric(RPL_CREATED, InspIRCd::TimeString(ServerInstance->startup_time, "This server was created %H:%M:%S %b %d %Y"));
+               user->WriteNumeric(numeric004);
+
+               ServerInstance->ISupport.SendTo(user);
+
+               /* Trigger MOTD and LUSERS output, give modules a chance too */
+               ModResult MOD_RESULT;
+               std::string command("LUSERS");
+               CommandBase::Params parameters;
+               FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, user, true));
+               if (!MOD_RESULT)
+                       ServerInstance->Parser.CallHandler(command, parameters, user);
+
+               MOD_RESULT = MOD_RES_PASSTHRU;
+               command = "MOTD";
+               FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, user, true));
+               if (!MOD_RESULT)
+                       ServerInstance->Parser.CallHandler(command, parameters, user);
+
+               if (ServerInstance->Config->RawLog)
+               {
+                       ClientProtocol::Messages::Privmsg rawlogmsg(ServerInstance->FakeClient, user, "*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
+                       user->Send(ServerInstance->GetRFCEvents().privmsg, rawlogmsg);
+               }
+       }
+
+       void OnServiceAdd(ServiceProvider& service) CXX11_OVERRIDE
+       {
+               OnServiceChange(service);
+       }
+
+       void OnServiceDel(ServiceProvider& service) CXX11_OVERRIDE
+       {
+               OnServiceChange(service);
+       }
+
+       void Prioritize() CXX11_OVERRIDE
+       {
+               ServerInstance->Modules.SetPriority(this, I_OnUserConnect, PRIORITY_FIRST);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the ADMIN, COMMANDS, INFO, MODULES, MOTD, TIME, and VERSION commands", VF_VENDOR|VF_CORE);
+       }
+};
+
+MODULE_INIT(CoreModInfo)
diff --git a/src/coremods/core_info/core_info.h b/src/coremods/core_info/core_info.h
new file mode 100644 (file)
index 0000000..5cc619a
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * 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 "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 Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /ADMIN.
+ */
+class CommandAdmin : public ServerTargetCommand
+{
+ public:
+       /** Holds the admin's name, for output in
+        * the /ADMIN command.
+        */
+       std::string AdminName;
+
+       /** Holds the email address of the admin,
+        * for output in the /ADMIN command.
+        */
+       std::string AdminEmail;
+
+       /** Holds the admin's nickname, for output
+        * in the /ADMIN command
+        */
+       std::string AdminNick;
+
+       /** Constructor for admin.
+        */
+       CommandAdmin(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /COMMANDS.
+ */
+class CommandCommands : public Command
+{
+ public:
+       /** Constructor for commands.
+        */
+       CommandCommands(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /INFO.
+ */
+class CommandInfo : public ServerTargetCommand
+{
+ public:
+       /** Constructor for info.
+        */
+       CommandInfo(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /MODULES.
+ */
+class CommandModules : public ServerTargetCommand
+{
+ public:
+       /** Constructor for modules.
+        */
+       CommandModules(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /MOTD.
+ */
+class CommandMotd : public ServerTargetCommand
+{
+ public:
+       ConfigFileCache motds;
+
+       /** Constructor for motd.
+        */
+       CommandMotd(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /TIME.
+ */
+class CommandTime : public ServerTargetCommand
+{
+ public:
+       /** Constructor for time.
+        */
+       CommandTime(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /VERSION.
+ */
+class CommandVersion : public Command
+{
+ public:
+       /** Constructor for version.
+        */
+       CommandVersion(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
diff --git a/src/coremods/core_list.cpp b/src/coremods/core_list.cpp
new file mode 100644 (file)
index 0000000..40a5c8b
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+/** Handle /LIST.
+ */
+class CommandList : public Command
+{
+ private:
+       ChanModeReference secretmode;
+       ChanModeReference privatemode;
+
+       /** Parses the creation time or topic set time out of a LIST parameter.
+        * @param value The parameter containing a minute count.
+        * @return The UNIX time at \p value minutes ago.
+        */
+       time_t ParseMinutes(const std::string& value)
+       {
+               time_t minutes = ConvToNum<time_t>(value.c_str() + 2);
+               if (!minutes)
+                       return 0;
+               return ServerInstance->Time() - (minutes * 60);
+       }
+
+ public:
+       /** Constructor for list.
+        */
+       CommandList(Module* parent)
+               : Command(parent,"LIST", 0, 0)
+               , secretmode(creator, "secret")
+               , privatemode(creator, "private")
+       {
+               allow_empty_last_param = false;
+               Penalty = 5;
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+
+/** Handle /LIST
+ */
+CmdResult CommandList::Handle(User* user, const Params& parameters)
+{
+       // C: Searching based on creation time, via the "C<val" and "C>val" modifiers
+       // to search for a channel creation time that is lower or higher than val
+       // respectively.
+       time_t mincreationtime = 0;
+       time_t maxcreationtime = 0;
+
+       // M: Searching based on mask.
+       // N: Searching based on !mask.
+       bool match_name_topic = false;
+       bool match_inverted = false;
+       const char* match = NULL;
+
+       // T: Searching based on topic time, via the "T<val" and "T>val" modifiers to
+       // search for a topic time that is lower or higher than val respectively.
+       time_t mintopictime = 0;
+       time_t maxtopictime = 0;
+
+       // U: Searching based on user count within the channel, via the "<val" and
+       // ">val" modifiers to search for a channel that has less than or more than
+       // val users respectively.
+       size_t minusers = 0;
+       size_t maxusers = 0;
+
+       for (Params::const_iterator iter = parameters.begin(); iter != parameters.end(); ++iter)
+       {
+               const std::string& constraint = *iter;
+               if (constraint[0] == '<')
+               {
+                       maxusers = ConvToNum<size_t>(constraint.c_str() + 1);
+               }
+               else if (constraint[0] == '>')
+               {
+                       minusers = ConvToNum<size_t>(constraint.c_str() + 1);
+               }
+               else if (!constraint.compare(0, 2, "C<", 2) || !constraint.compare(0, 2, "c<", 2))
+               {
+                       mincreationtime = ParseMinutes(constraint);
+               }
+               else if (!constraint.compare(0, 2, "C>", 2) || !constraint.compare(0, 2, "c>", 2))
+               {
+                       maxcreationtime = ParseMinutes(constraint);
+               }
+               else if (!constraint.compare(0, 2, "T<", 2) || !constraint.compare(0, 2, "t<", 2))
+               {
+                       mintopictime = ParseMinutes(constraint);
+               }
+               else if (!constraint.compare(0, 2, "T>", 2) || !constraint.compare(0, 2, "t>", 2))
+               {
+                       maxtopictime = ParseMinutes(constraint);
+               }
+               else
+               {
+                       // If the glob is prefixed with ! it is inverted.
+                       match = constraint.c_str();
+                       if (match[0] == '!')
+                       {
+                               match_inverted = true;
+                               match += 1;
+                       }
+
+                       // Ensure that the user didn't just run "LIST !".
+                       if (match[0])
+                               match_name_topic = true;
+               }
+       }
+
+       const bool has_privs = user->HasPrivPermission("channels/auspex");
+
+       user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
+       const chan_hash& chans = ServerInstance->GetChans();
+       for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+       {
+               Channel* const chan = i->second;
+
+               // Check the user count if a search has been specified.
+               const size_t users = chan->GetUserCounter();
+               if ((minusers && users <= minusers) || (maxusers && users >= maxusers))
+                       continue;
+
+               // Check the creation ts if a search has been specified.
+               const time_t creationtime = chan->age;
+               if ((mincreationtime && creationtime <= mincreationtime) || (maxcreationtime && creationtime >= maxcreationtime))
+                       continue;
+
+               // Check the topic ts if a search has been specified.
+               const time_t topictime = chan->topicset;
+               if ((mintopictime && (!topictime || topictime <= mintopictime)) || (maxtopictime && (!topictime || topictime >= maxtopictime)))
+                       continue;
+
+               // Attempt to match a glob pattern.
+               if (match_name_topic)
+               {
+                       bool matches = InspIRCd::Match(chan->name, match) || InspIRCd::Match(chan->topic, match);
+
+                       // The user specified an match that we did not match.
+                       if (!matches && !match_inverted)
+                               continue;
+
+                       // The user specified an inverted match that we did match.
+                       if (matches && match_inverted)
+                               continue;
+               }
+
+               // if the channel is not private/secret, OR the user is on the channel anyway
+               bool n = (has_privs || chan->HasUser(user));
+
+               // 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) && (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, chan->name, users, InspIRCd::Format("[+%s] %s", chan->ChanModes(n), chan->topic.c_str()));
+                       }
+               }
+       }
+       user->WriteNumeric(RPL_LISTEND, "End of channel list.");
+
+       return CMD_SUCCESS;
+}
+
+class CoreModList : public Module
+{
+ private:
+       CommandList cmd;
+
+ public:
+       CoreModList()
+               : cmd(this)
+       {
+       }
+
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+       {
+               tokens["ELIST"] = "CMNTU";
+               tokens["SAFELIST"];
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the LIST command", VF_VENDOR|VF_CORE);
+       }
+};
+
+MODULE_INIT(CoreModList)
diff --git a/src/coremods/core_loadmodule.cpp b/src/coremods/core_loadmodule.cpp
new file mode 100644 (file)
index 0000000..faecab2
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+/** Handle /LOADMODULE.
+ */
+class CommandLoadmodule : public Command
+{
+ public:
+       /** Constructor for loadmodule.
+        */
+       CommandLoadmodule ( Module* parent) : Command(parent,"LOADMODULE",1,1) { flags_needed='o'; syntax = "<modulename>"; }
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /LOADMODULE
+ */
+CmdResult CommandLoadmodule::Handle(User* user, const Params& 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, parameters[0], "Module successfully loaded.");
+               return CMD_SUCCESS;
+       }
+       else
+       {
+               user->WriteNumeric(ERR_CANTLOADMODULE, parameters[0], ServerInstance->Modules->LastError());
+               return CMD_FAILURE;
+       }
+}
+
+/** Handle /UNLOADMODULE.
+ */
+class CommandUnloadmodule : public Command
+{
+ public:
+       bool allowcoreunload;
+
+       /** Constructor for unloadmodule.
+        */
+       CommandUnloadmodule(Module* parent)
+               : Command(parent, "UNLOADMODULE", 1)
+               , allowcoreunload(false)
+       {
+               flags_needed = 'o';
+               syntax = "<modulename>";
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+CmdResult CommandUnloadmodule::Handle(User* user, const Params& parameters)
+{
+       if (!allowcoreunload && InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map))
+       {
+               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, 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, parameters[0], "Module successfully unloaded.");
+       }
+       else
+       {
+               user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], (m ? ServerInstance->Modules->LastError() : "No such module"));
+               return CMD_FAILURE;
+       }
+
+       return CMD_SUCCESS;
+}
+
+class CoreModLoadModule : public Module
+{
+       CommandLoadmodule cmdloadmod;
+       CommandUnloadmodule cmdunloadmod;
+
+ public:
+       CoreModLoadModule()
+               : cmdloadmod(this), cmdunloadmod(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the LOADMODULE and UNLOADMODULE commands", VF_VENDOR|VF_CORE);
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("security");
+               cmdunloadmod.allowcoreunload = tag->getBool("allowcoreunload");
+       }
+};
+
+MODULE_INIT(CoreModLoadModule)
diff --git a/src/coremods/core_lusers.cpp b/src/coremods/core_lusers.cpp
new file mode 100644 (file)
index 0000000..ab6131b
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+struct LusersCounters
+{
+       unsigned int max_local;
+       unsigned int max_global;
+       unsigned int invisible;
+
+       LusersCounters(unsigned int inv)
+               : max_local(ServerInstance->Users->LocalUserCount())
+               , max_global(ServerInstance->Users->RegisteredUserCount())
+               , invisible(inv)
+       {
+       }
+
+       inline void UpdateMaxUsers()
+       {
+               unsigned int current = ServerInstance->Users->LocalUserCount();
+               if (current > max_local)
+                       max_local = current;
+
+               current = ServerInstance->Users->RegisteredUserCount();
+               if (current > max_global)
+                       max_global = current;
+       }
+};
+
+/** Handle /LUSERS.
+ */
+class CommandLusers : public Command
+{
+       LusersCounters& counters;
+ public:
+       /** Constructor for lusers.
+        */
+       CommandLusers(Module* parent, LusersCounters& Counters)
+               : Command(parent,"LUSERS",0,0), counters(Counters)
+       { }
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /LUSERS
+ */
+CmdResult CommandLusers::Handle(User* user, const Params& parameters)
+{
+       unsigned int n_users = ServerInstance->Users->RegisteredUserCount();
+       ProtocolInterface::ServerList serverlist;
+       ServerInstance->PI->GetServerList(serverlist);
+       unsigned int n_serv = serverlist.size();
+       unsigned int n_local_servs = 0;
+       for (ProtocolInterface::ServerList::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
+       {
+               if (i->parentname == ServerInstance->Config->ServerName)
+                       n_local_servs++;
+       }
+       // fix for default GetServerList not returning us
+       if (!n_serv)
+               n_serv = 1;
+
+       counters.UpdateMaxUsers();
+
+       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, ServerInstance->Users.OperCount(), "operator(s) online");
+
+       if (ServerInstance->Users->UnregisteredUserCount())
+               user->WriteNumeric(RPL_LUSERUNKNOWN, ServerInstance->Users.UnregisteredUserCount(), "unknown connections");
+
+       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;
+}
+
+class InvisibleWatcher : public ModeWatcher
+{
+       unsigned int& invisible;
+public:
+       InvisibleWatcher(Module* mod, unsigned int& Invisible)
+               : ModeWatcher(mod, "invisible", MODETYPE_USER), invisible(Invisible)
+       {
+       }
+
+       void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding) CXX11_OVERRIDE
+       {
+               if (dest->registered != REG_ALL)
+                       return;
+
+               if (adding)
+                       invisible++;
+               else
+                       invisible--;
+       }
+};
+
+class ModuleLusers : public Module
+{
+       UserModeReference invisiblemode;
+       LusersCounters counters;
+       CommandLusers cmd;
+       InvisibleWatcher mw;
+
+       unsigned int CountInvisible()
+       {
+               unsigned int c = 0;
+               const user_hash& users = ServerInstance->Users->GetUsers();
+               for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+               {
+                       User* u = i->second;
+                       if (u->IsModeSet(invisiblemode))
+                               c++;
+               }
+               return c;
+       }
+
+ public:
+       ModuleLusers()
+               : invisiblemode(this, "invisible")
+               , counters(CountInvisible())
+               , cmd(this, counters)
+               , mw(this, counters.invisible)
+       {
+       }
+
+       void OnPostConnect(User* user) CXX11_OVERRIDE
+       {
+               counters.UpdateMaxUsers();
+               if (user->IsModeSet(invisiblemode))
+                       counters.invisible++;
+       }
+
+       void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
+       {
+               if (user->IsModeSet(invisiblemode))
+                       counters.invisible--;
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the LUSERS command", VF_VENDOR | VF_CORE);
+       }
+};
+
+MODULE_INIT(ModuleLusers)
diff --git a/src/coremods/core_message.cpp b/src/coremods/core_message.cpp
new file mode 100644 (file)
index 0000000..a17157b
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+enum
+{
+       // From RFC 2812.
+       ERR_NOSUCHSERVICE = 408
+};
+
+class MessageDetailsImpl : public MessageDetails
+{
+public:
+       MessageDetailsImpl(MessageType mt, const std::string& msg, const ClientProtocol::TagMap& tags)
+               : MessageDetails(mt, msg, tags)
+       {
+       }
+
+       bool IsCTCP(std::string& name, std::string& body) const CXX11_OVERRIDE
+       {
+               if (!this->IsCTCP())
+                       return false;
+
+               size_t end_of_name = text.find(' ', 2);
+               size_t end_of_ctcp = *text.rbegin() == '\x1' ? 1 : 0;
+               if (end_of_name == std::string::npos)
+               {
+                       // The CTCP only contains a name.
+                       name.assign(text, 1, text.length() - 1 - end_of_ctcp);
+                       body.clear();
+                       return true;
+               }
+
+               // The CTCP contains a name and a body.
+               name.assign(text, 1, end_of_name - 1);
+
+               size_t start_of_body = text.find_first_not_of(' ', end_of_name + 1);
+               if (start_of_body == std::string::npos)
+               {
+                       // The CTCP body is provided but empty.
+                       body.clear();
+                       return true;
+               }
+
+               // The CTCP body provided was non-empty.
+               body.assign(text, start_of_body, text.length() - start_of_body - end_of_ctcp);
+               return true;
+       }
+
+       bool IsCTCP(std::string& name) const CXX11_OVERRIDE
+       {
+               if (!this->IsCTCP())
+                       return false;
+
+               size_t end_of_name = text.find(' ', 2);
+               if (end_of_name == std::string::npos)
+               {
+                       // The CTCP only contains a name.
+                       size_t end_of_ctcp = *text.rbegin() == '\x1' ? 1 : 0;
+                       name.assign(text, 1, text.length() - 1 - end_of_ctcp);
+                       return true;
+               }
+
+               // The CTCP contains a name and a body.
+               name.assign(text, 1, end_of_name - 1);
+               return true;
+       }
+
+       bool IsCTCP() const CXX11_OVERRIDE
+       {
+               // According to draft-oakley-irc-ctcp-02 a valid CTCP must begin with SOH and
+               // contain at least one octet which is not NUL, SOH, CR, LF, or SPACE. As most
+               // of these are restricted at the protocol level we only need to check for SOH
+               // and SPACE.
+               return (text.length() >= 2) && (text[0] == '\x1') &&  (text[1] != '\x1') && (text[1] != ' ');
+       }
+};
+
+namespace
+{
+       bool FirePreEvents(User* source, MessageTarget& msgtarget, MessageDetails& msgdetails)
+       {
+               // Inform modules that a message wants to be sent.
+               ModResult modres;
+               FIRST_MOD_RESULT(OnUserPreMessage, modres, (source, msgtarget, msgdetails));
+               if (modres == MOD_RES_DENY)
+               {
+                       // Inform modules that a module blocked the mssage.
+                       FOREACH_MOD(OnUserMessageBlocked, (source, msgtarget, msgdetails));
+                       return false;
+               }
+
+               // Check whether a module zapped the message body.
+               if (msgdetails.text.empty())
+               {
+                       source->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
+                       return false;
+               }
+
+               // Inform modules that a message is about to be sent.
+               FOREACH_MOD(OnUserMessage, (source, msgtarget, msgdetails));
+               return true;
+       }
+
+       CmdResult FirePostEvent(User* source, const MessageTarget& msgtarget, const MessageDetails& msgdetails)
+       {
+               // If the source is local and was not sending a CTCP reply then update their idle time.
+               LocalUser* lsource = IS_LOCAL(source);
+               if (lsource && (msgdetails.type != MSG_NOTICE || !msgdetails.IsCTCP()))
+                       lsource->idle_lastmsg = ServerInstance->Time();
+
+               // Inform modules that a message was sent.
+               FOREACH_MOD(OnUserPostMessage, (source, msgtarget, msgdetails));
+               return CMD_SUCCESS;
+       }
+}
+
+class CommandMessage : public Command
+{
+ private:
+       const MessageType msgtype;
+       ChanModeReference moderatedmode;
+       ChanModeReference noextmsgmode;
+
+       CmdResult HandleChannelTarget(User* source, const Params& parameters, const char* target, PrefixMode* pm)
+       {
+               Channel* chan = ServerInstance->FindChan(target);
+               if (!chan)
+               {
+                       // The target channel does not exist.
+                       source->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+                       return CMD_FAILURE;
+               }
+
+               if (IS_LOCAL(source))
+               {
+                       if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(source))
+                       {
+                               // The noextmsg mode is set and the source is not in the channel.
+                               source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (no external messages)");
+                               return CMD_FAILURE;
+                       }
+
+                       bool no_chan_priv = chan->GetPrefixValue(source) < VOICE_VALUE;
+                       if (no_chan_priv && chan->IsModeSet(moderatedmode))
+                       {
+                               // The moderated mode is set and the source has no status rank.
+                               source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (+m is set)");
+                               return CMD_FAILURE;
+                       }
+
+                       if (no_chan_priv && ServerInstance->Config->RestrictBannedUsers != ServerConfig::BUT_NORMAL && chan->IsBanned(source))
+                       {
+                               // The source is banned in the channel and restrictbannedusers is enabled.
+                               if (ServerInstance->Config->RestrictBannedUsers == ServerConfig::BUT_RESTRICT_NOTIFY)
+                                       source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)");
+                               return CMD_FAILURE;
+                       }
+               }
+
+               // Fire the pre-message events.
+               MessageTarget msgtarget(chan, pm ? pm->GetPrefix() : 0);
+               MessageDetailsImpl msgdetails(msgtype, parameters[1], parameters.GetTags());
+               msgdetails.exemptions.insert(source);
+               if (!FirePreEvents(source, msgtarget, msgdetails))
+                       return CMD_FAILURE;
+
+               // Send the message to the members of the channel.
+               ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, source, chan, msgdetails.text, msgdetails.type, msgtarget.status);
+               privmsg.AddTags(msgdetails.tags_out);
+               privmsg.SetSideEffect(true);
+               chan->Write(ServerInstance->GetRFCEvents().privmsg, privmsg, msgtarget.status, msgdetails.exemptions);
+
+               // Create the outgoing message and message event.
+               return FirePostEvent(source, msgtarget, msgdetails);
+       }
+
+       CmdResult HandleServerTarget(User* source, const Params& parameters)
+       {
+               // If the source isn't allowed to mass message users then reject
+               // the attempt to mass-message users.
+               if (!source->HasPrivPermission("users/mass-message"))
+                       return CMD_FAILURE;
+
+               // Extract the server glob match from the target parameter.
+               std::string servername(parameters[0], 1);
+
+               // Fire the pre-message events.
+               MessageTarget msgtarget(&servername);
+               MessageDetailsImpl msgdetails(msgtype, parameters[1], parameters.GetTags());
+               if (!FirePreEvents(source, msgtarget, msgdetails))
+                       return CMD_FAILURE;
+
+               // If the current server name matches the server name glob then send
+               // the message out to the local users.
+               if (InspIRCd::Match(ServerInstance->Config->ServerName, servername))
+               {
+                       // Create the outgoing message and message event.
+                       ClientProtocol::Messages::Privmsg message(ClientProtocol::Messages::Privmsg::nocopy, source, "$*", msgdetails.text, msgdetails.type);
+                       message.AddTags(msgdetails.tags_out);
+                       message.SetSideEffect(true);
+                       ClientProtocol::Event messageevent(ServerInstance->GetRFCEvents().privmsg, message);
+
+                       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+                       for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+                       {
+                               LocalUser* luser = *i;
+
+                               // Don't send to unregistered users or the user who is the source.
+                               if (luser->registered != REG_ALL || luser == source)
+                                       continue;
+
+                               // Only send to non-exempt users.
+                               if (!msgdetails.exemptions.count(luser))
+                                       luser->Send(messageevent);
+                       }
+               }
+
+               // Fire the post-message event.
+               return FirePostEvent(source, msgtarget, msgdetails);
+       }
+
+       CmdResult HandleUserTarget(User* source, const Params& parameters)
+       {
+               User* target;
+               if (IS_LOCAL(source))
+               {
+                       // Local sources can specify either a nick or a nick@server mask as the target.
+                       const char* targetserver = strchr(parameters[0].c_str(), '@');
+                       if (targetserver)
+                       {
+                               // The target is a user on a specific server (e.g. jto@tolsun.oulu.fi).
+                               target = ServerInstance->FindNickOnly(parameters[0].substr(0, targetserver - parameters[0].c_str()));
+                               if (target && strcasecmp(target->server->GetName().c_str(), targetserver + 1))
+                                       target = NULL;
+                       }
+                       else
+                       {
+                               // If the source is a local user then we only look up the target by nick.
+                               target = ServerInstance->FindNickOnly(parameters[0]);
+                       }
+               }
+               else
+               {
+                       // Remote users can only specify a nick or UUID as the target.
+                       target = ServerInstance->FindNick(parameters[0]);
+               }
+
+               if (!target || target->registered != REG_ALL)
+               {
+                       // The target user does not exist or is not fully registered.
+                       source->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+                       return CMD_FAILURE;
+               }
+
+               // If the target is away then inform the user.
+               if (target->IsAway() && msgtype == MSG_PRIVMSG)
+                       source->WriteNumeric(RPL_AWAY, target->nick, target->awaymsg);
+
+               // Fire the pre-message events.
+               MessageTarget msgtarget(target);
+               MessageDetailsImpl msgdetails(msgtype, parameters[1], parameters.GetTags());
+               if (!FirePreEvents(source, msgtarget, msgdetails))
+                       return CMD_FAILURE;
+
+               LocalUser* const localtarget = IS_LOCAL(target);
+               if (localtarget)
+               {
+                       // Send to the target if they are a local user.
+                       ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, source, localtarget->nick, msgdetails.text, msgtype);
+                       privmsg.AddTags(msgdetails.tags_out);
+                       privmsg.SetSideEffect(true);
+                       localtarget->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
+               }
+
+               // Fire the post-message event.
+               return FirePostEvent(source, msgtarget, msgdetails);
+       }
+
+ public:
+       CommandMessage(Module* parent, MessageType mt)
+               : Command(parent, ClientProtocol::Messages::Privmsg::CommandStrFromMsgType(mt), 2, 2)
+               , msgtype(mt)
+               , moderatedmode(parent, "moderated")
+               , noextmsgmode(parent, "noextmsg")
+       {
+               syntax = "<target>[,<target>]+ :<message>";
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               if (CommandParser::LoopCall(user, this, parameters, 0))
+                       return CMD_SUCCESS;
+
+               // The specified message was empty.
+               if (parameters[1].empty())
+               {
+                       user->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
+                       return CMD_FAILURE;
+               }
+
+               // The target is a server glob.
+               if (parameters[0][0] == '$')
+                       return HandleServerTarget(user, parameters);
+
+               // If the message begins with a status character then look it up.
+               const char* target = parameters[0].c_str();
+               PrefixMode* pmh = ServerInstance->Modes->FindPrefix(target[0]);
+               if (pmh)
+                       target++;
+
+               // The target is a channel name.
+               if (*target == '#')
+                       return HandleChannelTarget(user, parameters, target, pmh);
+
+               // The target is a nickname.
+               return HandleUserTarget(user, parameters);
+       }
+
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               if (IS_LOCAL(user))
+                       // This is handled by the OnUserPostMessage hook to split the LoopCall pieces
+                       return ROUTE_LOCALONLY;
+               else
+                       return ROUTE_MESSAGE(parameters[0]);
+       }
+};
+
+class CommandSQuery : public SplitCommand
+{
+ public:
+       CommandSQuery(Module* Creator)
+               : SplitCommand(Creator, "SQUERY", 2, 2)
+       {
+               syntax = "<service> :<message>";
+       }
+
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               // The specified message was empty.
+               if (parameters[1].empty())
+               {
+                       user->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
+                       return CMD_FAILURE;
+               }
+
+               // The target can be either a nick or a nick@server mask.
+               User* target;
+               const char* targetserver = strchr(parameters[0].c_str(), '@');
+               if (targetserver)
+               {
+                       // The target is a user on a specific server (e.g. jto@tolsun.oulu.fi).
+                       target = ServerInstance->FindNickOnly(parameters[0].substr(0, targetserver - parameters[0].c_str()));
+                       if (target && strcasecmp(target->server->GetName().c_str(), targetserver + 1))
+                               target = NULL;
+               }
+               else
+               {
+                       // The targer can be on any server.
+                       target = ServerInstance->FindNickOnly(parameters[0]);
+               }
+
+               if (!target || target->registered != REG_ALL || !target->server->IsULine())
+               {
+                       // The target user does not exist, is not fully registered, or is not a service.
+                       user->WriteNumeric(ERR_NOSUCHSERVICE, parameters[0], "No such service");
+                       return CMD_FAILURE;
+               }
+
+               // Fire the pre-message events.
+               MessageTarget msgtarget(target);
+               MessageDetailsImpl msgdetails(MSG_PRIVMSG, parameters[1], parameters.GetTags());
+               if (!FirePreEvents(user, msgtarget, msgdetails))
+                       return CMD_FAILURE;
+
+               // The SQUERY command targets a service on a U-lined server. This can never
+               // be on the server local to the source so we don't need to do any routing
+               // logic and can forward it as a PRIVMSG.
+
+               // Fire the post-message event.
+               return FirePostEvent(user, msgtarget, msgdetails);
+       }
+};
+
+class ModuleCoreMessage : public Module
+{
+ private:
+       CommandMessage cmdprivmsg;
+       CommandMessage cmdnotice;
+       CommandSQuery cmdsquery;
+
+ public:
+       ModuleCoreMessage()
+               : cmdprivmsg(this, MSG_PRIVMSG)
+               , cmdnotice(this, MSG_NOTICE)
+               , cmdsquery(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the NOTICE, PRIVMSG, and SQUERY commands", VF_CORE|VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleCoreMessage)
diff --git a/src/coremods/core_mode.cpp b/src/coremods/core_mode.cpp
new file mode 100644 (file)
index 0000000..fa20a69
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * 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"
+
+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(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+CommandMode::CommandMode(Module* parent)
+       : Command(parent, "MODE", 1)
+       , seq(0)
+{
+       syntax = "<target> [[(+|-)]<modes> [<mode-parameters>]]";
+       memset(&sent, 0, sizeof(sent));
+}
+
+CmdResult CommandMode::Handle(User* user, const Params& parameters)
+{
+       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))
+       {
+               if (target[0] == '#')
+                       user->WriteNumeric(Numerics::NoSuchChannel(target));
+               else
+                       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.GetLastChangeList().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 Params& 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;
+}
+
+namespace
+{
+       void GetModeList(Numeric::Numeric& num, Channel* chan, User* user)
+       {
+               // We should only show the value of secret parameters (i.e. key) if
+               // the user is a member of the channel.
+               bool show_secret = chan->HasUser(user);
+
+               size_t modepos = num.push("+").GetParams().size() - 1;
+               std::string modes;
+               std::string param;
+               for (unsigned char chr = 65; chr < 123; ++chr)
+               {
+                       // Check that the mode exists and is set.
+                       ModeHandler* mh = ServerInstance->Modes->FindMode(chr, MODETYPE_CHANNEL);
+                       if (!mh || !chan->IsModeSet(mh))
+                               continue;
+
+                       // Add the mode to the set list.
+                       modes.push_back(mh->GetModeChar());
+
+                       // If the mode has a parameter we need to include that too.
+                       ParamModeBase* pm = mh->IsParameterMode();
+                       if (!pm)
+                               continue;
+
+                       // If a mode has a secret parameter and the user is not privy to
+                       // the value of it then we use <name> instead of the value.
+                       if (pm->IsParameterSecret() && !show_secret)
+                       {
+                               num.push("<" + pm->name + ">");
+                               continue;
+                       }
+
+                       // Retrieve the parameter and add it to the mode list.
+                       pm->GetParameter(chan, param);
+                       num.push(param);
+                       param.clear();
+               }
+               num.GetParams()[modepos].append(modes);
+       }
+}
+
+void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel)
+{
+       if (targetchannel)
+       {
+               // Display channel's current mode string
+               Numeric::Numeric modenum(RPL_CHANNELMODEIS);
+               modenum.push(targetchannel->name);
+               GetModeList(modenum, targetchannel, user);
+               user->WriteNumeric(modenum);
+               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");
+               }
+       }
+}
+
+class CoreModMode : public Module
+{
+ private:
+       CommandMode cmdmode;
+
+ public:
+       CoreModMode()
+               : cmdmode(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the MODE command", VF_VENDOR|VF_CORE);
+       }
+};
+
+MODULE_INIT(CoreModMode)
diff --git a/src/coremods/core_oper/cmd_die.cpp b/src/coremods/core_oper/cmd_die.cpp
new file mode 100644 (file)
index 0000000..48b18bb
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 "exitcodes.h"
+#include "core_oper.h"
+
+CommandDie::CommandDie(Module* parent, std::string& hashref)
+       : Command(parent, "DIE", 1, 1)
+       , hash(hashref)
+{
+       flags_needed = 'o';
+       syntax = "<servername>";
+}
+
+void DieRestart::SendError(const std::string& message)
+{
+       ClientProtocol::Messages::Error errormsg(message);
+       ClientProtocol::Event errorevent(ServerInstance->GetRFCEvents().error, errormsg);
+       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->Send(errorevent);
+               }
+       }
+}
+
+/** Handle /DIE
+ */
+CmdResult CommandDie::Handle(User* user, const Params& parameters)
+{
+       if (ServerInstance->PassCompare(user, password, parameters[0], hash))
+       {
+               {
+                       std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating.";
+                       ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, diebuf);
+                       DieRestart::SendError(diebuf);
+               }
+
+               ServerInstance->Exit(EXIT_STATUS_DIE);
+       }
+       else
+       {
+               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;
+       }
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_oper/cmd_kill.cpp b/src/coremods/core_oper/cmd_kill.cpp
new file mode 100644 (file)
index 0000000..01179d6
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_oper.h"
+
+CommandKill::CommandKill(Module* parent)
+       : Command(parent, "KILL", 2, 2)
+       , protoev(parent, name)
+{
+       flags_needed = 'o';
+       syntax = "<nick>[,<nick>]+ :<reason>";
+       TRANSLATE2(TR_CUSTOM, TR_CUSTOM);
+}
+
+class KillMessage : public ClientProtocol::Message
+{
+ public:
+       KillMessage(ClientProtocol::EventProvider& protoev, User* user, LocalUser* target, const std::string& text, const std::string& hidenick)
+               : ClientProtocol::Message("KILL", NULL)
+       {
+               if (hidenick.empty())
+                       SetSourceUser(user);
+               else
+                       SetSource(hidenick);
+
+               PushParamRef(target->nick);
+               PushParamRef(text);
+       }
+};
+
+/** Handle /KILL
+ */
+CmdResult CommandKill::Handle(User* user, const Params& parameters)
+{
+       /* Allow comma seperated lists of users for /KILL (thanks w00t) */
+       if (CommandParser::LoopCall(user, this, parameters, 0))
+       {
+               // If we got a colon delimited list of nicks then the handler ran for each nick,
+               // and KILL commands were broadcast for remote targets.
+               return CMD_FAILURE;
+       }
+
+       User* target = ServerInstance->FindNick(parameters[0]);
+       if (!target)
+       {
+               user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+               return CMD_FAILURE;
+       }
+
+       /*
+        * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
+        * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got.
+        *
+        * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill
+        * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
+        */
+
+       if (IS_LOCAL(user))
+       {
+               /*
+                * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user
+                * and the other half not. This would be a bad thing. ;p -- w00t
+                */
+               ModResult MOD_RESULT;
+               FIRST_MOD_RESULT(OnKill, MOD_RESULT, (user, target, parameters[1]));
+
+               if (MOD_RESULT == MOD_RES_DENY)
+                       return CMD_FAILURE;
+
+               killreason = "Killed (";
+               if (!hidenick.empty())
+               {
+                       // hidekills is on, use it
+                       killreason += hidenick;
+               }
+               else
+               {
+                       // hidekills is off, do nothing
+                       killreason += user->nick;
+               }
+
+               killreason += " (" + parameters[1] + "))";
+       }
+       else
+       {
+               /* Leave it alone, remote server has already formatted it */
+               killreason.assign(parameters[1], 0, ServerInstance->Config->Limits.MaxQuit);
+       }
+
+       if ((!hideuline) || (!user->server->IsULine()))
+       {
+               if (IS_LOCAL(user) && IS_LOCAL(target))
+                       ServerInstance->SNO->WriteGlobalSno('k', "Local kill by %s: %s (%s)", user->nick.c_str(), target->GetFullRealHost().c_str(), parameters[1].c_str());
+               else
+                       ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), target->GetFullRealHost().c_str(), parameters[1].c_str());
+       }
+
+       if (IS_LOCAL(target))
+       {
+               LocalUser* localu = IS_LOCAL(target);
+               KillMessage msg(protoev, user, localu, killreason, hidenick);
+               ClientProtocol::Event killevent(protoev, msg);
+               localu->Send(killevent);
+
+               this->lastuuid.clear();
+       }
+       else
+       {
+               this->lastuuid = target->uuid;
+       }
+
+       // send the quit out
+       ServerInstance->Users->QuitUser(target, killreason);
+
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandKill::GetRouting(User* user, const Params& parameters)
+{
+       // FindNick() doesn't work here because we quit the target user in Handle() which
+       // removes it from the nicklist, so we check lastuuid: if it's empty then this KILL
+       // was for a local user, otherwise it contains the uuid of the user who was killed.
+       if (lastuuid.empty())
+               return ROUTE_LOCALONLY;
+       return ROUTE_BROADCAST;
+}
+
+
+void CommandKill::EncodeParameter(std::string& param, unsigned int index)
+{
+       // Manually translate the nick -> uuid (see above), and also the reason (params[1])
+       // because we decorate it if the oper is local and want remote servers to see the
+       // decorated reason not the original.
+       param = ((index == 0) ? lastuuid : killreason);
+}
diff --git a/src/coremods/core_oper/cmd_oper.cpp b/src/coremods/core_oper/cmd_oper.cpp
new file mode 100644 (file)
index 0000000..8c3c86a
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_oper.h"
+
+CommandOper::CommandOper(Module* parent)
+       : SplitCommand(parent, "OPER", 2, 2)
+{
+       syntax = "<username> <password>";
+}
+
+CmdResult CommandOper::HandleLocal(LocalUser* user, const Params& parameters)
+{
+       bool match_login = false;
+       bool match_pass = false;
+       bool match_hosts = false;
+
+       const std::string userHost = user->ident + "@" + user->GetRealHost();
+       const std::string userIP = user->ident + "@" + user->GetIPString();
+
+       ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
+       if (i != ServerInstance->Config->oper_blocks.end())
+       {
+               OperInfo* ifo = i->second;
+               ConfigTag* tag = ifo->oper_block;
+               match_login = true;
+               match_pass = ServerInstance->PassCompare(user, tag->getString("password"), parameters[1], tag->getString("hash"));
+               match_hosts = InspIRCd::MatchMask(tag->getString("host"), userHost, userIP);
+
+               if (match_pass && match_hosts)
+               {
+                       /* found this oper's opertype */
+                       user->Oper(ifo);
+                       return CMD_SUCCESS;
+               }
+       }
+
+       std::string fields;
+       if (!match_login)
+               fields.append("login ");
+       if (!match_pass)
+               fields.append("password ");
+       if (!match_hosts)
+               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->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());
+       return CMD_FAILURE;
+}
diff --git a/src/coremods/core_oper/cmd_rehash.cpp b/src/coremods/core_oper/cmd_rehash.cpp
new file mode 100644 (file)
index 0000000..e234e54
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *   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/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_oper.h"
+
+CommandRehash::CommandRehash(Module* parent)
+       : Command(parent, "REHASH", 0)
+{
+       flags_needed = 'o';
+       Penalty = 2;
+       syntax = "[<servermask>]";
+}
+
+CmdResult CommandRehash::Handle(User* user, const Params& parameters)
+{
+       std::string param = parameters.size() ? parameters[0] : "";
+
+       FOREACH_MOD(OnPreRehash, (user, param));
+
+       if (param.empty())
+       {
+               // standard rehash of local server
+       }
+       else if (param.find_first_of("*.") != std::string::npos)
+       {
+               // rehash of servers by server name (with wildcard)
+               if (!InspIRCd::Match(ServerInstance->Config->ServerName, parameters[0]))
+               {
+                       // Doesn't match us. PreRehash is already done, nothing left to do
+                       return CMD_SUCCESS;
+               }
+       }
+       else
+       {
+               // parameterized rehash
+
+               // the leading "-" is optional; remove it if present.
+               if (param[0] == '-')
+                       param.erase(param.begin());
+
+               FOREACH_MOD(OnModuleRehash, (user, param));
+               return CMD_SUCCESS;
+       }
+
+       // Rehash for me. Try to start the rehash thread
+       if (!ServerInstance->ConfigThread)
+       {
+               std::string m = user->nick + " is rehashing config file " + FileSystem::GetFileName(ServerInstance->ConfigFileName) + " on " + ServerInstance->Config->ServerName;
+               ServerInstance->SNO->WriteGlobalSno('a', m);
+
+               if (IS_LOCAL(user))
+                       user->WriteNumeric(RPL_REHASHING, FileSystem::GetFileName(ServerInstance->ConfigFileName), "Rehashing");
+               else
+                       ServerInstance->PI->SendUserNotice(user, "*** Rehashing server " + FileSystem::GetFileName(ServerInstance->ConfigFileName));
+
+               /* Don't do anything with the logs here -- logs are restarted
+                * after the config thread has completed.
+                */
+               ServerInstance->Rehash(user->uuid);
+       }
+       else
+       {
+               /*
+                * A rehash is already in progress! ahh shit.
+                * XXX, todo: we should find some way to kill runaway rehashes that are blocking, this is a major problem for unrealircd users
+                */
+               if (IS_LOCAL(user))
+                       user->WriteNotice("*** Could not rehash: A rehash is already in progress.");
+               else
+                       ServerInstance->PI->SendUserNotice(user, "*** Could not rehash: A rehash is already in progress.");
+       }
+
+       // Always return success so spanningtree forwards an incoming REHASH even if we failed
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_oper/cmd_restart.cpp b/src/coremods/core_oper/cmd_restart.cpp
new file mode 100644 (file)
index 0000000..0ddea5c
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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_oper.h"
+
+CommandRestart::CommandRestart(Module* parent, std::string& hashref)
+       : Command(parent, "RESTART", 1, 1)
+       , hash(hashref)
+{
+       flags_needed = 'o';
+       syntax = "<servername>";
+}
+
+CmdResult CommandRestart::Handle(User* user, const Params& parameters)
+{
+       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Restart: %s", user->nick.c_str());
+       if (ServerInstance->PassCompare(user, password, parameters[0], hash))
+       {
+               ServerInstance->SNO->WriteGlobalSno('a', "RESTART command from %s, restarting server.", user->GetFullRealHost().c_str());
+
+               DieRestart::SendError("Server restarting.");
+
+#ifndef _WIN32
+               /* XXX: This hack sets FD_CLOEXEC on all possible file descriptors, so they're closed if the execvp() below succeeds.
+                * Certainly, this is not a nice way to do things and it's slow when the fd limit is high.
+                *
+                * A better solution would be to set the close-on-exec flag for each fd we create (or create them with O_CLOEXEC),
+                * however there is no guarantee that third party libs will do the same.
+                */
+               for (int i = getdtablesize(); --i > 2;)
+               {
+                       int flags = fcntl(i, F_GETFD);
+                       if (flags != -1)
+                               fcntl(i, F_SETFD, flags | FD_CLOEXEC);
+               }
+#endif
+
+               execvp(ServerInstance->Config->cmdline.argv[0], ServerInstance->Config->cmdline.argv);
+               ServerInstance->SNO->WriteGlobalSno('a', "Failed RESTART - could not execute '%s' (%s)",
+                       ServerInstance->Config->cmdline.argv[0], strerror(errno));
+       }
+       else
+       {
+               ServerInstance->SNO->WriteGlobalSno('a', "Failed RESTART command from %s.", user->GetFullRealHost().c_str());
+       }
+       return CMD_FAILURE;
+}
diff --git a/src/coremods/core_oper/core_oper.cpp b/src/coremods/core_oper/core_oper.cpp
new file mode 100644 (file)
index 0000000..eaa1a04
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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 "core_oper.h"
+
+class CoreModOper : public Module
+{
+       std::string powerhash;
+
+       CommandDie cmddie;
+       CommandKill cmdkill;
+       CommandOper cmdoper;
+       CommandRehash cmdrehash;
+       CommandRestart cmdrestart;
+
+ public:
+       CoreModOper()
+               : cmddie(this, powerhash)
+               , cmdkill(this)
+               , cmdoper(this)
+               , cmdrehash(this)
+               , cmdrestart(this, powerhash)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("power");
+
+               // The hash method for *BOTH* the die and restart passwords
+               powerhash = tag->getString("hash");
+
+               cmddie.password = tag->getString("diepass", ServerInstance->Config->ServerName, 1);
+               cmdrestart.password = tag->getString("restartpass", ServerInstance->Config->ServerName, 1);
+
+               ConfigTag* security = ServerInstance->Config->ConfValue("security");
+               cmdkill.hidenick = security->getString("hidekills");
+               cmdkill.hideuline = security->getBool("hideulinekills");
+       }
+
+       void OnPostOper(User* user, const std::string&, const std::string&) CXX11_OVERRIDE
+       {
+               LocalUser* luser = IS_LOCAL(user);
+               if (!luser)
+                       return;
+
+               const std::string vhost = luser->oper->getConfig("vhost");
+               if (!vhost.empty())
+                       luser->ChangeDisplayedHost(vhost);
+
+               const std::string klass = luser->oper->getConfig("class");
+               if (!klass.empty())
+                       luser->SetClass(klass);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the DIE, KILL, OPER, REHASH, and RESTART commands", VF_VENDOR | VF_CORE);
+       }
+};
+
+MODULE_INIT(CoreModOper)
diff --git a/src/coremods/core_oper/core_oper.h b/src/coremods/core_oper/core_oper.h
new file mode 100644 (file)
index 0000000..7589b86
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * 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 "inspircd.h"
+
+namespace DieRestart
+{
+       /** 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.
+ */
+class CommandDie : public Command
+{
+ public:
+       std::string& hash;
+       std::string password;
+
+       /** Constructor for die.
+        */
+       CommandDie(Module* parent, std::string& hashref);
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /KILL.
+ */
+class CommandKill : public Command
+{
+       std::string lastuuid;
+       std::string killreason;
+       ClientProtocol::EventProvider protoev;
+
+ public:
+       /** Set to a non empty string to obfuscate nicknames prepended to a KILL. */
+       std::string hidenick;
+
+       /** Set to hide kills from clients of ulined servers in snotices. */
+       bool hideuline;
+
+       /** Constructor for kill.
+        */
+       CommandKill(Module* parent);
+
+       /** Handle command.
+        * @param user User issuing the command
+        * @param parameters Parameters to the command
+        * @return A value from CmdResult to indicate command success or failure.
+        */
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+       void EncodeParameter(std::string& param, unsigned int index) CXX11_OVERRIDE;
+};
+
+/** Handle /OPER.
+ */
+class CommandOper : public SplitCommand
+{
+ public:
+       /** Constructor for oper.
+        */
+       CommandOper(Module* parent);
+
+       /** Handle command.
+        * @param user User issuing the command
+        * @param parameters Parameters to the command
+        * @return A value from CmdResult to indicate command success or failure.
+        */
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /REHASH.
+ */
+class CommandRehash : public Command
+{
+ public:
+       /** Constructor for rehash.
+        */
+       CommandRehash(Module* parent);
+
+       /** Handle command.
+        * @param user User issuing the command
+        * @param parameters Parameters to the command
+        * @return A value from CmdResult to indicate command success or failure.
+        */
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /RESTART
+ */
+class CommandRestart : public Command
+{
+ public:
+       std::string& hash;
+       std::string password;
+
+       /** Constructor for restart.
+        */
+       CommandRestart(Module* parent, std::string& hashref);
+
+       /** Handle command.
+        * @param user User issuing the command
+        * @param parameters Parameters to the command
+        * @return A value from CmdResult to indicate command success or failure.
+        */
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
diff --git a/src/coremods/core_reloadmodule.cpp b/src/coremods/core_reloadmodule.cpp
new file mode 100644 (file)
index 0000000..ea5d40a
--- /dev/null
@@ -0,0 +1,785 @@
+/*
+ * 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>
+ *
+ * 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 "listmode.h"
+#include "modules/reload.h"
+
+static Events::ModuleEventProvider* reloadevprov;
+static ClientProtocol::Serializer* dummyserializer;
+
+class DummySerializer : public ClientProtocol::Serializer
+{
+       bool Parse(LocalUser* user, const std::string& line, ClientProtocol::ParseOutput& parseoutput) CXX11_OVERRIDE
+       {
+               return false;
+       }
+
+       ClientProtocol::SerializedMessage Serialize(const ClientProtocol::Message& msg, const ClientProtocol::TagSelection& tagwl) const CXX11_OVERRIDE
+       {
+               return ClientProtocol::SerializedMessage();
+       }
+
+ public:
+       DummySerializer(Module* mod)
+               : ClientProtocol::Serializer(mod, "dummy")
+       {
+       }
+};
+
+class CommandReloadmodule : public Command
+{
+       Events::ModuleEventProvider evprov;
+       DummySerializer dummyser;
+
+ public:
+       /** Constructor for reloadmodule.
+        */
+       CommandReloadmodule(Module* parent)
+               : Command(parent, "RELOADMODULE", 1)
+               , evprov(parent, "event/reloadmodule")
+               , dummyser(parent)
+       {
+               reloadevprov = &evprov;
+               dummyserializer = &dummyser;
+               flags_needed = 'o';
+               syntax = "<modulename>";
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+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;
+                       ClientProtocol::Serializer* serializer;
+               };
+
+               ProviderInfo(ModeHandler* mode)
+                       : itemname(mode->name)
+                       , mh(mode)
+               {
+               }
+
+               ProviderInfo(ExtensionItem* ei)
+                       : itemname(ei->name)
+                       , extitem(ei)
+               {
+               }
+
+               ProviderInfo(ClientProtocol::Serializer* ser)
+                       : itemname(ser->name)
+                       , serializer(ser)
+               {
+               }
+       };
+
+       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
+       struct UserData : public OwnedModesExts
+       {
+               static const size_t UNUSED_INDEX = (size_t)-1;
+               size_t serializerindex;
+
+               UserData(User* user, size_t serializeridx)
+                       : OwnedModesExts(user->uuid)
+                       , serializerindex(serializeridx)
+               {
+               }
+       };
+
+       /** 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 serializers provided by the module
+        */
+       std::vector<ProviderInfo> handledserializers;
+
+       /** 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);
+       size_t SaveSerializer(User* user);
+
+       /** Get the index of a ProviderInfo representing the serializer in the handledserializers list.
+        * If the serializer is not already in the list it is added.
+        * @param serializer Serializer to get an index to.
+        * @return Index of the ProviderInfo representing the serializer.
+        */
+       size_t GetSerializerIndex(ClientProtocol::Serializer* serializer);
+
+       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);
+
+       /** Link previously saved serializer names to currently available Serializers
+        */
+       void LinkSerializers();
+
+       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 previously saved serializer on a User.
+        * Quit the user if the serializer cannot be restored.
+        * @param serializerindex Saved serializer index to restore.
+        * @param user User whose serializer to restore. If not local then calling this method is a no-op.
+        * @return True if the serializer didn't need restoring or was restored successfully.
+        * False if the serializer should have been restored but the required serializer is unavailable and the user was quit.
+        */
+       bool RestoreSerializer(size_t serializerindex, User* user);
+
+       /** 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);
+
+               // Save serializer name if applicable and get an index to it
+               size_t serializerindex = SaveSerializer(user);
+
+               // 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()) || (serializerindex != UserData::UNUSED_INDEX))
+               {
+                       userdatalist.push_back(UserData(user, serializerindex));
+                       userdatalist.back().swap(currdata);
+               }
+       }
+}
+
+size_t DataKeeper::GetSerializerIndex(ClientProtocol::Serializer* serializer)
+{
+       for (size_t i = 0; i < handledserializers.size(); i++)
+       {
+               if (handledserializers[i].serializer == serializer)
+                       return i;
+       }
+
+       handledserializers.push_back(ProviderInfo(serializer));
+       return handledserializers.size()-1;
+}
+
+size_t DataKeeper::SaveSerializer(User* user)
+{
+       LocalUser* const localuser = IS_LOCAL(user);
+       if ((!localuser) || (!localuser->serializer))
+               return UserData::UNUSED_INDEX;
+       if (localuser->serializer->creator != mod)
+               return UserData::UNUSED_INDEX;
+
+       const size_t serializerindex = GetSerializerIndex(localuser->serializer);
+       localuser->serializer = dummyserializer;
+       return serializerindex;
+}
+
+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::LinkSerializers()
+{
+       for (std::vector<ProviderInfo>::iterator i = handledserializers.begin(); i != handledserializers.end(); ++i)
+       {
+               ProviderInfo& item = *i;
+               item.serializer = ServerInstance->Modules.FindDataService<ClientProtocol::Serializer>(item.itemname);
+               VerifyServiceProvider(item.serializer, "Serializer");
+       }
+}
+
+void DataKeeper::Restore(Module* newmod)
+{
+       this->mod = newmod;
+
+       // Find the new extension items
+       LinkExtensions();
+       LinkModes(MODETYPE_USER);
+       LinkModes(MODETYPE_CHANNEL);
+       LinkSerializers();
+
+       // 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);
+       }
+}
+
+bool DataKeeper::RestoreSerializer(size_t serializerindex, User* user)
+{
+       if (serializerindex == UserData::UNUSED_INDEX)
+               return true;
+
+       // The following checks are redundant
+       LocalUser* const localuser = IS_LOCAL(user);
+       if (!localuser)
+               return true;
+       if (localuser->serializer != dummyserializer)
+               return true;
+
+       const ProviderInfo& provinfo = handledserializers[serializerindex];
+       if (!provinfo.serializer)
+       {
+               // Users cannot exist without a serializer
+               ServerInstance->Users.QuitUser(user, "Serializer lost in reload");
+               return false;
+       }
+
+       localuser->serializer = provinfo.serializer;
+       return true;
+}
+
+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;
+               }
+
+               // Attempt to restore serializer first, if it fails it's a fatal error and RestoreSerializer() quits them
+               if (!RestoreSerializer(userdata.serializerindex, user))
+                       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 ActionBase
+{
+       Module* const mod;
+       const std::string uuid;
+       const std::string passedname;
+
+ public:
+       ReloadAction(Module* m, const std::string& uid, const std::string& passedmodname)
+               : mod(m)
+               , uuid(uid)
+               , passedname(passedmodname)
+       {
+       }
+
+       void Call() CXX11_OVERRIDE
+       {
+               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, passedname, InspIRCd::Format("Module %ssuccessfully reloaded.", (result ? "" : "un")));
+
+               ServerInstance->GlobalCulls.AddItem(this);
+       }
+};
+
+CmdResult CommandReloadmodule::Handle(User* user, const Params& parameters)
+{
+       Module* m = ServerInstance->Modules->Find(parameters[0]);
+       if (m == creator)
+       {
+               user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "You cannot reload core_reloadmodule (unload and load it)");
+               return CMD_FAILURE;
+       }
+
+       if (creator->dying)
+               return CMD_FAILURE;
+
+       if ((m) && (ServerInstance->Modules.CanUnload(m)))
+       {
+               ServerInstance->AtomicActions.AddAction(new ReloadAction(m, user->uuid, parameters[0]));
+               return CMD_SUCCESS;
+       }
+       else
+       {
+               user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Could not find module by that name");
+               return CMD_FAILURE;
+       }
+}
+
+class CoreModReloadmodule : public Module
+{
+ private:
+       CommandReloadmodule cmd;
+
+ public:
+       CoreModReloadmodule()
+               : cmd(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the RELOADMODULE command", VF_CORE | VF_VENDOR);
+       }
+};
+
+MODULE_INIT(CoreModReloadmodule)
diff --git a/src/coremods/core_serialize_rfc.cpp b/src/coremods/core_serialize_rfc.cpp
new file mode 100644 (file)
index 0000000..b8d075a
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * 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"
+
+enum
+{
+       // From ircu.
+       ERR_INPUTTOOLONG = 417
+};
+
+class RFCSerializer : public ClientProtocol::Serializer
+{
+
+       /** The maximum size of client-originated message tags in an incoming message including the `@`. */
+       static const std::string::size_type MAX_CLIENT_MESSAGE_TAG_LENGTH = 4095;
+
+       /** The maximum size of server-originated message tags in an outgoing message including the `@`. */
+       static const std::string::size_type MAX_SERVER_MESSAGE_TAG_LENGTH = 4095;
+
+       static void SerializeTags(const ClientProtocol::TagMap& tags, const ClientProtocol::TagSelection& tagwl, std::string& line);
+
+ public:
+       RFCSerializer(Module* mod)
+               : ClientProtocol::Serializer(mod, "rfc")
+       {
+       }
+
+       bool Parse(LocalUser* user, const std::string& line, ClientProtocol::ParseOutput& parseoutput) CXX11_OVERRIDE;
+       ClientProtocol::SerializedMessage Serialize(const ClientProtocol::Message& msg, const ClientProtocol::TagSelection& tagwl) const CXX11_OVERRIDE;
+};
+
+bool RFCSerializer::Parse(LocalUser* user, const std::string& line, ClientProtocol::ParseOutput& parseoutput)
+{
+       size_t start = line.find_first_not_of(" ");
+       if (start == std::string::npos)
+       {
+               // Discourage the user from flooding the server.
+               user->CommandFloodPenalty += 2000;
+               return false;
+       }
+
+       // Work out how long the message can actually be.
+       size_t maxline = ServerInstance->Config->Limits.MaxLine - start - 2;
+       if (line[start] == '@')
+               maxline += MAX_CLIENT_MESSAGE_TAG_LENGTH + 1;
+
+       irc::tokenstream tokens(line, start, maxline);
+       ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I %s", user->uuid.c_str(), tokens.GetMessage().c_str());
+
+       // This will always exist because of the check at the start of the function.
+       std::string token;
+       tokens.GetMiddle(token);
+       if (token[0] == '@')
+       {
+               // Check that the client tags fit within the client tag space.
+               if (token.length() > MAX_CLIENT_MESSAGE_TAG_LENGTH)
+               {
+                       user->WriteNumeric(ERR_INPUTTOOLONG, "Input line was too long");
+                       user->CommandFloodPenalty += 2000;
+                       return false;
+               }
+
+               // Truncate the RFC part of the message if it is too long.
+               size_t maxrfcline = token.length() + ServerInstance->Config->Limits.MaxLine - 1;
+               if (tokens.GetMessage().length() > maxrfcline)
+                       tokens.GetMessage().erase(maxrfcline);
+
+               // Line begins with message tags, parse them.
+               std::string tagval;
+               irc::sepstream ss(token.substr(1), ';');
+               while (ss.GetToken(token))
+               {
+                       // Two or more tags with the same key must not be sent, but if a client violates that we accept
+                       // the first occurence of duplicate tags and ignore all later occurences.
+                       //
+                       // Another option is to reject the message entirely but there is no standard way of doing that.
+                       const std::string::size_type p = token.find('=');
+                       if (p != std::string::npos)
+                       {
+                               // Tag has a value
+                               tagval.assign(token, p+1, std::string::npos);
+                               token.erase(p);
+                       }
+                       else
+                               tagval.clear();
+
+                       HandleTag(user, token, tagval, parseoutput.tags);
+               }
+
+               // Try to read the prefix or command name.
+               if (!tokens.GetMiddle(token))
+               {
+                       // Discourage the user from flooding the server.
+                       user->CommandFloodPenalty += 2000;
+                       return false;
+               }
+       }
+
+       if (token[0] == ':')
+       {
+               // If this exists then the client sent a prefix as part of their
+               // message. Section 2.3 of RFC 1459 technically says we should only
+               // allow the nick of the client here but in practise everyone just
+               // ignores it so we will copy them.
+
+               // Try to read the command name.
+               if (!tokens.GetMiddle(token))
+               {
+                       // Discourage the user from flooding the server.
+                       user->CommandFloodPenalty += 2000;
+                       return false;
+               }
+       }
+
+       parseoutput.cmd.assign(token);
+
+       // Build the parameter map. We intentionally do not respect the RFC 1459
+       // thirteen parameter limit here.
+       while (tokens.GetTrailing(token))
+               parseoutput.params.push_back(token);
+
+       return true;
+}
+
+namespace
+{
+       void CheckTagLength(std::string& line, size_t prevsize, size_t& length, size_t maxlength)
+       {
+               const std::string::size_type diffsize = line.size() - prevsize;
+               if (length + diffsize > maxlength)
+                       line.erase(prevsize);
+               else
+                       length += diffsize;
+       }
+}
+
+void RFCSerializer::SerializeTags(const ClientProtocol::TagMap& tags, const ClientProtocol::TagSelection& tagwl, std::string& line)
+{
+       size_t client_tag_length = 0;
+       size_t server_tag_length = 0;
+       for (ClientProtocol::TagMap::const_iterator i = tags.begin(); i != tags.end(); ++i)
+       {
+               if (!tagwl.IsSelected(tags, i))
+                       continue;
+
+               const std::string::size_type prevsize = line.size();
+               line.push_back(prevsize ? ';' : '@');
+               line.append(i->first);
+               const std::string& val = i->second.value;
+               if (!val.empty())
+               {
+                       line.push_back('=');
+                       line.append(val);
+               }
+
+               // The tags part of the message must not contain more client and server tags than allowed by the
+               // message tags specification. This is complicated by the tag space having separate limits for
+               // both server-originated and client-originated tags. If either of the tag limits is exceeded then
+               // the most recently added tag is removed.
+               if (i->first[0] == '+')
+                       CheckTagLength(line, prevsize, client_tag_length, MAX_CLIENT_MESSAGE_TAG_LENGTH);
+               else
+                       CheckTagLength(line, prevsize, server_tag_length, MAX_SERVER_MESSAGE_TAG_LENGTH);
+       }
+
+       if (!line.empty())
+               line.push_back(' ');
+}
+
+ClientProtocol::SerializedMessage RFCSerializer::Serialize(const ClientProtocol::Message& msg, const ClientProtocol::TagSelection& tagwl) const
+{
+       std::string line;
+       SerializeTags(msg.GetTags(), tagwl, line);
+
+       // Save position for length calculation later
+       const std::string::size_type rfcmsg_begin = line.size();
+
+       if (msg.GetSource())
+       {
+               line.push_back(':');
+               line.append(*msg.GetSource());
+               line.push_back(' ');
+       }
+       line.append(msg.GetCommand());
+
+       const ClientProtocol::Message::ParamList& params = msg.GetParams();
+       if (!params.empty())
+       {
+               for (ClientProtocol::Message::ParamList::const_iterator i = params.begin(); i != params.end()-1; ++i)
+               {
+                       const std::string& param = *i;
+                       line.push_back(' ');
+                       line.append(param);
+               }
+
+               line.append(" :", 2).append(params.back());
+       }
+
+       // Truncate if too long
+       std::string::size_type maxline = ServerInstance->Config->Limits.MaxLine - 2;
+       if (line.length() - rfcmsg_begin > maxline)
+               line.erase(rfcmsg_begin + maxline);
+
+       line.append("\r\n", 2);
+       return line;
+}
+
+class ModuleCoreRFCSerializer : public Module
+{
+       RFCSerializer rfcserializer;
+
+ public:
+       ModuleCoreRFCSerializer()
+               : rfcserializer(this)
+       {
+       }
+
+       void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+       {
+               if (type != ExtensionItem::EXT_USER)
+                       return;
+
+               LocalUser* const user = IS_LOCAL(static_cast<User*>(item));
+               if ((user) && (user->serializer == &rfcserializer))
+                       ServerInstance->Users.QuitUser(user, "Protocol serializer module unloading");
+       }
+
+       void OnUserInit(LocalUser* user) CXX11_OVERRIDE
+       {
+               if (!user->serializer)
+                       user->serializer = &rfcserializer;
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("RFC client protocol serializer and unserializer", VF_CORE|VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleCoreRFCSerializer)
diff --git a/src/coremods/core_stats.cpp b/src/coremods/core_stats.cpp
new file mode 100644 (file)
index 0000000..918d59a
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 "xline.h"
+#include "modules/stats.h"
+
+#ifdef _WIN32
+#include <psapi.h>
+#pragma comment(lib, "psapi.lib") // For GetProcessMemoryInfo()
+#endif
+
+/** Handle /STATS.
+ */
+class CommandStats : public Command
+{
+       Events::ModuleEventProvider statsevprov;
+       void DoStats(Stats::Context& stats);
+
+ public:
+       /** STATS characters which non-opers can request. */
+       std::string userstats;
+
+       CommandStats(Module* Creator)
+               : Command(Creator, "STATS", 1, 2)
+               , statsevprov(Creator, "event/stats")
+       {
+               allow_empty_last_param = false;
+               syntax = "<symbol> [<servername>]";
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               if ((parameters.size() > 1) && (parameters[1].find('.') != std::string::npos))
+                       return ROUTE_UNICAST(parameters[1]);
+               return ROUTE_LOCALONLY;
+       }
+};
+
+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->GetDisplayedHost() : 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 = userstats.find(statschar) != std::string::npos;
+       bool isRemoteOper = IS_REMOTE(user) && (user->IsOper());
+       bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
+
+       if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
+       {
+               ServerInstance->SNO->WriteToSnoMask('t',
+                               "%s '%c' denied for %s (%s@%s)",
+                               (IS_LOCAL(user) ? "Stats" : "Remote stats"),
+                               statschar, user->nick.c_str(), user->ident.c_str(), user->GetRealHost().c_str());
+               stats.AddRow(481, (std::string("Permission Denied - STATS ") + statschar + " requires the servers/auspex priv."));
+               return;
+       }
+
+       ModResult MOD_RESULT;
+       FIRST_MOD_RESULT_CUSTOM(statsevprov, Stats::EventListener, OnStats, MOD_RESULT, (stats));
+       if (MOD_RESULT == MOD_RES_DENY)
+       {
+               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->GetRealHost().c_str());
+               return;
+       }
+
+       switch (statschar)
+       {
+               /* stats p (show listening ports) */
+               case 'p':
+               {
+                       for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
+                       {
+                               ListenSocket* ls = *i;
+                               std::string type = ls->bind_tag->getString("type", "clients");
+                               std::string hook = ls->bind_tag->getString("ssl", "plaintext");
+
+                               stats.AddRow(249, ls->bind_sa.str() + " (" + type + ", " + hook + ")");
+                       }
+               }
+               break;
+
+               /* These stats symbols must be handled by a linking module */
+               case 'n':
+               case 'c':
+               break;
+
+               case 'i':
+               {
+                       for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
+                       {
+                               ConnectClass* c = *i;
+                               Stats::Row row(215);
+                               row.push("I").push(c->name);
+
+                               std::string param;
+                               if (c->type == CC_ALLOW)
+                                       param.push_back('+');
+                               if (c->type == CC_DENY)
+                                       param.push_back('-');
+
+                               if (c->type == CC_NAMED)
+                                       param.push_back('*');
+                               else
+                                       param.append(c->host);
+
+                               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()));
+
+                               param = ConvToStr(c->GetPenaltyThreshold());
+                               if (c->fakelag)
+                                       param.push_back('*');
+                               row.push(param);
+
+                               stats.AddRow(row);
+                       }
+               }
+               break;
+
+               case 'Y':
+               {
+                       int idx = 0;
+                       for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+                       {
+                               ConnectClass* c = *i;
+                               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++;
+                       }
+               }
+               break;
+
+               case 'P':
+               {
+                       unsigned int idx = 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())
+                               {
+                                       LocalUser* lu = IS_LOCAL(oper);
+                                       stats.AddRow(249, oper->nick + " (" + oper->ident + "@" + oper->GetDisplayedHost() + ") Idle: " +
+                                                       (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
+                                       idx++;
+                               }
+                       }
+                       stats.AddRow(249, ConvToStr(idx)+" OPER(s)");
+               }
+               break;
+
+               case 'k':
+                       ServerInstance->XLines->InvokeStats("K",216,stats);
+               break;
+               case 'g':
+                       ServerInstance->XLines->InvokeStats("G",223,stats);
+               break;
+               case 'q':
+                       ServerInstance->XLines->InvokeStats("Q",217,stats);
+               break;
+               case 'Z':
+                       ServerInstance->XLines->InvokeStats("Z",223,stats);
+               break;
+               case 'e':
+                       ServerInstance->XLines->InvokeStats("E",223,stats);
+               break;
+               case 'E':
+               {
+                       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':
+               {
+                       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 */
+                                       stats.AddRow(212, i->second->name, i->second->use_count);
+                               }
+                       }
+               }
+               break;
+
+               /* stats z (debug and memory info) */
+               case 'z':
+               {
+                       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;
+                       SocketEngine::GetStats().GetBandwidth(kbitpersec_in, kbitpersec_out, kbitpersec_total);
+
+                       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.
+                        * Also cuts out some identical code in both branches of the ifndef. -- Om
+                        */
+                       rusage R;
+
+                       /* 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 */
+                       {
+#ifndef __HAIKU__
+                               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));
+#endif
+                               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;
+
+                               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;
+
+                               stats.AddRow(249, InspIRCd::Format("CPU Use (total):  %03.5f%%", per));
+                       }
+#else
+                       PROCESS_MEMORY_COUNTERS MemCounters;
+                       if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
+                       {
+                               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;
+                       FILETIME ExitTime;
+                       FILETIME KernelTime;
+                       FILETIME UserTime;
+                       LARGE_INTEGER ThisSample;
+                       if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) &&
+                               QueryPerformanceCounter(&ThisSample))
+                       {
+                               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 per = (n_eaten/n_elapsed);
+
+                               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);
+
+                               stats.AddRow(249, InspIRCd::Format("CPU Use (total):  %03.5f%%", per));
+                       }
+#endif
+               }
+               break;
+
+               case 'T':
+               {
+                       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;
+
+               /* stats o */
+               case 'o':
+               {
+                       for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); ++i)
+                       {
+                               OperInfo* ifo = i->second;
+                               ConfigTag* tag = ifo->oper_block;
+                               stats.AddRow(243, 'O', tag->getString("host"), '*', tag->getString("name"), tag->getString("type"), '0');
+                       }
+               }
+               break;
+               case 'O':
+               {
+                       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++)
+                               {
+                                       ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
+                                       if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A'])
+                                               umodes.push_back(c);
+                                       mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
+                                       if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
+                                               cmodes.push_back(c);
+                               }
+                               stats.AddRow(243, 'O', tag->name, umodes, cmodes);
+                       }
+               }
+               break;
+
+               /* stats l (show user I/O stats) */
+               case 'l':
+               /* stats L (show user I/O stats with IP addresses) */
+               case 'L':
+                       GenerateStatsLl(stats);
+               break;
+
+               /* stats u (show server uptime) */
+               case 'u':
+               {
+                       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;
+
+               default:
+               break;
+       }
+
+       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->GetRealHost().c_str());
+       return;
+}
+
+CmdResult CommandStats::Handle(User* user, const Params& parameters)
+{
+       if (parameters.size() > 1 && !irc::equals(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;
+       }
+       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;
+}
+
+class CoreModStats : public Module
+{
+ private:
+       CommandStats cmd;
+
+ public:
+       CoreModStats()
+               : cmd(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* security = ServerInstance->Config->ConfValue("security");
+               cmd.userstats = security->getString("userstats");
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the STATS command", VF_CORE | VF_VENDOR);
+       }
+};
+
+MODULE_INIT(CoreModStats)
diff --git a/src/coremods/core_stub.cpp b/src/coremods/core_stub.cpp
new file mode 100644 (file)
index 0000000..06e033f
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+enum
+{
+       // From RFC 1459.
+       ERR_SUMMONDISABLED = 445,
+       ERR_USERSDISABLED = 446
+};
+
+
+/** Handle /CONNECT.
+ */
+class CommandConnect : public Command
+{
+ public:
+       /** Constructor for connect.
+        */
+       CommandConnect(Module* parent)
+               : Command(parent, "CONNECT", 1)
+       {
+               flags_needed = 'o';
+               syntax = "<servermask>";
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               /*
+                * This is handled by the server linking module, if necessary. Do not remove this stub.
+                */
+               user->WriteNotice("Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.");
+               return CMD_SUCCESS;
+       }
+};
+
+/** Handle /LINKS.
+ */
+class CommandLinks : public Command
+{
+ public:
+       /** Constructor for links.
+        */
+       CommandLinks(Module* parent)
+               : Command(parent, "LINKS", 0, 0)
+       {
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               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;
+       }
+};
+
+/** Handle /SERVER.
+ */
+class CommandServer : public Command
+{
+ public:
+       /** Constructor for server.
+        */
+       CommandServer(Module* parent)
+               : Command(parent, "SERVER")
+       {
+               works_before_reg = true;
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               if (user->registered == REG_ALL)
+               {
+                       user->WriteNumeric(ERR_ALREADYREGISTERED, "You are already registered. (Perhaps your IRC client does not have a /SERVER command).");
+               }
+               else
+               {
+                       user->WriteNumeric(ERR_NOTREGISTERED, "SERVER", "You may not register as a server (servers have separate ports from clients, change your config)");
+               }
+               return CMD_FAILURE;
+       }
+};
+
+/** Handle /SQUIT.
+ */
+class CommandSquit : public Command
+{
+ public:
+       /** Constructor for squit.
+        */
+       CommandSquit(Module* parent)
+               : Command(parent, "SQUIT", 1, 2)
+       {
+               flags_needed = 'o';
+               syntax = "<servermask>";
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               user->WriteNotice("Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.");
+               return CMD_FAILURE;
+       }
+};
+
+class CommandSummon
+       : public SplitCommand
+{
+ public:
+       CommandSummon(Module* Creator)
+               : SplitCommand(Creator, "SUMMON", 1)
+       {
+       }
+
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               user->WriteNumeric(ERR_SUMMONDISABLED, "SUMMON has been disabled");
+               return CMD_SUCCESS;
+       }
+};
+
+class CommandUsers
+       : public SplitCommand
+{
+ public:
+       CommandUsers(Module* Creator)
+               : SplitCommand(Creator, "USERS")
+       {
+       }
+
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               user->WriteNumeric(ERR_USERSDISABLED, "USERS has been disabled");
+               return CMD_SUCCESS;
+       }
+};
+
+class CoreModStub : public Module
+{
+       CommandConnect cmdconnect;
+       CommandLinks cmdlinks;
+       CommandServer cmdserver;
+       CommandSquit cmdsquit;
+       CommandSummon cmdsummon;
+       CommandUsers cmdusers;
+
+ public:
+       CoreModStub()
+               : cmdconnect(this)
+               , cmdlinks(this)
+               , cmdserver(this)
+               , cmdsquit(this)
+               , cmdsummon(this)
+               , cmdusers(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides stubs for unimplemented commands", VF_VENDOR|VF_CORE);
+       }
+};
+
+MODULE_INIT(CoreModStub)
diff --git a/src/coremods/core_user/cmd_away.cpp b/src/coremods/core_user/cmd_away.cpp
new file mode 100644 (file)
index 0000000..834e871
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+enum
+{
+       // From RFC 1459.
+       RPL_UNAWAY = 305,
+       RPL_NOWAWAY = 306
+};
+
+CommandAway::CommandAway(Module* parent)
+       : Command(parent, "AWAY", 0, 1)
+       , awayevprov(parent)
+{
+       allow_empty_last_param = false;
+       syntax = "[:<message>]";
+}
+
+/** Handle /AWAY
+ */
+CmdResult CommandAway::Handle(User* user, const Params& parameters)
+{
+       LocalUser* luser = IS_LOCAL(user);
+       ModResult MOD_RESULT;
+
+       if (!parameters.empty())
+       {
+               std::string message(parameters[0]);
+               if (luser)
+               {
+                       FIRST_MOD_RESULT_CUSTOM(awayevprov, Away::EventListener, OnUserPreAway, MOD_RESULT, (luser, message));
+                       if (MOD_RESULT == MOD_RES_DENY)
+                               return CMD_FAILURE;
+               }
+
+               user->awaytime = ServerInstance->Time();
+               user->awaymsg.assign(message, 0, ServerInstance->Config->Limits.MaxAway);
+               user->WriteNumeric(RPL_NOWAWAY, "You have been marked as being away");
+               FOREACH_MOD_CUSTOM(awayevprov, Away::EventListener, OnUserAway, (user));
+       }
+       else
+       {
+               if (luser)
+               {
+                       FIRST_MOD_RESULT_CUSTOM(awayevprov, Away::EventListener, OnUserPreBack, MOD_RESULT, (luser));
+                       if (MOD_RESULT == MOD_RES_DENY)
+                               return CMD_FAILURE;
+               }
+
+               user->awaytime = 0;
+               user->awaymsg.clear();
+               user->WriteNumeric(RPL_UNAWAY, "You are no longer marked as being away");
+               FOREACH_MOD_CUSTOM(awayevprov, Away::EventListener, OnUserBack, (user));
+       }
+
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandAway::GetRouting(User* user, const Params& parameters)
+{
+       return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_user/cmd_ison.cpp b/src/coremods/core_user/cmd_ison.cpp
new file mode 100644 (file)
index 0000000..a316b3c
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+class IsonReplyBuilder : public Numeric::Builder<' ', true>
+{
+ public:
+       IsonReplyBuilder(LocalUser* user)
+               : Numeric::Builder<' ', true>(user, RPL_ISON)
+       {
+       }
+
+       void AddNick(const std::string& nickname)
+       {
+               User* const user = ServerInstance->FindNickOnly(nickname);
+               if ((user) && (user->registered == REG_ALL))
+                       Add(user->nick);
+       }
+};
+
+/** Handle /ISON
+ */
+CmdResult CommandIson::HandleLocal(LocalUser* user, const Params& parameters)
+{
+       IsonReplyBuilder reply(user);
+
+       for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end()-1; ++i)
+       {
+               const std::string& targetstr = *i;
+               reply.AddNick(targetstr);
+       }
+
+       // 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_user/cmd_nick.cpp b/src/coremods/core_user/cmd_nick.cpp
new file mode 100644 (file)
index 0000000..670931f
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+CommandNick::CommandNick(Module* parent)
+       : SplitCommand(parent, "NICK", 1, 1)
+{
+       works_before_reg = true;
+       syntax = "<newnick>";
+       Penalty = 0;
+}
+
+/** Handle nick changes from users.
+ * NOTE: If you are used to ircds based on ircd2.8, and are looking
+ * for the client introduction code in here, youre in the wrong place.
+ * You need to look in the spanningtree module for this!
+ */
+CmdResult CommandNick::HandleLocal(LocalUser* user, const Params& parameters)
+{
+       std::string oldnick = user->nick;
+       std::string newnick = parameters[0];
+
+       // anything except the initial NICK gets a flood penalty
+       if (user->registered == REG_ALL)
+               user->CommandFloodPenalty += 4000;
+
+       if (newnick.empty())
+       {
+               user->WriteNumeric(ERR_NONICKNAMEGIVEN, "No nickname given");
+               return CMD_FAILURE;
+       }
+
+       if (newnick == "0")
+       {
+               newnick = user->uuid;
+       }
+       else if (!ServerInstance->IsNick(newnick))
+       {
+               user->WriteNumeric(ERR_ERRONEUSNICKNAME, newnick, "Erroneous Nickname");
+               return CMD_FAILURE;
+       }
+
+       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;
+
+       // 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 != ServerConfig::BUT_NORMAL)
+       {
+               for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); ++i)
+               {
+                       Channel* chan = (*i)->chan;
+                       if (chan->GetPrefixValue(user) < VOICE_VALUE && chan->IsBanned(user))
+                       {
+                               if (ServerInstance->Config->RestrictBannedUsers == ServerConfig::BUT_RESTRICT_NOTIFY)
+                                       user->WriteNumeric(ERR_CANTCHANGENICK, InspIRCd::Format("Cannot change nickname while on %s (you're banned)",
+                                               chan->name.c_str()));
+                               return CMD_FAILURE;
+                       }
+               }
+       }
+
+       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
new file mode 100644 (file)
index 0000000..9400eed
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+CommandPart::CommandPart(Module* parent)
+       : Command(parent, "PART", 1, 2)
+{
+       Penalty = 5;
+       syntax = "<channel>[,<channel>]+ [:<reason>]";
+}
+
+CmdResult CommandPart::Handle(User* user, const Params& parameters)
+{
+       std::string reason;
+       if (parameters.size() > 1)
+       {
+               if (IS_LOCAL(user))
+                       msgwrap.Wrap(parameters[1], reason);
+               else
+                       reason = parameters[1];
+       }
+
+       if (CommandParser::LoopCall(user, this, parameters, 0))
+               return CMD_SUCCESS;
+
+       Channel* c = ServerInstance->FindChan(parameters[0]);
+
+       if (!c)
+       {
+               user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+               return CMD_FAILURE;
+       }
+
+       if (!c->PartUser(user, reason))
+       {
+               user->WriteNumeric(ERR_NOTONCHANNEL, c->name, "You're not on that channel");
+               return CMD_FAILURE;
+       }
+
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandPart::GetRouting(User* user, const Params& parameters)
+{
+       return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_user/cmd_quit.cpp b/src/coremods/core_user/cmd_quit.cpp
new file mode 100644 (file)
index 0000000..74781a9
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+CommandQuit::CommandQuit(Module* parent)
+       : Command(parent, "QUIT", 0, 1)
+       , operquit("operquit", ExtensionItem::EXT_USER, parent)
+{
+       works_before_reg = true;
+       syntax = "[:<message>]";
+}
+
+CmdResult CommandQuit::Handle(User* user, const Params& parameters)
+{
+       std::string quitmsg;
+       if (parameters.empty())
+               quitmsg = "Client exited";
+       else if (IS_LOCAL(user))
+               msgwrap.Wrap(parameters[0], quitmsg);
+       else
+               quitmsg = parameters[0];
+
+       std::string* operquitmsg = operquit.get(user);
+       ServerInstance->Users->QuitUser(user, quitmsg, operquitmsg);
+
+       return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandQuit::GetRouting(User* user, const Params& parameters)
+{
+       return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
diff --git a/src/coremods/core_user/cmd_user.cpp b/src/coremods/core_user/cmd_user.cpp
new file mode 100644 (file)
index 0000000..89b1739
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+enum
+{
+       // From ircu.
+       ERR_INVALIDUSERNAME = 468
+};
+
+CommandUser::CommandUser(Module* parent)
+       : SplitCommand(parent, "USER", 4, 4)
+{
+       allow_empty_last_param = false;
+       works_before_reg = true;
+       Penalty = 0;
+       syntax = "<username> <unused> <unused> :<realname>";
+}
+
+CmdResult CommandUser::HandleLocal(LocalUser* user, const Params& parameters)
+{
+       /* A user may only send the USER command once */
+       if (!(user->registered & REG_USER))
+       {
+               if (!ServerInstance->IsIdent(parameters[0]))
+               {
+                       user->WriteNumeric(ERR_INVALIDUSERNAME, name, "Your username is not valid");
+                       return CMD_FAILURE;
+               }
+               else
+               {
+                       user->ChangeIdent(parameters[0]);
+                       user->ChangeRealName(parameters[3]);
+                       user->registered = (user->registered | REG_USER);
+               }
+       }
+       else
+       {
+               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;
+               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/cmd_userhost.cpp b/src/coremods/core_user/cmd_userhost.cpp
new file mode 100644 (file)
index 0000000..f531d23
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+CmdResult CommandUserhost::Handle(User* user, const Params& parameters)
+{
+       const bool has_privs = user->HasPrivPermission("users/auspex");
+
+       std::string retbuf;
+
+       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 += u->nick;
+
+                       if (u->IsOper())
+                       {
+                               // XXX: +H hidden opers must not be shown as opers
+                               if ((u == user) || (has_privs) || (!u->IsModeSet(hideopermode)))
+                                       retbuf += '*';
+                       }
+
+                       retbuf += '=';
+                       retbuf += (u->IsAway() ? '-' : '+');
+                       retbuf += u->ident;
+                       retbuf += '@';
+                       retbuf += u->GetHost(u == user || has_privs);
+                       retbuf += ' ';
+               }
+       }
+
+       user->WriteNumeric(RPL_USERHOST, retbuf);
+
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_user/core_user.cpp b/src/coremods/core_user/core_user.cpp
new file mode 100644 (file)
index 0000000..8f90c4e
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * 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 "core_user.h"
+
+/** Handle /PASS.
+ */
+class CommandPass : public SplitCommand
+{
+ public:
+       /** Constructor for pass.
+        */
+       CommandPass(Module* parent)
+               : SplitCommand(parent, "PASS", 1, 1)
+       {
+               works_before_reg = true;
+               Penalty = 0;
+               syntax = "<password>";
+       }
+
+       /** 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 HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               // Check to make sure they haven't registered -- Fix by FCS
+               if (user->registered == REG_ALL)
+               {
+                       user->CommandFloodPenalty += 1000;
+                       user->WriteNumeric(ERR_ALREADYREGISTERED, "You may not reregister");
+                       return CMD_FAILURE;
+               }
+               user->password = parameters[0];
+
+               return CMD_SUCCESS;
+       }
+};
+
+/** Handle /PING.
+ */
+class CommandPing : public SplitCommand
+{
+ public:
+       /** Constructor for ping.
+        */
+       CommandPing(Module* parent)
+               : SplitCommand(parent, "PING", 1, 2)
+       {
+               syntax = "<servername> [:<servername>]";
+       }
+
+       /** 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 HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               ClientProtocol::Messages::Pong pong(parameters[0]);
+               user->Send(ServerInstance->GetRFCEvents().pong, pong);
+               return CMD_SUCCESS;
+       }
+};
+
+/** Handle /PONG.
+ */
+class CommandPong : public Command
+{
+ public:
+       /** Constructor for pong.
+        */
+       CommandPong(Module* parent)
+               : Command(parent, "PONG", 0, 1)
+       {
+               Penalty = 0;
+               syntax = "<ping-text>";
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               // set the user as alive so they survive to next ping
+               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;
+       }
+};
+
+void MessageWrapper::Wrap(const std::string& message, std::string& out)
+{
+       // If there is a fixed message, it is stored in prefix. Otherwise prefix contains
+       // only the prefix, so append the message and the suffix
+       out.assign(prefix);
+       if (!fixed)
+               out.append(message).append(suffix);
+}
+
+void MessageWrapper::ReadConfig(const char* prefixname, const char* suffixname, const char* fixedname)
+{
+       ConfigTag* tag = ServerInstance->Config->ConfValue("options");
+       prefix = tag->getString(fixedname);
+       fixed = (!prefix.empty());
+       if (!fixed)
+       {
+               prefix = tag->getString(prefixname);
+               suffix = tag->getString(suffixname);
+       }
+}
+
+class CoreModUser : public Module
+{
+       CommandAway cmdaway;
+       CommandNick cmdnick;
+       CommandPart cmdpart;
+       CommandPass cmdpass;
+       CommandPing cmdping;
+       CommandPong cmdpong;
+       CommandQuit cmdquit;
+       CommandUser cmduser;
+       CommandIson cmdison;
+       CommandUserhost cmduserhost;
+       SimpleUserModeHandler invisiblemode;
+       ModeUserOperator operatormode;
+       ModeUserServerNoticeMask snomaskmode;
+
+ public:
+       CoreModUser()
+               : cmdaway(this)
+               , cmdnick(this)
+               , cmdpart(this)
+               , cmdpass(this)
+               , cmdping(this)
+               , cmdpong(this)
+               , cmdquit(this)
+               , cmduser(this)
+               , cmdison(this)
+               , cmduserhost(this)
+               , invisiblemode(this, "invisible", 'i')
+               , operatormode(this)
+               , snomaskmode(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               cmdpart.msgwrap.ReadConfig("prefixpart", "suffixpart", "fixedpart");
+               cmdquit.msgwrap.ReadConfig("prefixquit", "suffixquit", "fixedquit");
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the AWAY, ISON, NICK, PART, PASS, PING, PONG, QUIT, USERHOST, and USER commands", VF_VENDOR|VF_CORE);
+       }
+};
+
+MODULE_INIT(CoreModUser)
diff --git a/src/coremods/core_user/core_user.h b/src/coremods/core_user/core_user.h
new file mode 100644 (file)
index 0000000..b4f04cd
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   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
+
+#include "inspircd.h"
+#include "listmode.h"
+#include "modules/away.h"
+
+class MessageWrapper
+{
+       std::string prefix;
+       std::string suffix;
+       bool fixed;
+
+ public:
+       /**
+        * Wrap the given message according to the config rules
+        * @param message The message to wrap
+        * @param out String where the result is placed
+        */
+       void Wrap(const std::string& message, std::string& out);
+
+       /**
+        * Read the settings from the given config keys (options block)
+        * @param prefixname Name of the config key to read the prefix from
+        * @param suffixname Name of the config key to read the suffix from
+        * @param fixedname Name of the config key to read the fixed string string from.
+        * If this key has a non-empty value, all messages will be replaced with it.
+        */
+       void ReadConfig(const char* prefixname, const char* suffixname, const char* fixedname);
+};
+
+/** Handle /AWAY.
+ */
+class CommandAway : public Command
+{
+ private:
+       Away::EventProvider awayevprov;
+
+ public:
+       /** Constructor for away.
+        */
+       CommandAway(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /ISON.
+ */
+class CommandIson : public SplitCommand
+{
+ public:
+       /** Constructor for ison.
+        */
+       CommandIson(Module* parent)
+               : SplitCommand(parent, "ISON", 1)
+       {
+               allow_empty_last_param = false;
+               syntax = "<nick> [<nick>]+";
+       }
+       /** 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 HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+
+/** Handle /NICK.
+ */
+class CommandNick : public SplitCommand
+{
+ public:
+       /** Constructor for nick.
+        */
+       CommandNick(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 HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /PART.
+ */
+class CommandPart : public Command
+{
+ public:
+       MessageWrapper msgwrap;
+
+       /** Constructor for part.
+        */
+       CommandPart(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /QUIT.
+ */
+class CommandQuit : public Command
+{
+ private:
+       StringExtItem operquit;
+
+ public:
+       MessageWrapper msgwrap;
+
+       /** Constructor for quit.
+        */
+       CommandQuit(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /USER.
+ */
+class CommandUser : public SplitCommand
+{
+ public:
+       /** Constructor for user.
+        */
+       CommandUser(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 HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+
+       /** 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);
+};
+
+/** Handle /USERHOST.
+ */
+class CommandUserhost : public Command
+{
+       UserModeReference hideopermode;
+
+ public:
+       /** Constructor for userhost.
+        */
+       CommandUserhost(Module* parent)
+               : Command(parent,"USERHOST", 1)
+               , hideopermode(parent, "hideoper")
+       {
+               allow_empty_last_param = false;
+               syntax = "<nick> [<nick>]+";
+       }
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** User mode +s
+ */
+class ModeUserServerNoticeMask : public ModeHandler
+{
+       /** Process a snomask modifier string, e.g. +abc-de
+        * @param user The target user
+        * @param input A sequence of notice mask characters
+        * @return The cleaned mode sequence which can be output,
+        * e.g. in the above example if masks c and e are not
+        * valid, this function will return +ab-d
+        */
+       std::string ProcessNoticeMasks(User* user, const std::string& input);
+
+ public:
+       ModeUserServerNoticeMask(Module* Creator);
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding) CXX11_OVERRIDE;
+       void OnParameterMissing(User* user, User* dest, Channel* channel) CXX11_OVERRIDE;
+
+       /** Create a displayable mode string of the snomasks set on a given user
+        * @param user The user whose notice masks to format
+        * @return The notice mask character sequence
+        */
+       std::string GetUserParameter(const User* user) const CXX11_OVERRIDE;
+};
+
+/** User mode +o
+ */
+class ModeUserOperator : public ModeHandler
+{
+ public:
+       ModeUserOperator(Module* Creator);
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding) CXX11_OVERRIDE;
+};
diff --git a/src/coremods/core_user/umode_o.cpp b/src/coremods/core_user/umode_o.cpp
new file mode 100644 (file)
index 0000000..20668fd
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   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/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+ModeUserOperator::ModeUserOperator(Module* Creator)
+       : ModeHandler(Creator, "oper", 'o', PARAM_NONE, MODETYPE_USER)
+{
+       oper = true;
+}
+
+ModeAction ModeUserOperator::OnModeChange(User* source, User* dest, Channel*, std::string&, bool adding)
+{
+       /* Only opers can execute this class at all */
+       if (!source->server->IsULine() && !source->IsOper())
+               return MODEACTION_DENY;
+
+       /* Not even opers can GIVE the +o mode, only take it away */
+       if (adding)
+               return MODEACTION_DENY;
+
+       /* Set the bitfields.
+        * Note that oper status is only given in User::Oper()
+        * NOT here. It is impossible to directly set +o without
+        * verifying as an oper and getting an opertype assigned
+        * to your User!
+        */
+       char snomask = IS_LOCAL(dest) ? 'o' : 'O';
+       ServerInstance->SNO->WriteToSnoMask(snomask, "User %s de-opered (by %s)", dest->nick.c_str(), source->nick.c_str());
+       dest->UnOper();
+
+       return MODEACTION_ALLOW;
+}
diff --git a/src/coremods/core_user/umode_s.cpp b/src/coremods/core_user/umode_s.cpp
new file mode 100644 (file)
index 0000000..7c83e00
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   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/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+ModeUserServerNoticeMask::ModeUserServerNoticeMask(Module* Creator)
+       : ModeHandler(Creator, "snomask", 's', PARAM_SETONLY, MODETYPE_USER)
+{
+       oper = true;
+}
+
+ModeAction ModeUserServerNoticeMask::OnModeChange(User* source, User* dest, Channel*, std::string &parameter, bool adding)
+{
+       if (adding)
+       {
+               dest->SetMode(this, true);
+               // Process the parameter (remove chars we don't understand, remove redundant chars, etc.)
+               parameter = ProcessNoticeMasks(dest, parameter);
+               return MODEACTION_ALLOW;
+       }
+       else
+       {
+               if (dest->IsModeSet(this))
+               {
+                       dest->SetMode(this, false);
+                       dest->snomasks.reset();
+                       return MODEACTION_ALLOW;
+               }
+       }
+
+       // Mode not set and trying to unset, deny
+       return MODEACTION_DENY;
+}
+
+std::string ModeUserServerNoticeMask::GetUserParameter(const User* user) const
+{
+       std::string ret;
+       if (!user->IsModeSet(this))
+               return ret;
+
+       ret.push_back('+');
+       for (unsigned char n = 0; n < 64; n++)
+       {
+               if (user->snomasks[n])
+                       ret.push_back(n + 'A');
+       }
+       return ret;
+}
+
+void ModeUserServerNoticeMask::OnParameterMissing(User* user, User* dest, Channel* channel)
+{
+       user->WriteNotice("*** The user mode +s requires a parameter (server notice mask). Please provide a parameter, e.g. '+s +*'.");
+}
+
+std::string ModeUserServerNoticeMask::ProcessNoticeMasks(User* user, const std::string& input)
+{
+       bool adding = true;
+       std::bitset<64> curr = user->snomasks;
+
+       for (std::string::const_iterator i = input.begin(); i != input.end(); ++i)
+       {
+               switch (*i)
+               {
+                       case '+':
+                               adding = true;
+                       break;
+                       case '-':
+                               adding = false;
+                       break;
+                       case '*':
+                               for (size_t j = 0; j < 64; j++)
+                               {
+                                       if (ServerInstance->SNO->IsSnomaskUsable(j+'A'))
+                                               curr[j] = adding;
+                               }
+                       break;
+                       default:
+                               // For local users check whether the given snomask is valid and enabled - IsSnomaskUsable() tests both.
+                               // For remote users accept what we were told, unless the snomask char is not a letter.
+                               if (IS_LOCAL(user))
+                               {
+                                       if (!ServerInstance->SNO->IsSnomaskUsable(*i))
+                                       {
+                                               user->WriteNumeric(ERR_UNKNOWNSNOMASK, *i, "is an unknown snomask character");
+                                               continue;
+                                       }
+                               }
+                               else if (!(((*i >= 'a') && (*i <= 'z')) || ((*i >= 'A') && (*i <= 'Z'))))
+                                       continue;
+
+                               size_t index = ((*i) - 'A');
+                               curr[index] = adding;
+                       break;
+               }
+       }
+
+       std::string plus = "+";
+       std::string minus = "-";
+
+       // Apply changes and construct two strings consisting of the newly added and the removed snomask chars
+       for (size_t i = 0; i < 64; i++)
+       {
+               bool isset = curr[i];
+               if (user->snomasks[i] != isset)
+               {
+                       user->snomasks[i] = isset;
+                       std::string& appendhere = (isset ? plus : minus);
+                       appendhere.push_back(i+'A');
+               }
+       }
+
+       // Create the final string that will be shown to the user and sent to servers
+       // Form: "+ABc-de"
+       std::string output;
+       if (plus.length() > 1)
+               output = plus;
+
+       if (minus.length() > 1)
+               output += minus;
+
+       // Unset the snomask usermode itself if every snomask was unset
+       if (user->snomasks.none())
+               user->SetMode(this, false);
+
+       return output;
+}
diff --git a/src/coremods/core_wallops.cpp b/src/coremods/core_wallops.cpp
new file mode 100644 (file)
index 0000000..09fafd2
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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"
+
+/** Handle /WALLOPS.
+ */
+class CommandWallops : public Command
+{
+       SimpleUserModeHandler wallopsmode;
+       ClientProtocol::EventProvider protoevprov;
+
+ public:
+       /** Constructor for wallops.
+        */
+       CommandWallops(Module* parent)
+               : Command(parent, "WALLOPS", 1, 1)
+               , wallopsmode(parent, "wallops", 'w')
+               , protoevprov(parent, name)
+       {
+               flags_needed = 'o';
+               syntax = ":<message>";
+       }
+
+       /** 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(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               return ROUTE_BROADCAST;
+       }
+};
+
+CmdResult CommandWallops::Handle(User* user, const Params& parameters)
+{
+       ClientProtocol::Message msg("WALLOPS", user);
+       msg.PushParamRef(parameters[0]);
+       ClientProtocol::Event wallopsevent(protoevprov, msg);
+
+       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+       for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               LocalUser* curr = *i;
+               if (curr->IsModeSet(wallopsmode))
+                       curr->Send(wallopsevent);
+       }
+
+       return CMD_SUCCESS;
+}
+
+class CoreModWallops : public Module
+{
+ private:
+       CommandWallops cmd;
+
+ public:
+       CoreModWallops()
+               : cmd(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the WALLOPS command", VF_CORE | VF_VENDOR);
+       }
+};
+
+MODULE_INIT(CoreModWallops)
diff --git a/src/coremods/core_who.cpp b/src/coremods/core_who.cpp
new file mode 100644 (file)
index 0000000..d6df6de
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2018 Peter Powell <petpow@saberuk.com>
+ *   Copyright (C) 2014 Adam <Adam@anope.org>
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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/account.h"
+#include "modules/who.h"
+
+enum
+{
+       // From RFC 1459.
+       RPL_ENDOFWHO = 315,
+       RPL_WHOREPLY = 352,
+
+       // From ircu.
+       RPL_WHOSPCRPL = 354
+};
+
+static const char whox_field_order[] = "tcuihsnfdlaor";
+static const char who_field_order[] = "cuhsnf";
+
+struct WhoData : public Who::Request
+{
+       bool GetFieldIndex(char flag, size_t& out) const CXX11_OVERRIDE
+       {
+               if (!whox)
+               {
+                       const char* pos = strchr(who_field_order, flag);
+                       if (pos == NULL)
+                               return false;
+
+                       out = pos - who_field_order;
+                       return true;
+               }
+
+               if (!whox_fields[flag])
+                       return false;
+
+               out = 0;
+               for (const char* c = whox_field_order; *c && *c != flag; ++c)
+               {
+                       if (whox_fields[*c])
+                               ++out;
+               }
+
+               return whox_field_order[out];
+       }
+
+       WhoData(const CommandBase::Params& parameters)
+       {
+               // Find the matchtext and swap the 0 for a * so we can use InspIRCd::Match on it.
+               matchtext = parameters.size() > 2 ? parameters[2] : parameters[0];
+               if (matchtext == "0")
+                       matchtext = "*";
+
+               // Fuzzy matches are when the source has not specified a specific user.
+               fuzzy_match = (parameters.size() > 1) || (matchtext.find_first_of("*?.") != std::string::npos);
+
+               // If flags have been specified by the source.
+               if (parameters.size() > 1)
+               {
+                       std::bitset<UCHAR_MAX>* current_bitset = &flags;
+                       for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
+                       {
+                               unsigned char chr = static_cast<unsigned char>(*iter);
+
+                               // If the source specifies a percentage the rest of the flags are WHOX fields.
+                               if (chr == '%')
+                               {
+                                       whox = true;
+                                       current_bitset = &whox_fields;
+                                       continue;
+                               }
+
+                               // If we are in WHOX mode and the source specifies a comma
+                               // the rest of the parameter is the query type.
+                               if (whox && chr == ',')
+                               {
+                                       whox_querytype.assign(++iter, parameters[1].end());
+                                       break;
+                               }
+
+                               // The source specified a matching flag.
+                               current_bitset->set(chr);
+                       }
+               }
+       }
+};
+
+class CommandWho : public SplitCommand
+{
+ private:
+       ChanModeReference secretmode;
+       ChanModeReference privatemode;
+       UserModeReference hidechansmode;
+       UserModeReference invisiblemode;
+       Events::ModuleEventProvider whoevprov;
+
+       /** Determines whether a user can view the users of a channel. */
+       bool CanView(Channel* chan, User* user)
+       {
+               // If we are in a channel we can view all users in it.
+               if (chan->HasUser(user))
+                       return true;
+
+               // Opers with the users/auspex priv can see everything.
+               if (user->HasPrivPermission("users/auspex"))
+                       return true;
+
+               // You can see inside a channel from outside unless it is secret or private.
+               return !chan->IsModeSet(secretmode) && !chan->IsModeSet(privatemode);
+       }
+
+       /** Gets the first channel which is visible between the source and the target users. */
+       Membership* GetFirstVisibleChannel(LocalUser* source, User* user)
+       {
+               for (User::ChanList::iterator iter = user->chans.begin(); iter != user->chans.end(); ++iter)
+               {
+                       Membership* memb = *iter;
+
+                       // TODO: move the +I check into m_hidechans.
+                       bool has_modes = memb->chan->IsModeSet(secretmode) || memb->chan->IsModeSet(privatemode) || user->IsModeSet(hidechansmode);
+                       if (source == user || !has_modes || memb->chan->HasUser(source))
+                               return memb;
+               }
+               return NULL;
+       }
+
+       /** Determines whether WHO flags match a specific channel user. */
+       bool MatchChannel(LocalUser* source, Membership* memb, WhoData& data);
+
+       /** Determines whether WHO flags match a specific user. */
+       static bool MatchUser(LocalUser* source, User* target, WhoData& data);
+
+       /** Performs a WHO request on a channel. */
+       void WhoChannel(LocalUser* source, const std::vector<std::string>& parameters, Channel* c, WhoData& data);
+
+       /** Template for getting a user from various types of collection. */
+       template<typename T>
+       static User* GetUser(T& t);
+
+       /** Performs a WHO request on a list of users. */
+       template<typename T>
+       void WhoUsers(LocalUser* source, const std::vector<std::string>& parameters, const T& users, WhoData& data);
+
+ public:
+       CommandWho(Module* parent)
+               : SplitCommand(parent, "WHO", 1, 3)
+               , secretmode(parent, "secret")
+               , privatemode(parent, "private")
+               , hidechansmode(parent, "hidechans")
+               , invisiblemode(parent, "invisible")
+               , whoevprov(parent, "event/who")
+       {
+               allow_empty_last_param = false;
+               syntax = "<server>|<nick>|<channel>|<realname>|<host>|0 [[Aafhilmnoprstux][%acdfhilnorstu] <server>|<nick>|<channel>|<realname>|<host>|0]";
+       }
+
+       /** Sends a WHO reply to a user. */
+       void SendWhoLine(LocalUser* user, const std::vector<std::string>& parameters, Membership* memb, User* u, WhoData& data);
+
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+template<> User* CommandWho::GetUser(UserManager::OperList::const_iterator& t) { return *t; }
+template<> User* CommandWho::GetUser(user_hash::const_iterator& t) { return t->second; }
+
+bool CommandWho::MatchChannel(LocalUser* source, Membership* memb, WhoData& data)
+{
+       bool source_has_users_auspex = source->HasPrivPermission("users/auspex");
+       bool source_can_see_server = ServerInstance->Config->HideServer.empty() || source_has_users_auspex;
+
+       // The source only wants remote users. This user is eligible if:
+       //   (1) The source can't see server information.
+       //   (2) The source is not local to the current server.
+       LocalUser* lu = IS_LOCAL(memb->user);
+       if (data.flags['f'] && source_can_see_server && lu)
+               return false;
+
+       // The source only wants local users. This user is eligible if:
+       //   (1) The source can't see server information.
+       //   (2) The source is local to the current server.
+       if (data.flags['l'] && source_can_see_server && !lu)
+               return false;
+
+       // Only show operators if the oper flag has been specified.
+       if (data.flags['o'] && !memb->user->IsOper())
+               return false;
+
+       // All other flags are ignored for channels.
+       return true;
+}
+
+bool CommandWho::MatchUser(LocalUser* source, User* user, WhoData& data)
+{
+       // Users who are not fully registered can never match.
+       if (user->registered != REG_ALL)
+               return false;
+
+       bool source_has_users_auspex = source->HasPrivPermission("users/auspex");
+       bool source_can_see_target = source == user || source_has_users_auspex;
+       bool source_can_see_server = ServerInstance->Config->HideServer.empty() || source_has_users_auspex;
+
+       // The source only wants remote users. This user is eligible if:
+       //   (1) The source can't see server information.
+       //   (2) The source is not local to the current server.
+       LocalUser* lu = IS_LOCAL(user);
+       if (data.flags['f'] && source_can_see_server && lu)
+               return false;
+
+       // The source only wants local users. This user is eligible if:
+       //   (1) The source can't see server information.
+       //   (2) The source is local to the current server.
+       if (data.flags['l'] && source_can_see_server && !lu)
+               return false;
+
+       // The source wants to match against users' away messages.
+       bool match = false;
+       if (data.flags['A'])
+               match = user->IsAway() && InspIRCd::Match(user->awaymsg, data.matchtext, ascii_case_insensitive_map);
+
+       // The source wants to match against users' account names.
+       else if (data.flags['a'])
+       {
+               const AccountExtItem* accountext = GetAccountExtItem();
+               const std::string* account = accountext ? accountext->get(user) : NULL;
+               match = account && InspIRCd::Match(*account, data.matchtext);
+       }
+
+       // The source wants to match against users' hostnames.
+       else if (data.flags['h'])
+       {
+               const std::string host = user->GetHost(source_can_see_target && data.flags['x']);
+               match = InspIRCd::Match(host, data.matchtext, ascii_case_insensitive_map);
+       }
+
+       // The source wants to match against users' IP addresses.
+       else if (data.flags['i'])
+               match = source_can_see_target && InspIRCd::MatchCIDR(user->GetIPString(), data.matchtext, ascii_case_insensitive_map);
+
+       // The source wants to match against users' modes.
+       else if (data.flags['m'])
+       {
+               if (source_can_see_target)
+               {
+                       bool set = true;
+                       for (std::string::const_iterator iter = data.matchtext.begin(); iter != data.matchtext.end(); ++iter)
+                       {
+                               unsigned char chr = static_cast<unsigned char>(*iter);
+                               switch (chr)
+                               {
+                                       // The following user modes should be set.
+                                       case '+':
+                                               set = true;
+                                               break;
+
+                                       // The following user modes should be unset.
+                                       case '-':
+                                               set = false;
+                                               break;
+
+                                       default:
+                                               if (user->IsModeSet(chr) != set)
+                                                       return false;
+                                               break;
+                               }
+                       }
+
+                       // All of the modes matched.
+                       return true;
+               }
+       }
+
+       // The source wants to match against users' nicks.
+       else if (data.flags['n'])
+               match = InspIRCd::Match(user->nick, data.matchtext);
+
+       // The source wants to match against users' connection ports.
+       else if (data.flags['p'])
+       {
+               if (source_can_see_target && lu)
+               {
+                       irc::portparser portrange(data.matchtext, false);
+                       long port;
+                       while ((port = portrange.GetToken()))
+                       {
+                               if (port == lu->server_sa.port())
+                               {
+                                       match = true;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       // The source wants to match against users' real names.
+       else if (data.flags['r'])
+               match = InspIRCd::Match(user->GetRealName(), data.matchtext, ascii_case_insensitive_map);
+
+       else if (data.flags['s'])
+       {
+               bool show_real_server_name = ServerInstance->Config->HideServer.empty() || (source->HasPrivPermission("servers/auspex") && data.flags['x']);
+               const std::string server = show_real_server_name ? user->server->GetName() : ServerInstance->Config->HideServer;
+               match = InspIRCd::Match(server, data.matchtext, ascii_case_insensitive_map);
+       }
+
+       // The source wants to match against users' connection times.
+       else if (data.flags['t'])
+       {
+               time_t seconds = ServerInstance->Time() - InspIRCd::Duration(data.matchtext);
+               if (user->signon >= seconds)
+                       match = true;
+       }
+
+       // The source wants to match against users' idents.
+       else if (data.flags['u'])
+               match = InspIRCd::Match(user->ident, data.matchtext, ascii_case_insensitive_map);
+
+       // The <name> passed to WHO is matched against users' host, server,
+       // real name and nickname if the channel <name> cannot be found.
+       else
+       {
+               const std::string host = user->GetHost(source_can_see_target && data.flags['x']);
+               match = InspIRCd::Match(host, data.matchtext, ascii_case_insensitive_map);
+
+               if (!match)
+               {
+                       bool show_real_server_name = ServerInstance->Config->HideServer.empty() || (source->HasPrivPermission("servers/auspex") && data.flags['x']);
+                       const std::string server = show_real_server_name ? user->server->GetName() : ServerInstance->Config->HideServer;
+                       match = InspIRCd::Match(server, data.matchtext, ascii_case_insensitive_map);
+               }
+
+               if (!match)
+                       match = InspIRCd::Match(user->GetRealName(), data.matchtext, ascii_case_insensitive_map);
+
+               if (!match)
+                       match = InspIRCd::Match(user->nick, data.matchtext);
+       }
+
+       return match;
+}
+
+void CommandWho::WhoChannel(LocalUser* source, const std::vector<std::string>& parameters, Channel* chan, WhoData& data)
+{
+       if (!CanView(chan, source))
+               return;
+
+       bool inside = chan->HasUser(source);
+       const Channel::MemberMap& users = chan->GetUsers();
+       for (Channel::MemberMap::const_iterator iter = users.begin(); iter != users.end(); ++iter)
+       {
+               User* user = iter->first;
+               Membership* memb = iter->second;
+
+               // Only show invisible users if the source is in the channel or has the users/auspex priv.
+               if (!inside && user->IsModeSet(invisiblemode) && !source->HasPrivPermission("users/auspex"))
+                       continue;
+
+               // Skip the user if it doesn't match the query.
+               if (!MatchChannel(source, memb, data))
+                       continue;
+
+               SendWhoLine(source, parameters, memb, user, data);
+       }
+}
+
+template<typename T>
+void CommandWho::WhoUsers(LocalUser* source, const std::vector<std::string>& parameters, const T& users, WhoData& data)
+{
+       for (typename T::const_iterator iter = users.begin(); iter != users.end(); ++iter)
+       {
+               User* user = GetUser(iter);
+
+               // Only show users in response to a fuzzy WHO if we can see them normally.
+               bool can_see_normally = user == source || source->SharesChannelWith(user) || !user->IsModeSet(invisiblemode);
+               if (data.fuzzy_match && !can_see_normally && !source->HasPrivPermission("users/auspex"))
+                       continue;
+
+               // Skip the user if it doesn't match the query.
+               if (!MatchUser(source, user, data))
+                       continue;
+
+               SendWhoLine(source, parameters, NULL, user, data);
+       }
+}
+
+void CommandWho::SendWhoLine(LocalUser* source, const std::vector<std::string>& parameters, Membership* memb, User* user, WhoData& data)
+{
+       if (!memb)
+               memb = GetFirstVisibleChannel(source, user);
+
+       bool source_can_see_target = source == user || source->HasPrivPermission("users/auspex");
+       Numeric::Numeric wholine(data.whox ? RPL_WHOSPCRPL : RPL_WHOREPLY);
+       if (data.whox)
+       {
+               // The source used WHOX so we send a fancy customised response.
+
+               // Include the query type in the reply.
+               if (data.whox_fields['t'])
+                       wholine.push(data.whox_querytype.empty() || data.whox_querytype.length() > 3 ? "0" : data.whox_querytype);
+
+               // Include the first channel name.
+               if (data.whox_fields['c'])
+                       wholine.push(memb ? memb->chan->name : "*");
+
+               // Include the user's ident.
+               if (data.whox_fields['u'])
+                       wholine.push(user->ident);
+
+               // Include the user's IP address.
+               if (data.whox_fields['i'])
+                       wholine.push(source_can_see_target ? user->GetIPString() : "255.255.255.255");
+
+               // Include the user's hostname.
+               if (data.whox_fields['h'])
+                       wholine.push(user->GetHost(source_can_see_target && data.flags['x']));
+
+               // Include the server name.
+               if (data.whox_fields['s'])
+               {
+                       if (ServerInstance->Config->HideServer.empty() || (source->HasPrivPermission("servers/auspex") && data.flags['x']))
+                               wholine.push(user->server->GetName());
+                       else
+                               wholine.push(ServerInstance->Config->HideServer);
+               }
+
+               // Include the user's nickname.
+               if (data.whox_fields['n'])
+                       wholine.push(user->nick);
+
+               // Include the user's flags.
+               if (data.whox_fields['f'])
+               {
+                       // Away state.
+                       std::string flags(user->IsAway() ? "G" : "H");
+
+                       // Operator status.
+                       if (user->IsOper())
+                               flags.push_back('*');
+
+                       // Membership prefix.
+                       if (memb)
+                       {
+                               char prefix = memb->GetPrefixChar();
+                               if (prefix)
+                                       flags.push_back(prefix);
+                       }
+
+                       wholine.push(flags);
+               }
+
+               // Include the number of hops between the users.
+               if (data.whox_fields['d'])
+                       wholine.push("0");
+
+               // Include the user's idle time.
+               if (data.whox_fields['l'])
+               {
+                       LocalUser* lu = IS_LOCAL(user);
+                       unsigned long idle = lu ? ServerInstance->Time() - lu->idle_lastmsg : 0;
+                       wholine.push(ConvToStr(idle));
+               }
+
+               // Include the user's account name.
+               if (data.whox_fields['a'])
+               {
+                       const AccountExtItem* accountext = GetAccountExtItem();
+                       const std::string* account = accountext ? accountext->get(user) : NULL;
+                       wholine.push(account ? *account : "0");
+               }
+
+               // Include the user's operator rank level.
+               if (data.whox_fields['o'])
+                       wholine.push(memb ? ConvToStr(memb->getRank()) : "0");
+
+               // Include the user's real name.
+               if (data.whox_fields['r'])
+                       wholine.push(user->GetRealName());
+       }
+       else
+       {
+               // We are not using WHOX so we just send a plain RFC response.
+
+               // Include the channel name.
+               wholine.push(memb ? memb->chan->name : "*");
+
+               // Include the user's ident.
+               wholine.push(user->ident);
+
+               // Include the user's hostname.
+               wholine.push(user->GetHost(source_can_see_target && data.flags['x']));
+
+               // Include the server name.
+               if (ServerInstance->Config->HideServer.empty() || (source->HasPrivPermission("servers/auspex") && data.flags['x']))
+                       wholine.push(user->server->GetName());
+               else
+                       wholine.push(ServerInstance->Config->HideServer);
+
+               // Include the user's nick.
+               wholine.push(user->nick);
+
+               // Include the user's flags.
+               {
+                       // Away state.
+                       std::string flags(user->IsAway() ? "G" : "H");
+
+                       // Operator status.
+                       if (user->IsOper())
+                               flags.push_back('*');
+
+                       // Membership prefix.
+                       if (memb)
+                       {
+                               char prefix = memb->GetPrefixChar();
+                               if (prefix)
+                                       flags.push_back(prefix);
+                       }
+
+                       wholine.push(flags);
+               }
+
+               // Include the number of hops between the users and the user's real name.
+               wholine.push("0 ");
+               wholine.GetParams().back().append(user->GetRealName());
+       }
+
+       ModResult res;
+       FIRST_MOD_RESULT_CUSTOM(whoevprov, Who::EventListener, OnWhoLine, res, (data, source, user, memb, wholine));
+       if (res != MOD_RES_DENY)
+               data.results.push_back(wholine);
+}
+
+CmdResult CommandWho::HandleLocal(LocalUser* user, const Params& parameters)
+{
+       WhoData data(parameters);
+
+       // Is the source running a WHO on a channel?
+       Channel* chan = ServerInstance->FindChan(data.matchtext);
+       if (chan)
+               WhoChannel(user, parameters, chan, data);
+
+       // If we only want to match against opers we only have to iterate the oper list.
+       else if (data.flags['o'])
+               WhoUsers(user, parameters, ServerInstance->Users->all_opers, data);
+
+       // Otherwise we have to use the global user list.
+       else
+               WhoUsers(user, parameters, ServerInstance->Users->GetUsers(), data);
+
+       // Send the results to the source.
+       for (std::vector<Numeric::Numeric>::const_iterator n = data.results.begin(); n != data.results.end(); ++n)
+               user->WriteNumeric(*n);
+       user->WriteNumeric(RPL_ENDOFWHO, (data.matchtext.empty() ? "*" : data.matchtext.c_str()), "End of /WHO list.");
+
+       // Penalize the source a bit for large queries with one unit of penalty per 200 results.
+       user->CommandFloodPenalty += data.results.size() * 5;
+       return CMD_SUCCESS;
+}
+
+class CoreModWho : public Module
+{
+ private:
+       CommandWho cmd;
+
+ public:
+       CoreModWho()
+               : cmd(this)
+       {
+       }
+
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+       {
+               tokens["WHOX"];
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the WHO command", VF_VENDOR|VF_CORE);
+       }
+};
+
+MODULE_INIT(CoreModWho)
diff --git a/src/coremods/core_whois.cpp b/src/coremods/core_whois.cpp
new file mode 100644 (file)
index 0000000..0755f66
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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/whois.h"
+
+enum
+{
+       // From RFC 1459.
+       RPL_WHOISUSER = 311,
+       RPL_WHOISOPERATOR = 313,
+       RPL_WHOISIDLE = 317,
+       RPL_WHOISCHANNELS = 319,
+
+       // From UnrealIRCd.
+       RPL_WHOISHOST = 378,
+       RPL_WHOISMODES = 379,
+
+       // InspIRCd-specific.
+       RPL_CHANNELSMSG = 651
+};
+
+enum SplitWhoisState
+{
+       // Don't split private/secret channels into a separate RPL_WHOISCHANNELS numeric.
+       SPLITWHOIS_NONE,
+
+       // Split private/secret channels into a separate RPL_WHOISCHANNELS numeric.
+       SPLITWHOIS_SPLIT,
+
+       // Split private/secret channels into a separate RPL_WHOISCHANNELS numeric with RPL_CHANNELSMSG to explain the split.
+       SPLITWHOIS_SPLITMSG
+};
+
+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
+{
+       ChanModeReference secretmode;
+       ChanModeReference privatemode;
+       UserModeReference snomaskmode;
+       Events::ModuleEventProvider evprov;
+       Events::ModuleEventProvider lineevprov;
+
+       void DoWhois(LocalUser* user, User* dest, time_t signon, unsigned long idle);
+       void SendChanList(WhoisContextImpl& whois);
+
+ public:
+        /** If true then all opers are shown with a generic 'is a server operator' line rather than the oper type. */
+       bool genericoper;
+
+        /** How to handle private/secret channels in the WHOIS response. */
+       SplitWhoisState splitwhois;
+
+
+
+       /** Constructor for whois.
+        */
+       CommandWhois(Module* parent)
+               : SplitCommand(parent, "WHOIS", 1)
+               , secretmode(parent, "secret")
+               , privatemode(parent, "private")
+               , snomaskmode(parent, "snomask")
+               , evprov(parent, "event/whois")
+               , lineevprov(parent, "event/whoisline")
+       {
+               Penalty = 2;
+               syntax = "[<servername>] <nick>[,<nick>]+";
+       }
+
+       /** 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 HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
+       CmdResult HandleRemote(RemoteUser* target, const Params& parameters) CXX11_OVERRIDE;
+};
+
+class WhoisNumericSink
+{
+       WhoisContextImpl& whois;
+ public:
+       WhoisNumericSink(WhoisContextImpl& whoisref)
+               : whois(whoisref)
+       {
+       }
+
+       void operator()(Numeric::Numeric& numeric) const
+       {
+               whois.SendLine(numeric);
+       }
+};
+
+class WhoisChanListNumericBuilder : public Numeric::GenericBuilder<' ', false, WhoisNumericSink>
+{
+ public:
+       WhoisChanListNumericBuilder(WhoisContextImpl& whois)
+               : Numeric::GenericBuilder<' ', false, WhoisNumericSink>(WhoisNumericSink(whois), RPL_WHOISCHANNELS, false, whois.GetSource()->nick.size() + whois.GetTarget()->nick.size() + 1)
+       {
+               GetNumeric().push(whois.GetTarget()->nick).push(std::string());
+       }
+};
+
+class WhoisChanList
+{
+       const SplitWhoisState& splitwhois;
+       WhoisChanListNumericBuilder num;
+       WhoisChanListNumericBuilder secretnum;
+       std::string prefixstr;
+
+       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);
+       }
+
+ public:
+       WhoisChanList(WhoisContextImpl& whois, const SplitWhoisState& sws)
+               : splitwhois(sws)
+               , num(whois)
+               , secretnum(whois)
+       {
+       }
+
+       void AddVisible(Membership* memb)
+       {
+               AddMember(memb, num);
+       }
+
+       void AddHidden(Membership* memb)
+       {
+               AddMember(memb, splitwhois == SPLITWHOIS_NONE ? num : secretnum);
+       }
+
+       void Flush(WhoisContextImpl& whois)
+       {
+               num.Flush();
+               if (!secretnum.IsEmpty() && splitwhois == SPLITWHOIS_SPLITMSG)
+                       whois.SendLine(RPL_CHANNELSMSG, "is on private/secret channels:");
+               secretnum.Flush();
+       }
+};
+
+void CommandWhois::SendChanList(WhoisContextImpl& whois)
+{
+       WhoisChanList chanlist(whois, splitwhois);
+
+       User* const target = whois.GetTarget();
+       bool hasoperpriv = whois.GetSource()->HasPrivPermission("users/channel-spy");
+       for (User::ChanList::iterator i = target->chans.begin(); i != target->chans.end(); ++i)
+       {
+               Membership* memb = *i;
+               Channel* c = memb->chan;
+
+               // Anyone can view channels which are not private or secret.
+               if (!c->IsModeSet(privatemode) && !c->IsModeSet(secretmode))
+                       chanlist.AddVisible(memb);
+
+               // Hidden channels are visible when the following conditions are true:
+               // (1) The source user and the target user are the same.
+               // (2) The source user is a member of the hidden channel.
+               // (3) The source user is an oper with the users/channel-spy privilege.
+               else if (whois.IsSelfWhois() || c->HasUser(whois.GetSource()) || hasoperpriv)
+                       chanlist.AddHidden(memb);
+       }
+
+       chanlist.Flush(whois);
+}
+
+void CommandWhois::DoWhois(LocalUser* user, User* dest, time_t signon, unsigned long idle)
+{
+       WhoisContextImpl whois(user, dest, lineevprov);
+
+       whois.SendLine(RPL_WHOISUSER, dest->ident, dest->GetDisplayedHost(), '*', dest->GetRealName());
+       if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
+       {
+               whois.SendLine(RPL_WHOISHOST, InspIRCd::Format("is connecting from %s@%s %s", dest->ident.c_str(), dest->GetRealHost().c_str(), dest->GetIPString().c_str()));
+       }
+
+       SendChanList(whois);
+
+       if (!whois.IsSelfWhois() && !ServerInstance->Config->HideServer.empty() && !user->HasPrivPermission("servers/auspex"))
+       {
+               whois.SendLine(RPL_WHOISSERVER, ServerInstance->Config->HideServer, ServerInstance->Config->Network);
+       }
+       else
+       {
+               whois.SendLine(RPL_WHOISSERVER, dest->server->GetName(), dest->server->GetDesc());
+       }
+
+       if (dest->IsAway())
+       {
+               whois.SendLine(RPL_AWAY, dest->awaymsg);
+       }
+
+       if (dest->IsOper())
+       {
+               if (genericoper)
+                       whois.SendLine(RPL_WHOISOPERATOR, "is a server operator");
+               else
+                       whois.SendLine(RPL_WHOISOPERATOR, 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 (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
+       {
+               if (dest->IsModeSet(snomaskmode))
+               {
+                       whois.SendLine(RPL_WHOISMODES, InspIRCd::Format("is using modes %s %s", dest->GetModeLetters().c_str(), snomaskmode->GetUserParameter(dest).c_str()));
+               }
+               else
+               {
+                       whois.SendLine(RPL_WHOISMODES, InspIRCd::Format("is using modes %s", dest->GetModeLetters().c_str()));
+               }
+       }
+
+       FOREACH_MOD_CUSTOM(evprov, Whois::EventListener, OnWhois, (whois));
+
+       /*
+        * We only send these if we've been provided them. That is, if hideserver is turned off, and user is local, or
+        * if remote whois is queried, too. This is to keep the user hidden, and also since you can't reliably tell remote time. -- w00t
+        */
+       if ((idle) || (signon))
+       {
+               whois.SendLine(RPL_WHOISIDLE, idle, signon, "seconds idle, signon time");
+       }
+
+       whois.SendLine(RPL_ENDOFWHOIS, "End of /WHOIS list.");
+}
+
+CmdResult CommandWhois::HandleRemote(RemoteUser* target, const Params& parameters)
+{
+       if (parameters.size() < 2)
+               return CMD_FAILURE;
+
+       User* user = ServerInstance->FindUUID(parameters[0]);
+       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 = ConvToNum<unsigned long>(parameters.back());
+       DoWhois(localuser, target, target->signon, idle);
+
+       return CMD_SUCCESS;
+}
+
+CmdResult CommandWhois::HandleLocal(LocalUser* user, const Params& parameters)
+{
+       User *dest;
+       unsigned int userindex = 0;
+       unsigned long idle = 0;
+       time_t signon = 0;
+
+       if (CommandParser::LoopCall(user, this, parameters, 0))
+               return CMD_SUCCESS;
+
+       /*
+        * If 2 paramters are specified (/whois nick nick), ignore the first one like spanningtree
+        * does, and use the second one, otherwise, use the only paramter. -- djGrrr
+        */
+       if (parameters.size() > 1)
+               userindex = 1;
+
+       dest = ServerInstance->FindNickOnly(parameters[userindex]);
+
+       if ((dest) && (dest->registered == REG_ALL))
+       {
+               /*
+                * Okay. Umpteenth attempt at doing this, so let's re-comment...
+                * For local users (/w localuser), we show idletime if hideserver is disabled
+                * For local users (/w localuser localuser), we always show idletime, hence parameters.size() > 1 check.
+                * For remote users (/w remoteuser), we do NOT show idletime
+                * For remote users (/w remoteuser remoteuser), spanningtree will handle calling do_whois, so we can ignore this case.
+                * Thanks to djGrrr for not being impatient while I have a crap day coding. :p -- w00t
+                */
+               LocalUser* localuser = IS_LOCAL(dest);
+               if (localuser && (ServerInstance->Config->HideServer.empty() || parameters.size() > 1))
+               {
+                       idle = labs((long)((localuser->idle_lastmsg)-ServerInstance->Time()));
+                       signon = dest->signon;
+               }
+
+               DoWhois(user,dest,signon,idle);
+       }
+       else
+       {
+               /* no such nick/channel */
+               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;
+       }
+
+       return CMD_SUCCESS;
+}
+
+class CoreModWhois : public Module
+{
+ private:
+       CommandWhois cmd;
+
+ public:
+       CoreModWhois()
+               : cmd(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("options");
+               const std::string splitwhois = tag->getString("splitwhois", "no");
+               SplitWhoisState newsplitstate;
+               if (stdalgo::string::equalsci(splitwhois, "no"))
+                       newsplitstate = SPLITWHOIS_NONE;
+               else if (stdalgo::string::equalsci(splitwhois, "split"))
+                       newsplitstate = SPLITWHOIS_SPLIT;
+               else if (stdalgo::string::equalsci(splitwhois, "splitmsg"))
+                       newsplitstate = SPLITWHOIS_SPLITMSG;
+               else
+                       throw ModuleException(splitwhois + " is an invalid <security:splitwhois> value, at " + tag->getTagLocation());
+
+               ConfigTag* security = ServerInstance->Config->ConfValue("security");
+               cmd.genericoper = security->getBool("genericoper");
+               cmd.splitwhois = newsplitstate;
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the WHOIS command", VF_VENDOR|VF_CORE);
+       }
+};
+
+MODULE_INIT(CoreModWhois)
diff --git a/src/coremods/core_whowas.cpp b/src/coremods/core_whowas.cpp
new file mode 100644 (file)
index 0000000..68e6621
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 "commands/cmd_whowas.h"
+#include "modules/stats.h"
+
+enum
+{
+       // From RFC 1459.
+       RPL_WHOWASUSER = 314,
+       RPL_ENDOFWHOWAS = 369,
+
+       // InspIRCd-specific.
+       RPL_WHOWASIP = 652
+};
+
+CommandWhowas::CommandWhowas( Module* parent)
+       : Command(parent, "WHOWAS", 1)
+{
+       syntax = "<nick>";
+       Penalty = 2;
+}
+
+CmdResult CommandWhowas::Handle(User* user, const Params& parameters)
+{
+       /* if whowas disabled in config */
+       if (!manager.IsEnabled())
+       {
+               user->WriteNumeric(ERR_UNKNOWNCOMMAND, name, "This command has been disabled.");
+               return CMD_FAILURE;
+       }
+
+       const WhoWas::Nick* const nick = manager.FindNick(parameters[0]);
+       if (!nick)
+       {
+               user->WriteNumeric(ERR_WASNOSUCHNICK, parameters[0], "There was no such nickname");
+       }
+       else
+       {
+               const WhoWas::Nick::List& list = nick->entries;
+               for (WhoWas::Nick::List::const_iterator i = list.begin(); i != list.end(); ++i)
+               {
+                       WhoWas::Entry* u = *i;
+
+                       user->WriteNumeric(RPL_WHOWASUSER, parameters[0], u->ident, u->dhost, '*', u->real);
+
+                       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->HideServer.empty() && !user->HasPrivPermission("servers/auspex"));
+                       user->WriteNumeric(RPL_WHOISSERVER, parameters[0], (hide_server ? ServerInstance->Config->HideServer : u->server), signon);
+               }
+       }
+
+       user->WriteNumeric(RPL_ENDOFWHOWAS, parameters[0], "End of WHOWAS");
+       return CMD_SUCCESS;
+}
+
+WhoWas::Manager::Manager()
+       : GroupSize(0), MaxGroups(0), MaxKeep(0)
+{
+}
+
+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::Nick::List& list = i->second->entries;
+               entrycount += list.size();
+       }
+
+       Stats stats;
+       stats.entrycount = entrycount;
+       return stats;
+}
+
+void WhoWas::Manager::Add(User* user)
+{
+       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(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::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(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
+                       PurgeNick(whowas_fifo.front());
+               }
+       }
+       else
+       {
+               // We've met this nick before, add a new record to the list
+               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 (list.size() > this->GroupSize)
+               {
+                       delete list.front();
+                       list.pop_front();
+               }
+       }
+}
+
+/* on rehash, refactor maps according to new conf values */
+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())
+       {
+               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(); )
+       {
+               WhoWas::Nick::List& list = i->second->entries;
+               while (list.size() > this->GroupSize)
+               {
+                       delete list.front();
+                       list.pop_front();
+               }
+
+               if (list.empty())
+                       PurgeNick(i++);
+               else
+                       ++i;
+       }
+}
+
+/* call maintain once an hour to remove expired nicks */
+void WhoWas::Manager::Maintain()
+{
+       time_t min = ServerInstance->Time() - this->MaxKeep;
+       for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); )
+       {
+               WhoWas::Nick::List& list = i->second->entries;
+               while (!list.empty() && list.front()->signon < min)
+               {
+                       delete list.front();
+                       list.pop_front();
+               }
+
+               if (list.empty())
+                       PurgeNick(i++);
+               else
+                       ++i;
+       }
+}
+
+WhoWas::Manager::~Manager()
+{
+       for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
+       {
+               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;
+}
+
+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);
+}
+
+WhoWas::Entry::Entry(User* user)
+       : host(user->GetRealHost())
+       , dhost(user->GetDisplayedHost())
+       , ident(user->ident)
+       , server(user->server->GetName())
+       , real(user->GetRealName())
+       , 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, public Stats::EventListener
+{
+       CommandWhowas cmd;
+
+ public:
+       ModuleWhoWas()
+               : Stats::EventListener(this)
+               , cmd(this)
+       {
+       }
+
+       void OnGarbageCollect() CXX11_OVERRIDE
+       {
+               // Remove all entries older than MaxKeep
+               cmd.manager.Maintain();
+       }
+
+       void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
+       {
+               cmd.manager.Add(user);
+       }
+
+       ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
+       {
+               if (stats.GetSymbol() == 'z')
+                       stats.AddRow(249, "Whowas entries: "+ConvToStr(cmd.manager.GetStats().entrycount));
+
+               return MOD_RES_PASSTHRU;
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("whowas");
+               unsigned int NewGroupSize = tag->getUInt("groupsize", 10, 0, 10000);
+               unsigned int NewMaxGroups = tag->getUInt("maxgroups", 10240, 0, 1000000);
+               unsigned int NewMaxKeep = tag->getDuration("maxkeep", 3600, 3600);
+
+               cmd.manager.UpdateConfig(NewGroupSize, NewMaxGroups, NewMaxKeep);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the WHOWAS command", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleWhoWas)
diff --git a/src/coremods/core_xline/cmd_eline.cpp b/src/coremods/core_xline/cmd_eline.cpp
new file mode 100644 (file)
index 0000000..2a9f192
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 "xline.h"
+#include "core_xline.h"
+
+CommandEline::CommandEline(Module* parent)
+       : Command(parent, "ELINE", 1, 3)
+{
+       flags_needed = 'o';
+       syntax = "<user@host> [<duration> :<reason>]";
+}
+
+/** Handle /ELINE
+ */
+CmdResult CommandEline::Handle(User* user, const Params& parameters)
+{
+       std::string target = parameters[0];
+
+       if (parameters.size() >= 3)
+       {
+               IdentHostPair ih;
+               User* find = ServerInstance->FindNick(target);
+               if ((find) && (find->registered == REG_ALL))
+               {
+                       ih.first = "*";
+                       ih.second = find->GetIPString();
+                       target = std::string("*@") + find->GetIPString();
+               }
+               else
+                       ih = ServerInstance->XLines->IdentSplit(target);
+
+               if (ih.first.empty())
+               {
+                       user->WriteNotice("*** Target not found.");
+                       return CMD_FAILURE;
+               }
+
+               InsaneBan::IPHostMatcher matcher;
+               if (InsaneBan::MatchesEveryone(ih.first+"@"+ih.second, matcher, user, "E", "hostmasks"))
+                       return CMD_FAILURE;
+
+               unsigned long duration;
+               if (!InspIRCd::Duration(parameters[1], duration))
+               {
+                       user->WriteNotice("*** Invalid duration for E-line.");
+                       return CMD_FAILURE;
+               }
+               ELine* el = new ELine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
+               if (ServerInstance->XLines->AddLine(el, user))
+               {
+                       if (!duration)
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s added permanent E-line for %s: %s", user->nick.c_str(), target.c_str(), parameters[2].c_str());
+                       }
+                       else
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s added timed E-line for %s, expires in %s (on %s): %s",
+                                       user->nick.c_str(), target.c_str(), InspIRCd::DurationString(duration).c_str(),
+                                       InspIRCd::TimeString(ServerInstance->Time() + duration).c_str(), parameters[2].c_str());
+                       }
+               }
+               else
+               {
+                       delete el;
+                       user->WriteNotice("*** E-line for " + target + " already exists.");
+               }
+       }
+       else
+       {
+               std::string reason;
+
+               if (ServerInstance->XLines->DelLine(target.c_str(), "E", reason, user))
+               {
+                       ServerInstance->SNO->WriteToSnoMask('x', "%s removed E-line on %s: %s", user->nick.c_str(), target.c_str(), reason.c_str());
+               }
+               else
+               {
+                       user->WriteNotice("*** E-line " + target + " not found on the list.");
+               }
+       }
+
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_xline/cmd_gline.cpp b/src/coremods/core_xline/cmd_gline.cpp
new file mode 100644 (file)
index 0000000..0506836
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 "xline.h"
+#include "core_xline.h"
+
+CommandGline::CommandGline(Module* parent)
+       : Command(parent, "GLINE", 1, 3)
+{
+       flags_needed = 'o';
+       syntax = "<user@host> [<duration> :<reason>]";
+}
+
+/** Handle /GLINE
+ */
+CmdResult CommandGline::Handle(User* user, const Params& parameters)
+{
+       std::string target = parameters[0];
+
+       if (parameters.size() >= 3)
+       {
+               IdentHostPair ih;
+               User* find = ServerInstance->FindNick(target);
+               if ((find) && (find->registered == REG_ALL))
+               {
+                       ih.first = "*";
+                       ih.second = find->GetIPString();
+                       target = std::string("*@") + find->GetIPString();
+               }
+               else
+                       ih = ServerInstance->XLines->IdentSplit(target);
+
+               if (ih.first.empty())
+               {
+                       user->WriteNotice("*** Target not found.");
+                       return CMD_FAILURE;
+               }
+
+               InsaneBan::IPHostMatcher matcher;
+               if (InsaneBan::MatchesEveryone(ih.first+"@"+ih.second, matcher, user, "G", "hostmasks"))
+                       return CMD_FAILURE;
+
+               else if (target.find('!') != std::string::npos)
+               {
+                       user->WriteNotice("*** G-line cannot operate on nick!user@host masks.");
+                       return CMD_FAILURE;
+               }
+
+               unsigned long duration;
+               if (!InspIRCd::Duration(parameters[1], duration))
+               {
+                       user->WriteNotice("*** Invalid duration for G-line.");
+                       return CMD_FAILURE;
+               }
+               GLine* gl = new GLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
+               if (ServerInstance->XLines->AddLine(gl, user))
+               {
+                       if (!duration)
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s added permanent G-line for %s: %s", user->nick.c_str(), target.c_str(), parameters[2].c_str());
+                       }
+                       else
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s added timed G-line for %s, expires in %s (on %s): %s",
+                                       user->nick.c_str(), target.c_str(), InspIRCd::DurationString(duration).c_str(),
+                                       InspIRCd::TimeString(ServerInstance->Time() + duration).c_str(), parameters[2].c_str());
+                       }
+
+                       ServerInstance->XLines->ApplyLines();
+               }
+               else
+               {
+                       delete gl;
+                       user->WriteNotice("** G-line for " + target + " already exists.");
+               }
+
+       }
+       else
+       {
+               std::string reason;
+
+               if (ServerInstance->XLines->DelLine(target.c_str(), "G", reason, user))
+               {
+                       ServerInstance->SNO->WriteToSnoMask('x', "%s removed G-line on %s: %s", user->nick.c_str(), target.c_str(), reason.c_str());
+               }
+               else
+               {
+                       user->WriteNotice("*** G-line " + target + " not found on the list.");
+               }
+       }
+
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_xline/cmd_kline.cpp b/src/coremods/core_xline/cmd_kline.cpp
new file mode 100644 (file)
index 0000000..391a70c
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 "xline.h"
+#include "core_xline.h"
+
+CommandKline::CommandKline(Module* parent)
+       : Command(parent, "KLINE", 1, 3)
+{
+       flags_needed = 'o';
+       syntax = "<user@host> [<duration> :<reason>]";
+}
+
+/** Handle /KLINE
+ */
+CmdResult CommandKline::Handle(User* user, const Params& parameters)
+{
+       std::string target = parameters[0];
+
+       if (parameters.size() >= 3)
+       {
+               IdentHostPair ih;
+               User* find = ServerInstance->FindNick(target);
+               if ((find) && (find->registered == REG_ALL))
+               {
+                       ih.first = "*";
+                       ih.second = find->GetIPString();
+                       target = std::string("*@") + find->GetIPString();
+               }
+               else
+                       ih = ServerInstance->XLines->IdentSplit(target);
+
+               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"))
+                       return CMD_FAILURE;
+
+               if (target.find('!') != std::string::npos)
+               {
+                       user->WriteNotice("*** K-line cannot operate on nick!user@host masks.");
+                       return CMD_FAILURE;
+               }
+
+               unsigned long duration;
+               if (!InspIRCd::Duration(parameters[1], duration))
+               {
+                       user->WriteNotice("*** Invalid duration for K-line.");
+                       return CMD_FAILURE;
+               }
+               KLine* kl = new KLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
+               if (ServerInstance->XLines->AddLine(kl,user))
+               {
+                       if (!duration)
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s added permanent K-line for %s: %s", user->nick.c_str(), target.c_str(), parameters[2].c_str());
+                       }
+                       else
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s added timed K-line for %s, expires in %s (on %s): %s",
+                                       user->nick.c_str(), target.c_str(), InspIRCd::DurationString(duration).c_str(),
+                                       InspIRCd::TimeString(ServerInstance->Time() + duration).c_str(), parameters[2].c_str());
+                       }
+
+                       ServerInstance->XLines->ApplyLines();
+               }
+               else
+               {
+                       delete kl;
+                       user->WriteNotice("*** K-line for " + target + " already exists.");
+               }
+       }
+       else
+       {
+               std::string reason;
+
+               if (ServerInstance->XLines->DelLine(target.c_str(), "K", reason, user))
+               {
+                       ServerInstance->SNO->WriteToSnoMask('x', "%s removed K-line on %s: %s", user->nick.c_str(), target.c_str(), reason.c_str());
+               }
+               else
+               {
+                       user->WriteNotice("*** K-line " + target + " not found on the list.");
+               }
+       }
+
+       return CMD_SUCCESS;
+}
diff --git a/src/coremods/core_xline/cmd_qline.cpp b/src/coremods/core_xline/cmd_qline.cpp
new file mode 100644 (file)
index 0000000..66ab405
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 "xline.h"
+#include "core_xline.h"
+
+CommandQline::CommandQline(Module* parent)
+       : Command(parent, "QLINE", 1, 3)
+{
+       flags_needed = 'o';
+       syntax = "<nickmask> [<duration> :<reason>]";
+}
+
+CmdResult CommandQline::Handle(User* user, const Params& parameters)
+{
+       if (parameters.size() >= 3)
+       {
+               NickMatcher matcher;
+               if (InsaneBan::MatchesEveryone(parameters[0], matcher, user, "Q", "nickmasks"))
+                       return CMD_FAILURE;
+
+               if (parameters[0].find('@') != std::string::npos || parameters[0].find('!') != std::string::npos || parameters[0].find('.') != std::string::npos)
+               {
+                       user->WriteNotice("*** A Q-line only bans a nick pattern, not a nick!user@host pattern.");
+                       return CMD_FAILURE;
+               }
+
+               unsigned long duration;
+               if (!InspIRCd::Duration(parameters[1], duration))
+               {
+                       user->WriteNotice("*** Invalid duration for Q-line.");
+                       return CMD_FAILURE;
+               }
+               QLine* ql = new QLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), parameters[0].c_str());
+               if (ServerInstance->XLines->AddLine(ql,user))
+               {
+                       if (!duration)
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s added permanent Q-line for %s: %s", user->nick.c_str(), parameters[0].c_str(), parameters[2].c_str());
+                       }
+                       else
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s added timed Q-line for %s, expires in %s (on %s): %s",
+                                       user->nick.c_str(), parameters[0].c_str(), InspIRCd::DurationString(duration).c_str(),
+                                       InspIRCd::TimeString(ServerInstance->Time() + duration).c_str(), parameters[2].c_str());
+                       }
+                       ServerInstance->XLines->ApplyLines();
+               }
+               else
+               {
+                       delete ql;
+                       user->WriteNotice("*** Q-line for " + parameters[0] + " already exists.");
+               }
+       }
+       else
+       {
+               std::string reason;
+
+               if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "Q", reason, user))
+               {
+                       ServerInstance->SNO->WriteToSnoMask('x', "%s removed Q-line on %s: %s", user->nick.c_str(), parameters[0].c_str(), reason.c_str());
+               }
+               else
+               {
+                       user->WriteNotice("*** Q-line " + parameters[0] + " not found on the list.");
+                       return CMD_FAILURE;
+               }
+       }
+
+       return CMD_SUCCESS;
+}
+
+bool CommandQline::NickMatcher::Check(User* user, const std::string& nick) const
+{
+       return InspIRCd::Match(user->nick, nick);
+}
diff --git a/src/coremods/core_xline/cmd_zline.cpp b/src/coremods/core_xline/cmd_zline.cpp
new file mode 100644 (file)
index 0000000..095f763
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2009 Matt Smith <dz@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * 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 "xline.h"
+#include "core_xline.h"
+
+CommandZline::CommandZline(Module* parent)
+       : Command(parent, "ZLINE", 1, 3)
+{
+       flags_needed = 'o';
+       syntax = "<ipmask> [<duration> :<reason>]";
+}
+
+CmdResult CommandZline::Handle(User* user, const Params& parameters)
+{
+       std::string target = parameters[0];
+
+       if (parameters.size() >= 3)
+       {
+               if (target.find('!') != std::string::npos)
+               {
+                       user->WriteNotice("*** You cannot include a nickname in a Z-line, a Z-line must ban only an IP mask.");
+                       return CMD_FAILURE;
+               }
+
+               User *u = ServerInstance->FindNick(target);
+
+               if ((u) && (u->registered == REG_ALL))
+               {
+                       target = u->GetIPString();
+               }
+
+               const char* ipaddr = target.c_str();
+
+               if (strchr(ipaddr,'@'))
+               {
+                       while (*ipaddr != '@')
+                               ipaddr++;
+                       ipaddr++;
+               }
+
+               IPMatcher matcher;
+               if (InsaneBan::MatchesEveryone(ipaddr, matcher, user, "Z", "ipmasks"))
+                       return CMD_FAILURE;
+
+               unsigned long duration;
+               if (!InspIRCd::Duration(parameters[1], duration))
+               {
+                       user->WriteNotice("*** Invalid duration for Z-line.");
+                       return CMD_FAILURE;
+               }
+               ZLine* zl = new ZLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ipaddr);
+               if (ServerInstance->XLines->AddLine(zl,user))
+               {
+                       if (!duration)
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s added permanent Z-line for %s: %s", user->nick.c_str(), ipaddr, parameters[2].c_str());
+                       }
+                       else
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s added timed Z-line for %s, expires in %s (on %s): %s",
+                                       user->nick.c_str(), ipaddr, InspIRCd::DurationString(duration).c_str(),
+                                       InspIRCd::TimeString(ServerInstance->Time() + duration).c_str(), parameters[2].c_str());
+                       }
+                       ServerInstance->XLines->ApplyLines();
+               }
+               else
+               {
+                       delete zl;
+                       user->WriteNotice("*** Z-line for " + std::string(ipaddr) + " already exists.");
+               }
+       }
+       else
+       {
+               std::string reason;
+
+               if (ServerInstance->XLines->DelLine(target.c_str(), "Z", reason, user))
+               {
+                       ServerInstance->SNO->WriteToSnoMask('x', "%s removed Z-line on %s: %s", user->nick.c_str(), target.c_str(), reason.c_str());
+               }
+               else
+               {
+                       user->WriteNotice("*** Z-line " + target + " not found on the list.");
+                       return CMD_FAILURE;
+               }
+       }
+
+       return CMD_SUCCESS;
+}
+
+bool CommandZline::IPMatcher::Check(User* user, const std::string& ip) const
+{
+       return InspIRCd::MatchCIDR(user->GetIPString(), ip, ascii_case_insensitive_map);
+}
diff --git a/src/coremods/core_xline/core_xline.cpp b/src/coremods/core_xline/core_xline.cpp
new file mode 100644 (file)
index 0000000..e8f045c
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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 "xline.h"
+#include "core_xline.h"
+
+bool InsaneBan::MatchesEveryone(const std::string& mask, MatcherBase& test, User* user, const char* bantype, const char* confkey)
+{
+       ConfigTag* insane = ServerInstance->Config->ConfValue("insane");
+
+       if (insane->getBool(confkey))
+               return false;
+
+       float itrigger = insane->getFloat("trigger", 95.5, 0.0, 100.0);
+
+       long matches = test.Run(mask);
+
+       if (!matches)
+               return false;
+
+       float percent = ((float)matches / (float)ServerInstance->Users->GetUsers().size()) * 100;
+       if (percent > itrigger)
+       {
+               ServerInstance->SNO->WriteToSnoMask('a', "\002WARNING\002: %s tried to set a %s-line mask of %s, which covers %.2f%% of the network!", user->nick.c_str(), bantype, mask.c_str(), percent);
+               return true;
+       }
+       return false;
+}
+
+bool InsaneBan::IPHostMatcher::Check(User* user, const std::string& mask) const
+{
+       return ((InspIRCd::MatchCIDR(user->MakeHost(), mask, ascii_case_insensitive_map)) ||
+                       (InspIRCd::MatchCIDR(user->MakeHostIP(), mask, ascii_case_insensitive_map)));
+}
+
+class CoreModXLine : public Module
+{
+       CommandEline cmdeline;
+       CommandGline cmdgline;
+       CommandKline cmdkline;
+       CommandQline cmdqline;
+       CommandZline cmdzline;
+
+ public:
+       CoreModXLine()
+               : cmdeline(this), cmdgline(this), cmdkline(this), cmdqline(this), cmdzline(this)
+       {
+       }
+
+       void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
+       {
+               if (user->quitting)
+                       return;
+
+               user->exempt = (ServerInstance->XLines->MatchesLine("E", user) != NULL);
+               user->CheckLines(true);
+       }
+
+       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);
+       }
+};
+
+MODULE_INIT(CoreModXLine)
diff --git a/src/coremods/core_xline/core_xline.h b/src/coremods/core_xline/core_xline.h
new file mode 100644 (file)
index 0000000..c607797
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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 "inspircd.h"
+
+class InsaneBan
+{
+ public:
+       class MatcherBase
+       {
+        public:
+               virtual long Run(const std::string& mask) = 0;
+       };
+
+       template <typename T>
+       class Matcher : public MatcherBase
+       {
+        public:
+               long Run(const std::string& mask) CXX11_OVERRIDE
+               {
+                       long matches = 0;
+                       const T* c = static_cast<T*>(this);
+                       const user_hash& users = ServerInstance->Users->GetUsers();
+                       for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+                       {
+                               if (c->Check(i->second, mask))
+                                       matches++;
+                       }
+                       return matches;
+               }
+       };
+
+       class IPHostMatcher : public Matcher<IPHostMatcher>
+       {
+        public:
+               bool Check(User* user, const std::string& mask) const;
+       };
+
+       /** Check if the given mask matches too many users according to the config, send an announcement if yes
+        * @param mask A mask to match against
+        * @param test The test that determines if a user matches the mask or not
+        * @param user A user whose nick will be included in the announcement if one is made
+        * @param bantype Type of the ban being set, will be used in the announcement if one is made
+        * @param confkey Name of the config key (inside the insane tag) which if false disables any checking
+        * @return True if the given mask matches too many users, false if not
+        */
+       static bool MatchesEveryone(const std::string& mask, MatcherBase& test, User* user, const char* bantype, const char* confkey);
+};
+
+/** Handle /ELINE.
+ */
+class CommandEline : public Command
+{
+ public:
+       /** Constructor for E-line.
+        */
+       CommandEline(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /GLINE.
+ */
+class CommandGline : public Command
+{
+ public:
+       /** Constructor for G-line.
+        */
+       CommandGline(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /KLINE.
+ */
+class CommandKline : public Command
+{
+ public:
+       /** Constructor for K-line.
+        */
+       CommandKline(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /QLINE.
+ */
+class CommandQline : public Command
+{
+       class NickMatcher : public InsaneBan::Matcher<NickMatcher>
+       {
+        public:
+               bool Check(User* user, const std::string& mask) const;
+       };
+
+ public:
+       /** Constructor for Q-line.
+        */
+       CommandQline(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+/** Handle /ZLINE.
+ */
+class CommandZline : public Command
+{
+       class IPMatcher : public InsaneBan::Matcher<IPMatcher>
+       {
+        public:
+               bool Check(User* user, const std::string& mask) const;
+       };
+
+ public:
+       /** Constructor for Z-line.
+        */
+       CommandZline(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(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
index 956ed34940f888b5a47d441c8eb914550648ec04..73f2def51c009822b517e1b5baef2fb81f48b98a 100644 (file)
@@ -21,7 +21,9 @@
 
 
 #include "inspircd.h"
+#ifdef INSPIRCD_ENABLE_RTTI
 #include <typeinfo>
+#endif
 
 void CullList::Apply()
 {
@@ -46,14 +48,18 @@ void CullList::Apply()
                classbase* c = list[i];
                if (gone.insert(c).second)
                {
-                       ServerInstance->Logs->Log("CULLLIST", DEBUG, "Deleting %s @%p", typeid(*c).name(),
+#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);
                }
                else
                {
-                       ServerInstance->Logs->Log("CULLLIST",DEBUG, "WARNING: Object @%p culled twice!",
+                       ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "WARNING: Object @%p culled twice!",
                                (void*)c);
                }
        }
@@ -65,7 +71,7 @@ void CullList::Apply()
        }
        if (list.size())
        {
-               ServerInstance->Logs->Log("CULLLIST",DEBUG, "WARNING: Objects added to cull list in a destructor");
+               ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "WARNING: Objects added to cull list in a destructor");
                Apply();
        }
 }
diff --git a/src/dns.cpp b/src/dns.cpp
deleted file mode 100644 (file)
index 3d3bc94..0000000
+++ /dev/null
@@ -1,1120 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2012 William Pitcock <nenolod@dereferenced.org>
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2006, 2009 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
- *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *   Copyright (C) 2005-2007 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/>.
- */
-
-
-/* $Core */
-
-/*
-dns.cpp - Nonblocking DNS functions.
-Very very loosely based on the firedns library,
-Copyright (C) 2002 Ian Gulliver. This file is no
-longer anything like firedns, there are many major
-differences between this code and the original.
-Please do not assume that firedns works like this,
-looks like this, walks like this or tastes like this.
-*/
-
-#ifndef _WIN32
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#else
-#include "inspircd_win32wrapper.h"
-#endif
-
-#include "inspircd.h"
-#include "socketengine.h"
-#include "configreader.h"
-#include "socket.h"
-
-#define DN_COMP_BITMASK        0xC000          /* highest 6 bits in a DN label header */
-
-/** Masks to mask off the responses we get from the DNSRequest methods
- */
-enum QueryInfo
-{
-       ERROR_MASK      = 0x10000       /* Result is an error */
-};
-
-/** Flags which can be ORed into a request or reply for different meanings
- */
-enum QueryFlags
-{
-       FLAGS_MASK_RD           = 0x01, /* Recursive */
-       FLAGS_MASK_TC           = 0x02,
-       FLAGS_MASK_AA           = 0x04, /* Authoritative */
-       FLAGS_MASK_OPCODE       = 0x78,
-       FLAGS_MASK_QR           = 0x80,
-       FLAGS_MASK_RCODE        = 0x0F, /* Request */
-       FLAGS_MASK_Z            = 0x70,
-       FLAGS_MASK_RA           = 0x80
-};
-
-
-/** Represents a dns resource record (rr)
- */
-struct ResourceRecord
-{
-       QueryType       type;           /* Record type */
-       unsigned int    rr_class;       /* Record class */
-       unsigned long   ttl;            /* Time to live */
-       unsigned int    rdlength;       /* Record length */
-};
-
-/** Represents a dns request/reply header, and its payload as opaque data.
- */
-class DNSHeader
-{
- public:
-       unsigned char   id[2];          /* Request id */
-       unsigned int    flags1;         /* Flags */
-       unsigned int    flags2;         /* Flags */
-       unsigned int    qdcount;
-       unsigned int    ancount;        /* Answer count */
-       unsigned int    nscount;        /* Nameserver count */
-       unsigned int    arcount;
-       unsigned char   payload[512];   /* Packet payload */
-};
-
-class DNSRequest
-{
- public:
-       unsigned char   id[2];          /* Request id */
-       unsigned char*  res;            /* Result processing buffer */
-       unsigned int    rr_class;       /* Request class */
-       QueryType       type;           /* Request type */
-       DNS*            dnsobj;         /* DNS caller (where we get our FD from) */
-       unsigned long   ttl;            /* Time to live */
-       std::string     orig;           /* Original requested name/ip */
-
-       DNSRequest(DNS* dns, int id, const std::string &original);
-       ~DNSRequest();
-       DNSInfo ResultIsReady(DNSHeader &h, unsigned length);
-       int SendRequests(const DNSHeader *header, const int length, QueryType qt);
-};
-
-class CacheTimer : public Timer
-{
- private:
-       DNS* dns;
- public:
-       CacheTimer(DNS* thisdns)
-               : Timer(5*60, ServerInstance->Time(), true), dns(thisdns) { }
-
-       virtual void Tick(time_t)
-       {
-               dns->PruneCache();
-       }
-};
-
-class RequestTimeout : public Timer
-{
-       DNSRequest* watch;
-       int watchid;
- public:
-       RequestTimeout(unsigned long n, DNSRequest* watching, int id) : Timer(n, ServerInstance->Time()), watch(watching), watchid(id)
-       {
-       }
-       ~RequestTimeout()
-       {
-               if (ServerInstance->Res)
-                       Tick(0);
-       }
-
-       void Tick(time_t)
-       {
-               if (ServerInstance->Res->requests[watchid] == watch)
-               {
-                       /* Still exists, whack it */
-                       if (ServerInstance->Res->Classes[watchid])
-                       {
-                               ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
-                               delete ServerInstance->Res->Classes[watchid];
-                               ServerInstance->Res->Classes[watchid] = NULL;
-                       }
-                       ServerInstance->Res->requests[watchid] = NULL;
-                       delete watch;
-               }
-       }
-};
-
-CachedQuery::CachedQuery(const std::string &res, QueryType qt, unsigned int ttl) : data(res), type(qt)
-{
-       if (ttl > 5*60)
-               ttl = 5*60;
-       expires = ServerInstance->Time() + ttl;
-}
-
-int CachedQuery::CalcTTLRemaining()
-{
-       int n = expires - ServerInstance->Time();
-       return (n < 0 ? 0 : n);
-}
-
-/* Allocate the processing buffer */
-DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
-{
-       /* hardening against overflow here:  make our work buffer twice the theoretical
-        * maximum size so that hostile input doesn't screw us over.
-        */
-       res = new unsigned char[sizeof(DNSHeader) * 2];
-       *res = 0;
-       orig = original;
-       RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
-       ServerInstance->Timers->AddTimer(RT); /* The timer manager frees this */
-}
-
-/* Deallocate the processing buffer */
-DNSRequest::~DNSRequest()
-{
-       delete[] res;
-}
-
-/** Fill a ResourceRecord class based on raw data input */
-inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
-{
-       rr->type = (QueryType)((input[0] << 8) + input[1]);
-       rr->rr_class = (input[2] << 8) + input[3];
-       rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
-       rr->rdlength = (input[8] << 8) + input[9];
-}
-
-/** Fill a DNSHeader class based on raw data input of a given length */
-inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
-{
-       header->id[0] = input[0];
-       header->id[1] = input[1];
-       header->flags1 = input[2];
-       header->flags2 = input[3];
-       header->qdcount = (input[4] << 8) + input[5];
-       header->ancount = (input[6] << 8) + input[7];
-       header->nscount = (input[8] << 8) + input[9];
-       header->arcount = (input[10] << 8) + input[11];
-       memcpy(header->payload,&input[12],length);
-}
-
-/** Empty a DNSHeader class out into raw data, ready for transmission */
-inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
-{
-       output[0] = header->id[0];
-       output[1] = header->id[1];
-       output[2] = header->flags1;
-       output[3] = header->flags2;
-       output[4] = header->qdcount >> 8;
-       output[5] = header->qdcount & 0xFF;
-       output[6] = header->ancount >> 8;
-       output[7] = header->ancount & 0xFF;
-       output[8] = header->nscount >> 8;
-       output[9] = header->nscount & 0xFF;
-       output[10] = header->arcount >> 8;
-       output[11] = header->arcount & 0xFF;
-       memcpy(&output[12],header->payload,length);
-}
-
-/** Send requests we have previously built down the UDP socket */
-int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
-{
-       ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
-
-       unsigned char payload[sizeof(DNSHeader)];
-
-       this->rr_class = 1;
-       this->type = qt;
-
-       DNS::EmptyHeader(payload,header,length);
-
-       if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, &(dnsobj->myserver.sa), sa_size(dnsobj->myserver)) != length+12)
-               return -1;
-
-       ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
-       return 0;
-}
-
-/** Add a query with a predefined header, and allocate an ID for it. */
-DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
-{
-       /* Is the DNS connection down? */
-       if (this->GetFd() == -1)
-               return NULL;
-
-       /* Create an id */
-       unsigned int tries = 0;
-       do {
-               id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
-               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
-                       id = -1;
-                       for (int i = 0; i < DNS::MAX_REQUEST_ID; i++)
-                       {
-                               if (!requests[i])
-                               {
-                                       id = i;
-                                       break;
-                               }
-                       }
-
-                       if (id == -1)
-                               throw ModuleException("DNS: All ids are in use");
-
-                       break;
-               }
-       } while (requests[id]);
-
-       DNSRequest* req = new DNSRequest(this, id, original);
-
-       header->id[0] = req->id[0] = id >> 8;
-       header->id[1] = req->id[1] = id & 0xFF;
-       header->flags1 = FLAGS_MASK_RD;
-       header->flags2 = 0;
-       header->qdcount = 1;
-       header->ancount = 0;
-       header->nscount = 0;
-       header->arcount = 0;
-
-       /* At this point we already know the id doesnt exist,
-        * so there needs to be no second check for the ::end()
-        */
-       requests[id] = req;
-
-       /* According to the C++ spec, new never returns NULL. */
-       return req;
-}
-
-int DNS::ClearCache()
-{
-       /* This ensures the buckets are reset to sane levels */
-       int rv = this->cache->size();
-       delete this->cache;
-       this->cache = new dnscache();
-       return rv;
-}
-
-int DNS::PruneCache()
-{
-       int n = 0;
-       dnscache* newcache = new dnscache();
-       for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
-               /* Dont include expired items (theres no point) */
-               if (i->second.CalcTTLRemaining())
-                       newcache->insert(*i);
-               else
-                       n++;
-
-       delete this->cache;
-       this->cache = newcache;
-       return n;
-}
-
-void DNS::Rehash()
-{
-       if (this->GetFd() > -1)
-       {
-               ServerInstance->SE->DelFd(this);
-               ServerInstance->SE->Shutdown(this, 2);
-               ServerInstance->SE->Close(this);
-               this->SetFd(-1);
-
-               /* Rehash the cache */
-               this->PruneCache();
-       }
-       else
-       {
-               /* Create initial dns cache */
-               this->cache = new dnscache();
-       }
-
-       irc::sockets::aptosa(ServerInstance->Config->DNSServer, DNS::QUERY_PORT, myserver);
-
-       /* Initialize mastersocket */
-       int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0);
-       this->SetFd(s);
-
-       /* Have we got a socket and is it nonblocking? */
-       if (this->GetFd() != -1)
-       {
-               ServerInstance->SE->SetReuse(s);
-               ServerInstance->SE->NonBlocking(s);
-               irc::sockets::sockaddrs bindto;
-               memset(&bindto, 0, sizeof(bindto));
-               bindto.sa.sa_family = myserver.sa.sa_family;
-               if (ServerInstance->SE->Bind(this->GetFd(), bindto) < 0)
-               {
-                       /* Failed to bind */
-                       ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error binding dns socket - hostnames will NOT resolve");
-                       ServerInstance->SE->Shutdown(this, 2);
-                       ServerInstance->SE->Close(this);
-                       this->SetFd(-1);
-               }
-               else if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
-               {
-                       ServerInstance->Logs->Log("RESOLVER",SPARSE,"Internal error starting DNS - hostnames will NOT resolve.");
-                       ServerInstance->SE->Shutdown(this, 2);
-                       ServerInstance->SE->Close(this);
-                       this->SetFd(-1);
-               }
-       }
-       else
-       {
-               ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error creating DNS socket - hostnames will NOT resolve");
-       }
-}
-
-/** Initialise the DNS UDP socket so that we can send requests */
-DNS::DNS()
-{
-       ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
-       /* Clear the Resolver class table */
-       memset(Classes,0,sizeof(Classes));
-
-       /* Clear the requests class table */
-       memset(requests,0,sizeof(requests));
-
-       /* DNS::Rehash() sets this to a valid ptr
-        */
-       this->cache = NULL;
-
-       /* Again, DNS::Rehash() sets this to a
-        * valid value
-        */
-       this->SetFd(-1);
-
-       /* Actually read the settings
-        */
-       this->Rehash();
-
-       this->PruneTimer = new CacheTimer(this);
-
-       ServerInstance->Timers->AddTimer(this->PruneTimer);
-}
-
-/** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
-int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
-{
-       short payloadpos = 0;
-       const char* tempchr, *tempchr2 = name;
-       unsigned short length;
-
-       /* split name up into labels, create query */
-       while ((tempchr = strchr(tempchr2,'.')) != NULL)
-       {
-               length = tempchr - tempchr2;
-               if (payloadpos + length + 1 > 507)
-                       return -1;
-               payload[payloadpos++] = length;
-               memcpy(&payload[payloadpos],tempchr2,length);
-               payloadpos += length;
-               tempchr2 = &tempchr[1];
-       }
-       length = strlen(tempchr2);
-       if (length)
-       {
-               if (payloadpos + length + 2 > 507)
-                       return -1;
-               payload[payloadpos++] = length;
-               memcpy(&payload[payloadpos],tempchr2,length);
-               payloadpos += length;
-               payload[payloadpos++] = 0;
-       }
-       if (payloadpos > 508)
-               return -1;
-       length = htons(rr);
-       memcpy(&payload[payloadpos],&length,2);
-       length = htons(rr_class);
-       memcpy(&payload[payloadpos + 2],&length,2);
-       return payloadpos + 4;
-}
-
-/** Start lookup of an hostname to an IP address */
-int DNS::GetIP(const char *name)
-{
-       DNSHeader h;
-       int id;
-       int length;
-
-       if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
-               return -1;
-
-       DNSRequest* req = this->AddQuery(&h, id, name);
-
-       if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
-               return -1;
-
-       return id;
-}
-
-/** Start lookup of an hostname to an IPv6 address */
-int DNS::GetIP6(const char *name)
-{
-       DNSHeader h;
-       int id;
-       int length;
-
-       if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
-               return -1;
-
-       DNSRequest* req = this->AddQuery(&h, id, name);
-
-       if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
-               return -1;
-
-       return id;
-}
-
-/** Start lookup of a cname to another name */
-int DNS::GetCName(const char *alias)
-{
-       DNSHeader h;
-       int id;
-       int length;
-
-       if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
-               return -1;
-
-       DNSRequest* req = this->AddQuery(&h, id, alias);
-
-       if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
-               return -1;
-
-       return id;
-}
-
-/** Start lookup of an IP address to a hostname */
-int DNS::GetNameForce(const char *ip, ForceProtocol fp)
-{
-       char query[128];
-       DNSHeader h;
-       int id;
-       int length;
-
-       if (fp == PROTOCOL_IPV6)
-       {
-               in6_addr i;
-               if (inet_pton(AF_INET6, ip, &i) > 0)
-               {
-                       DNS::MakeIP6Int(query, &i);
-               }
-               else
-               {
-                       ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv6 bad format for '%s'", ip);
-                       /* Invalid IP address */
-                       return -1;
-               }
-       }
-       else
-       {
-               in_addr i;
-               if (inet_aton(ip, &i))
-               {
-                       unsigned char* c = (unsigned char*)&i.s_addr;
-                       sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
-               }
-               else
-               {
-                       ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv4 bad format for '%s'", ip);
-                       /* Invalid IP address */
-                       return -1;
-               }
-       }
-
-       length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload);
-       if (length == -1)
-       {
-               ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't query '%s' using '%s' because it's too long", ip, query);
-               return -1;
-       }
-
-       DNSRequest* req = this->AddQuery(&h, id, ip);
-
-       if (!req)
-       {
-               ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't add query (resolver down?)");
-               return -1;
-       }
-
-       if (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)
-       {
-               ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't send (firewall?)");
-               return -1;
-       }
-
-       return id;
-}
-
-/** Build an ipv6 reverse domain from an in6_addr
- */
-void DNS::MakeIP6Int(char* query, const in6_addr *ip)
-{
-       const char* hex = "0123456789abcdef";
-       for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
-       {
-               if (index % 2)
-                       /* low nibble */
-                       *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
-               else
-                       /* high nibble */
-                       *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
-               *query++ = '.'; /* Seperator */
-       }
-       strcpy(query,"ip6.arpa"); /* Suffix the string */
-}
-
-/** Return the next id which is ready, and the result attached to it */
-DNSResult DNS::GetResult()
-{
-       /* Fetch dns query response and decide where it belongs */
-       DNSHeader header;
-       DNSRequest *req;
-       unsigned char buffer[sizeof(DNSHeader)];
-       irc::sockets::sockaddrs from;
-       memset(&from, 0, sizeof(from));
-       socklen_t x = sizeof(from);
-
-       int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);
-
-       /* Did we get the whole header? */
-       if (length < 12)
-       {
-               ServerInstance->Logs->Log("RESOLVER",DEBUG,"GetResult didn't get a full packet (len=%d)", length);
-               /* Nope - something screwed up. */
-               return DNSResult(-1,"",0,"");
-       }
-
-       /* Check wether the reply came from a different DNS
-        * server to the one we sent it to, or the source-port
-        * is not 53.
-        * A user could in theory still spoof dns packets anyway
-        * but this is less trivial than just sending garbage
-        * to the server, which is possible without this check.
-        *
-        * -- Thanks jilles for pointing this one out.
-        */
-       if (from != myserver)
-       {
-               std::string server1 = from.str();
-               std::string server2 = myserver.str();
-               ServerInstance->Logs->Log("RESOLVER",DEBUG,"Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
-                       server1.c_str(), server2.c_str());
-               return DNSResult(-1,"",0,"");
-       }
-
-       /* Put the read header info into a header class */
-       DNS::FillHeader(&header,buffer,length - 12);
-
-       /* Get the id of this request.
-        * Its a 16 bit value stored in two char's,
-        * so we use logic shifts to create the value.
-        */
-       unsigned long this_id = header.id[1] + (header.id[0] << 8);
-
-       /* Do we have a pending request matching this id? */
-       if (!requests[this_id])
-       {
-               /* Somehow we got a DNS response for a request we never made... */
-               ServerInstance->Logs->Log("RESOLVER",DEBUG,"Hmm, got a result that we didn't ask for (id=%lx). Ignoring.", this_id);
-               return DNSResult(-1,"",0,"");
-       }
-       else
-       {
-               /* Remove the query from the list of pending queries */
-               req = requests[this_id];
-               requests[this_id] = NULL;
-       }
-
-       /* Inform the DNSRequest class that it has a result to be read.
-        * When its finished it will return a DNSInfo which is a pair of
-        * unsigned char* resource record data, and an error message.
-        */
-       DNSInfo data = req->ResultIsReady(header, length);
-       std::string resultstr;
-
-       /* Check if we got a result, if we didnt, its an error */
-       if (data.first == NULL)
-       {
-               /* An error.
-                * Mask the ID with the value of ERROR_MASK, so that
-                * the dns_deal_with_classes() function knows that its
-                * an error response and needs to be treated uniquely.
-                * Put the error message in the second field.
-                */
-               std::string ro = req->orig;
-               delete req;
-               return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
-       }
-       else
-       {
-               unsigned long ttl = req->ttl;
-               char formatted[128];
-
-               /* Forward lookups come back as binary data. We must format them into ascii */
-               switch (req->type)
-               {
-                       case DNS_QUERY_A:
-                               snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
-                               resultstr = formatted;
-                       break;
-
-                       case DNS_QUERY_AAAA:
-                       {
-                               if (!inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted)))
-                               {
-                                       std::string ro = req->orig;
-                                       delete req;
-                                       return DNSResult(this_id | ERROR_MASK, "inet_ntop() failed", 0, ro);
-                               }
-
-                               resultstr = formatted;
-
-                               /* Special case. Sending ::1 around between servers
-                                * and to clients is dangerous, because the : on the
-                                * start makes the client or server interpret the IP
-                                * as the last parameter on the line with a value ":1".
-                                */
-                               if (*formatted == ':')
-                                       resultstr.insert(0, "0");
-                       }
-                       break;
-
-                       case DNS_QUERY_CNAME:
-                               /* Identical handling to PTR */
-
-                       case DNS_QUERY_PTR:
-                       {
-                               /* Reverse lookups just come back as char* */
-                               resultstr = std::string((const char*)data.first);
-                               if (resultstr.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-") != std::string::npos)
-                               {
-                                       std::string ro = req->orig;
-                                       delete req;
-                                       return DNSResult(this_id | ERROR_MASK, "Invalid char(s) in reply", 0, ro);
-                               }
-                       }
-                       break;
-
-                       default:
-                       break;
-               }
-
-               /* Build the reply with the id and hostname/ip in it */
-               std::string ro = req->orig;
-               DNSResult result = DNSResult(this_id,resultstr,ttl,ro,req->type);
-               delete req;
-               return result;
-       }
-}
-
-/** A result is ready, process it */
-DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, unsigned length)
-{
-       unsigned i = 0, o;
-       int q = 0;
-       int curanswer;
-       ResourceRecord rr;
-       unsigned short ptr;
-
-       /* This is just to keep _FORTIFY_SOURCE happy */
-       rr.type = DNS_QUERY_NONE;
-       rr.rdlength = 0;
-       rr.ttl = 1;     /* GCC is a whiney bastard -- see the XXX below. */
-       rr.rr_class = 0; /* Same for VC++ */
-
-       if (!(header.flags1 & FLAGS_MASK_QR))
-               return std::make_pair((unsigned char*)NULL,"Not a query result");
-
-       if (header.flags1 & FLAGS_MASK_OPCODE)
-               return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
-
-       if (header.flags2 & FLAGS_MASK_RCODE)
-               return std::make_pair((unsigned char*)NULL,"Domain name not found");
-
-       if (header.ancount < 1)
-               return std::make_pair((unsigned char*)NULL,"No resource records returned");
-
-       /* Subtract the length of the header from the length of the packet */
-       length -= 12;
-
-       while ((unsigned int)q < header.qdcount && i < length)
-       {
-               if (header.payload[i] > 63)
-               {
-                       i += 6;
-                       q++;
-               }
-               else
-               {
-                       if (header.payload[i] == 0)
-                       {
-                               q++;
-                               i += 5;
-                       }
-                       else i += header.payload[i] + 1;
-               }
-       }
-       curanswer = 0;
-       while ((unsigned)curanswer < header.ancount)
-       {
-               q = 0;
-               while (q == 0 && i < length)
-               {
-                       if (header.payload[i] > 63)
-                       {
-                               i += 2;
-                               q = 1;
-                       }
-                       else
-                       {
-                               if (header.payload[i] == 0)
-                               {
-                                       i++;
-                                       q = 1;
-                               }
-                               else i += header.payload[i] + 1; /* skip length and label */
-                       }
-               }
-               if (static_cast<int>(length - i) < 10)
-                       return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
-
-               /* XXX: We actually initialise 'rr' here including its ttl field */
-               DNS::FillResourceRecord(&rr,&header.payload[i]);
-
-               i += 10;
-               ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver: rr.type is %d and this.type is %d rr.class %d this.class %d", rr.type, this->type, rr.rr_class, this->rr_class);
-               if (rr.type != this->type)
-               {
-                       curanswer++;
-                       i += rr.rdlength;
-                       continue;
-               }
-               if (rr.rr_class != this->rr_class)
-               {
-                       curanswer++;
-                       i += rr.rdlength;
-                       continue;
-               }
-               break;
-       }
-       if ((unsigned int)curanswer == header.ancount)
-               return std::make_pair((unsigned char*)NULL,"No A, AAAA or PTR type answers (" + ConvToStr(header.ancount) + " answers)");
-
-       if (i + rr.rdlength > (unsigned int)length)
-               return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
-
-       if (rr.rdlength > 1023)
-               return std::make_pair((unsigned char*)NULL,"Resource record too large");
-
-       this->ttl = rr.ttl;
-
-       switch (rr.type)
-       {
-               /*
-                * CNAME and PTR are compressed.  We need to decompress them.
-                */
-               case DNS_QUERY_CNAME:
-               case DNS_QUERY_PTR:
-               {
-                       unsigned short lowest_pos = length;
-                       o = 0;
-                       q = 0;
-                       while (q == 0 && i < length && o + 256 < 1023)
-                       {
-                               /* DN label found (byte over 63) */
-                               if (header.payload[i] > 63)
-                               {
-                                       memcpy(&ptr,&header.payload[i],2);
-
-                                       i = ntohs(ptr);
-
-                                       /* check that highest two bits are set. if not, we've been had */
-                                       if ((i & DN_COMP_BITMASK) != DN_COMP_BITMASK)
-                                               return std::make_pair((unsigned char *) NULL, "DN label decompression header is bogus");
-
-                                       /* mask away the two highest bits. */
-                                       i &= ~DN_COMP_BITMASK;
-
-                                       /* and decrease length by 12 bytes. */
-                                       i -= 12;
-
-                                       if (i >= lowest_pos)
-                                               return std::make_pair((unsigned char *) NULL, "Invalid decompression pointer");
-                                       lowest_pos = i;
-                               }
-                               else
-                               {
-                                       if (header.payload[i] == 0)
-                                       {
-                                               q = 1;
-                                       }
-                                       else
-                                       {
-                                               res[o] = 0;
-                                               if (o != 0)
-                                                       res[o++] = '.';
-
-                                               if (o + header.payload[i] > sizeof(DNSHeader))
-                                                       return std::make_pair((unsigned char *) NULL, "DN label decompression is impossible -- malformed/hostile packet?");
-
-                                               memcpy(&res[o], &header.payload[i + 1], header.payload[i]);
-                                               o += header.payload[i];
-                                               i += header.payload[i] + 1;
-                                       }
-                               }
-                       }
-                       res[o] = 0;
-               }
-               break;
-               case DNS_QUERY_AAAA:
-                       if (rr.rdlength != sizeof(struct in6_addr))
-                               return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 16 bytes for an ipv6 entry -- malformed/hostile packet?");
-
-                       memcpy(res,&header.payload[i],rr.rdlength);
-                       res[rr.rdlength] = 0;
-               break;
-               case DNS_QUERY_A:
-                       if (rr.rdlength != sizeof(struct in_addr))
-                               return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 4 bytes for an ipv4 entry -- malformed/hostile packet?");
-
-                       memcpy(res,&header.payload[i],rr.rdlength);
-                       res[rr.rdlength] = 0;
-               break;
-               default:
-                       return std::make_pair((unsigned char *) NULL, "don't know how to handle undefined type (" + ConvToStr(rr.type) + ") -- rejecting");
-               break;
-       }
-       return std::make_pair(res,"No error");
-}
-
-/** Close the master socket */
-DNS::~DNS()
-{
-       ServerInstance->SE->Shutdown(this, 2);
-       ServerInstance->SE->Close(this);
-       ServerInstance->Timers->DelTimer(this->PruneTimer);
-       if (cache)
-               delete cache;
-}
-
-CachedQuery* DNS::GetCache(const std::string &source)
-{
-       dnscache::iterator x = cache->find(source.c_str());
-       if (x != cache->end())
-               return &(x->second);
-       else
-               return NULL;
-}
-
-void DNS::DelCache(const std::string &source)
-{
-       cache->erase(source.c_str());
-}
-
-void Resolver::TriggerCachedResult()
-{
-       if (CQ)
-               OnLookupComplete(CQ->data, time_left, true);
-}
-
-/** High level abstraction of dns used by application at large */
-Resolver::Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator) : Creator(creator), input(source), querytype(qt)
-{
-       ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
-       cached = false;
-
-       CQ = ServerInstance->Res->GetCache(source);
-       if (CQ)
-       {
-               time_left = CQ->CalcTTLRemaining();
-               if (!time_left)
-               {
-                       ServerInstance->Res->DelCache(source);
-               }
-               else if (CQ->type == qt)
-               {
-                       cached = true;
-                       return;
-               }
-               CQ = NULL;
-       }
-
-       switch (querytype)
-       {
-               case DNS_QUERY_A:
-                       this->myid = ServerInstance->Res->GetIP(source.c_str());
-               break;
-
-               case DNS_QUERY_PTR4:
-                       querytype = DNS_QUERY_PTR;
-                       this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
-               break;
-
-               case DNS_QUERY_PTR6:
-                       querytype = DNS_QUERY_PTR;
-                       this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
-               break;
-
-               case DNS_QUERY_AAAA:
-                       this->myid = ServerInstance->Res->GetIP6(source.c_str());
-               break;
-
-               case DNS_QUERY_CNAME:
-                       this->myid = ServerInstance->Res->GetCName(source.c_str());
-               break;
-
-               default:
-                       ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request with unknown query type %d", querytype);
-                       this->myid = -1;
-               break;
-       }
-       if (this->myid == -1)
-       {
-               throw ModuleException("Resolver: Couldn't get an id to make a request");
-       }
-       else
-       {
-               ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
-       }
-}
-
-/** Called when an error occurs */
-void Resolver::OnError(ResolverError, const std::string&)
-{
-       /* Nothing in here */
-}
-
-/** Destroy a resolver */
-Resolver::~Resolver()
-{
-       /* Nothing here (yet) either */
-}
-
-/** Get the request id associated with this class */
-int Resolver::GetId()
-{
-       return this->myid;
-}
-
-Module* Resolver::GetCreator()
-{
-       return this->Creator;
-}
-
-/** Process a socket read event */
-void DNS::HandleEvent(EventType, int)
-{
-       /* Fetch the id and result of the next available packet */
-       DNSResult res(0,"",0,"");
-       res.id = 0;
-       ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
-
-       res = this->GetResult();
-
-       ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
-
-       /* Is there a usable request id? */
-       if (res.id != -1)
-       {
-               /* Its an error reply */
-               if (res.id & ERROR_MASK)
-               {
-                       /* Mask off the error bit */
-                       res.id -= ERROR_MASK;
-                       /* Marshall the error to the correct class */
-                       if (Classes[res.id])
-                       {
-                               if (ServerInstance && ServerInstance->stats)
-                                       ServerInstance->stats->statsDnsBad++;
-                               Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
-                               delete Classes[res.id];
-                               Classes[res.id] = NULL;
-                       }
-                       return;
-               }
-               else
-               {
-                       /* It is a non-error result, marshall the result to the correct class */
-                       if (Classes[res.id])
-                       {
-                               if (ServerInstance && ServerInstance->stats)
-                                       ServerInstance->stats->statsDnsGood++;
-
-                               if (!this->GetCache(res.original.c_str()))
-                               {
-                                       if (cache->size() >= MAX_CACHE_SIZE)
-                                               cache->clear();
-                                       this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.type, res.ttl)));
-                               }
-
-                               Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
-                               delete Classes[res.id];
-                               Classes[res.id] = NULL;
-                       }
-               }
-
-               if (ServerInstance && ServerInstance->stats)
-                       ServerInstance->stats->statsDns++;
-       }
-}
-
-/** Add a derived Resolver to the working set */
-bool DNS::AddResolverClass(Resolver* r)
-{
-       ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
-       /* Check the pointers validity and the id's validity */
-       if ((r) && (r->GetId() > -1))
-       {
-               /* Check the slot isnt already occupied -
-                * This should NEVER happen unless we have
-                * a severely broken DNS server somewhere
-                */
-               if (!Classes[r->GetId()])
-               {
-                       /* Set up the pointer to the class */
-                       Classes[r->GetId()] = r;
-                       return true;
-               }
-       }
-
-       /* Pointer or id not valid, or duplicate id.
-        * Free the item and return
-        */
-       delete r;
-       return false;
-}
-
-void DNS::CleanResolvers(Module* module)
-{
-       for (int i = 0; i < MAX_REQUEST_ID; i++)
-       {
-               if (Classes[i])
-               {
-                       if (Classes[i]->GetCreator() == module)
-                       {
-                               Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");
-                               delete Classes[i];
-                               Classes[i] = NULL;
-                       }
-               }
-       }
-}
index d111da54847e2e799b300bf30100e88c54a90b94..f2acdd51c6c11736ca62f42bf8bc53a85b4b64b6 100644 (file)
@@ -22,7 +22,7 @@
 
 
 #include "inspircd.h"
-#include "dynamic.h"
+
 #ifndef _WIN32
 #include <dlfcn.h>
 #else
@@ -54,36 +54,30 @@ DLLManager::~DLLManager()
                dlclose(h);
 }
 
-union init_t {
-       void* vptr;
-       Module* (*fptr)();
-};
-
 Module* DLLManager::CallInit()
 {
-       if (!h)
-               return NULL;
-
-       init_t initfn;
-       initfn.vptr = dlsym(h, MODULE_INIT_STR);
-       if (!initfn.vptr)
+       union
        {
-               RetrieveLastError();
+               void* vptr;
+               Module* (*fptr)();
+       };
+
+       vptr = GetSymbol(MODULE_INIT_STR);
+       if (!vptr)
                return NULL;
-       }
 
-       return (*initfn.fptr)();
+       return (*fptr)();
 }
 
-std::string DLLManager::GetVersion()
+void* DLLManager::GetSymbol(const char* name)
 {
-       if (!h)
-               return "";
+       return h ? dlsym(h, name) : NULL;
+}
 
-       const char* srcver = (char*)dlsym(h, "inspircd_src_version");
-       if (srcver)
-               return srcver;
-       return "Unversioned module";
+std::string DLLManager::GetVersion()
+{
+       const char* srcver = static_cast<const char*>(GetSymbol("inspircd_src_version"));
+       return srcver ? srcver : "";
 }
 
 void DLLManager::RetrieveLastError()
index 0575256d08c8261c8b7b905dc283e2cd9c0c6cf5..5786758da5f691907187efda8507ed6732a07f39 100644 (file)
  */
 
 
-/* $Core */
-
 #include "inspircd.h"
 #include <fstream>
-#include "socketengine.h"
-#include "filelogger.h"
 
-FileLogStream::FileLogStream(int loglevel, FileWriter *fw)
-       : LogStream(loglevel), f(fw)
+FileLogStream::FileLogStream(LogLevel loglevel, FileWriter *fw) : LogStream(loglevel), f(fw)
 {
        ServerInstance->Logs->AddLoggerRef(f);
 }
@@ -38,9 +33,9 @@ FileLogStream::~FileLogStream()
        ServerInstance->Logs->DelLoggerRef(f);
 }
 
-void FileLogStream::OnLog(int loglevel, const std::string &type, const std::string &text)
+void FileLogStream::OnLog(LogLevel loglevel, const std::string &type, const std::string &text)
 {
-       static char TIMESTR[26];
+       static std::string TIMESTR;
        static time_t LAST = 0;
 
        if (loglevel < this->loglvl)
@@ -50,14 +45,9 @@ void FileLogStream::OnLog(int loglevel, const std::string &type, const std::stri
 
        if (ServerInstance->Time() != LAST)
        {
-               time_t local = ServerInstance->Time();
-               struct tm *timeinfo = localtime(&local);
-
-               strlcpy(TIMESTR,asctime(timeinfo),26);
-               TIMESTR[24] = ':';
+               TIMESTR = InspIRCd::TimeString(ServerInstance->Time());
                LAST = ServerInstance->Time();
        }
 
-       std::string out = std::string(TIMESTR) + " " + text.c_str() + "\n";
-       this->f->WriteLogLine(out);
+       this->f->WriteLogLine(TIMESTR + " " + type + ": " + text + "\n");
 }
diff --git a/src/fileutils.cpp b/src/fileutils.cpp
new file mode 100644 (file)
index 0000000..731e4ea
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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/>.
+ */
+
+
+#include "inspircd.h"
+
+#include <fstream>
+
+FileReader::FileReader(const std::string& filename)
+{
+       Load(filename);
+}
+
+void FileReader::Load(const std::string& filename)
+{
+       // If the file is stored in the file cache then we used that version instead.
+       ConfigFileCache::const_iterator it = ServerInstance->Config->Files.find(filename);
+       if (it != ServerInstance->Config->Files.end())
+       {
+               this->lines = it->second;
+       }
+       else
+       {
+               const std::string realName = ServerInstance->Config->Paths.PrependConfig(filename);
+               lines.clear();
+
+               std::ifstream stream(realName.c_str());
+               if (!stream.is_open())
+                       throw CoreException(filename + " does not exist or is not readable!");
+
+               std::string line;
+               while (std::getline(stream, line))
+               {
+                       lines.push_back(line);
+                       totalSize += line.size() + 2;
+               }
+
+               stream.close();
+       }
+}
+
+std::string FileReader::GetString() const
+{
+       std::string buffer;
+       for (file_cache::const_iterator it = this->lines.begin(); it != this->lines.end(); ++it)
+       {
+               buffer.append(*it);
+               buffer.append("\r\n");
+       }
+       return buffer;
+}
+
+std::string FileSystem::ExpandPath(const std::string& base, const std::string& fragment)
+{
+       // The fragment is an absolute path, don't modify it.
+       if (fragment[0] == '/' || FileSystem::StartsWithWindowsDriveLetter(fragment))
+               return fragment;
+
+       return base + '/' + fragment;
+}
+
+bool FileSystem::FileExists(const std::string& file)
+{
+       struct stat sb;
+       if (stat(file.c_str(), &sb) == -1)
+               return false;
+
+       if ((sb.st_mode & S_IFDIR) > 0)
+               return false;
+
+       return !access(file.c_str(), F_OK);
+}
+
+std::string FileSystem::GetFileName(const std::string& name)
+{
+#ifdef _WIN32
+       size_t pos = name.find_last_of("\\/");
+#else
+       size_t pos = name.rfind('/');
+#endif
+       return pos == std::string::npos ? name : name.substr(++pos);
+}
+
+bool FileSystem::StartsWithWindowsDriveLetter(const std::string& path)
+{
+       return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
+}
index e0347421b29d01fb89e7cedd13d89aacde04fb74..4fee9fd55d2925be015b014296ae8ddfafde6caa 100644 (file)
  */
 
 
-/* $Core */
-
 #include "inspircd.h"
-#include "hashcomp.h"
-#include "hash_map.h"
 
 /******************************************************
  *
@@ -35,7 +31,7 @@
  * scene spend a lot of time debating (arguing) about
  * the best way to write hash functions to hash irc
  * nicknames, channels etc.
- * We are lucky as C++ developers as hash_map does
+ * We are lucky as C++ developers as unordered_map does
  * a lot of this for us. It does intellegent memory
  * requests, bucketing, search functions, insertion
  * and deletion etc. All we have to do is write some
  *
  ******************************************************/
 
-/** A mapping of uppercase to lowercase, including scandinavian
- * 'oddities' as specified by RFC1459, e.g. { -> [, and | -> \
- */
-unsigned const char rfc_case_insensitive_map[256] = {
-       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,                                   /* 0-19 */
-       20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,                         /* 20-39 */
-       40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,                         /* 40-59 */
-       60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,             /* 60-79 */
-       112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 94, 95, 96, 97, 98, 99,           /* 80-99 */
-       100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,     /* 100-119 */
-       120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,     /* 120-139 */
-       140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,     /* 140-159 */
-       160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,     /* 160-179 */
-       180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,     /* 180-199 */
-       200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,     /* 200-219 */
-       220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,     /* 220-239 */
-       240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255                          /* 240-255 */
-};
 
-/** Case insensitive map, ASCII rules.
- * That is;
- * [ != {, but A == a.
+/**
+ * A case insensitive mapping of characters from upper case to lower case for
+ * the ASCII character set.
  */
 unsigned const char ascii_case_insensitive_map[256] = {
-        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,                                   /* 0-19 */
-        20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,                         /* 20-39 */
-        40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,                         /* 40-59 */
-        60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,             /* 60-79 */
-        112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,              /* 80-99 */
-        100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,     /* 100-119 */
-        120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,     /* 120-139 */
-        140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,     /* 140-159 */
-        160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,     /* 160-179 */
-        180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,     /* 180-199 */
-        200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,     /* 200-219 */
-        220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,     /* 220-239 */
-        240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255                          /* 240-255 */
+       0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   // 0-9
+       10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  // 10-19
+       20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  // 20-29
+       30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  // 30-39
+       40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  // 40-49
+       50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  // 50-59
+       60,  61,  62,  63,  64,  97,  98,  99,  100, 101, // 60-69
+       102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 70-79
+       112, 113, 114, 115, 116, 117, 118, 119, 120, 121, // 80-89
+       122, 91,  92,  93,  94,  95,  96,  97,  98,  99,  // 90-99
+       100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 100-109
+       110, 111, 112, 113, 114, 115, 116, 117, 118, 119, // 110-119
+       120, 121, 122, 123, 124, 125, 126, 127, 128, 129, // 120-129
+       130, 131, 132, 133, 134, 135, 136, 137, 138, 139, // 130-139
+       140, 141, 142, 143, 144, 145, 146, 147, 148, 149, // 140-149
+       150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 150-159
+       160, 161, 162, 163, 164, 165, 166, 167, 168, 169, // 160-169
+       170, 171, 172, 173, 174, 175, 176, 177, 178, 179, // 170-179
+       180, 181, 182, 183, 184, 185, 186, 187, 188, 189, // 180-189
+       190, 191, 192, 193, 194, 195, 196, 197, 198, 199, // 190-199
+       200, 201, 202, 203, 204, 205, 206, 207, 208, 209, // 200-209
+       210, 211, 212, 213, 214, 215, 216, 217, 218, 219, // 210-219
+       220, 221, 222, 223, 224, 225, 226, 227, 228, 229, // 220-229
+       230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 230-249
+       240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
+       250, 251, 252, 253, 254, 255,                     // 250-255
 };
 
-/** Case sensitive map.
- * Can technically also be used for ASCII case sensitive comparisons, as [ != {, etc.
- */
-unsigned const char rfc_case_sensitive_map[256] = {
-       0, 1, 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, 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, 54, 55, 56, 57, 58, 59, 60,
-        61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
-        81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
-        101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
-        121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
-        141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
-        161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
-        181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
-        201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
-        221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
-        241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
-};
 
-/* convert a string to lowercase. Note following special circumstances
- * taken from RFC 1459. Many "official" server branches still hold to this
- * rule so i will too;
- *
- *  Because of IRC's scandanavian origin, the characters {}| are
- *  considered to be the lower case equivalents of the characters []\,
- *  respectively. This is a critical issue when determining the
- *  equivalence of two nicknames.
- */
-void nspace::strlower(char *n)
-{
-       if (n)
-       {
-               for (char* t = n; *t; t++)
-                       *t = national_case_insensitive_map[(unsigned char)*t];
-       }
-}
-
-#ifdef HASHMAP_DEPRECATED
-       size_t CoreExport nspace::insensitive::operator()(const std::string &s) const
-#else
-       size_t nspace::hash<std::string>::operator()(const std::string &s) const
-#endif
 
-{
-       /* XXX: NO DATA COPIES! :)
-        * The hash function here is practically
-        * a copy of the one in STL's hash_fun.h,
-        * only with *x replaced with national_case_insensitive_map[*x].
-        * This avoids a copy to use hash<const char*>
-        */
-       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;
-}
-
-
-size_t CoreExport irc::hash::operator()(const irc::string &s) const
-{
-       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;
-}
+/**
+ * A case insensitive mapping of characters from upper case to lower case for
+ * the character set of RFC 1459. This is identical to ASCII with the small
+ * exception of {}| being considered to be the lower case equivalents of the
+ * characters []\ respectively.
+ */
+unsigned const char rfc_case_insensitive_map[256] = {
+       0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   // 0-9
+       10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  // 10-19
+       20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  // 20-29
+       30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  // 30-39
+       40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  // 40-49
+       50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  // 50-59
+       60,  61,  62,  63,  64,  97,  98,  99,  100, 101, // 60-69
+       102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 70-79
+       112, 113, 114, 115, 116, 117, 118, 119, 120, 121, // 80-89
+       122, 123, 124, 125, 94,  95,  96,  97,  98,  99,  // 90-99
+       100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 100-109
+       110, 111, 112, 113, 114, 115, 116, 117, 118, 119, // 110-119
+       120, 121, 122, 123, 124, 125, 126, 127, 128, 129, // 120-129
+       130, 131, 132, 133, 134, 135, 136, 137, 138, 139, // 130-139
+       140, 141, 142, 143, 144, 145, 146, 147, 148, 149, // 140-149
+       150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 150-159
+       160, 161, 162, 163, 164, 165, 166, 167, 168, 169, // 160-169
+       170, 171, 172, 173, 174, 175, 176, 177, 178, 179, // 170-179
+       180, 181, 182, 183, 184, 185, 186, 187, 188, 189, // 180-189
+       190, 191, 192, 193, 194, 195, 196, 197, 198, 199, // 190-199
+       200, 201, 202, 203, 204, 205, 206, 207, 208, 209, // 200-209
+       210, 211, 212, 213, 214, 215, 216, 217, 218, 219, // 210-219
+       220, 221, 222, 223, 224, 225, 226, 227, 228, 229, // 220-229
+       230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 230-239
+       240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
+       250, 251, 252, 253, 254, 255,                     // 250-255
+};
 
-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();
@@ -165,315 +128,173 @@ bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2)
        return (national_case_insensitive_map[*n1] == national_case_insensitive_map[*n2]);
 }
 
-/******************************************************
- *
- * This is the implementation of our special irc::string
- * class which is a case-insensitive equivalent to
- * std::string which is not only case-insensitive but
- * can also do scandanavian comparisons, e.g. { = [, etc.
- *
- * This class depends on the const array 'national_case_insensitive_map'.
- *
- ******************************************************/
-
-bool irc::irc_char_traits::eq(char c1st, char c2nd)
+size_t irc::find(const std::string& haystack, const std::string& needle)
 {
-       return national_case_insensitive_map[(unsigned char)c1st] == national_case_insensitive_map[(unsigned char)c2nd];
-}
-
-bool irc::irc_char_traits::ne(char c1st, char c2nd)
-{
-       return national_case_insensitive_map[(unsigned char)c1st] != national_case_insensitive_map[(unsigned char)c2nd];
-}
-
-bool irc::irc_char_traits::lt(char c1st, char c2nd)
-{
-       return national_case_insensitive_map[(unsigned char)c1st] < national_case_insensitive_map[(unsigned char)c2nd];
-}
+       // The haystack can't contain the needle if it is smaller than it.
+       if (needle.length() > haystack.length())
+               return std::string::npos;
 
-int irc::irc_char_traits::compare(const char* str1, const char* str2, size_t n)
-{
-       for(unsigned int i = 0; i < n; i++)
+       // The inner loop checks the characters between haystack_last and the end of the haystack.
+       size_t haystack_last = haystack.length() - needle.length();
+       for (size_t hpos = 0; hpos <= haystack_last; ++hpos)
        {
-               if(national_case_insensitive_map[(unsigned char)*str1] > national_case_insensitive_map[(unsigned char)*str2])
-                       return 1;
-
-               if(national_case_insensitive_map[(unsigned char)*str1] < national_case_insensitive_map[(unsigned char)*str2])
-                       return -1;
-
-               if(*str1 == 0 || *str2 == 0)
-                       return 0;
+               // Check for the needle at the current haystack position.
+               bool found = true;
+               for (size_t npos = 0; npos < needle.length(); ++npos)
+               {
+                       if (national_case_insensitive_map[(unsigned char)needle[npos]] != national_case_insensitive_map[(unsigned char)haystack[hpos + npos]])
+                       {
+                               // Uh-oh, characters at the current haystack position don't match.
+                               found = false;
+                               break;
+                       }
+               }
 
-               str1++;
-               str2++;
+               // The entire needle was found in the haystack!
+               if (found)
+                       return hpos;
        }
-       return 0;
-}
 
-const char* irc::irc_char_traits::find(const char* s1, int  n, char c)
-{
-       while(n-- > 0 && national_case_insensitive_map[(unsigned char)*s1] != national_case_insensitive_map[(unsigned char)c])
-               s1++;
-       return (n >= 0) ? s1 : NULL;
+       // We didn't find anything.
+       return std::string::npos;
 }
 
-irc::tokenstream::tokenstream(const std::string &source) : tokens(source), last_pushed(false)
-{
-       /* Record starting position and current position */
-       last_starting_position = tokens.begin();
-       n = tokens.begin();
-}
-
-irc::tokenstream::~tokenstream()
-{
-}
 
-bool irc::tokenstream::GetToken(std::string &token)
+bool irc::insensitive_swo::operator()(const std::string& a, const std::string& b) const
 {
-       std::string::iterator lsp = last_starting_position;
+       const unsigned char* charmap = national_case_insensitive_map;
+       std::string::size_type asize = a.size();
+       std::string::size_type bsize = b.size();
+       std::string::size_type maxsize = std::min(asize, bsize);
 
-       while (n != tokens.end())
+       for (std::string::size_type i = 0; i < maxsize; i++)
        {
-               /** Skip multi space, converting "  " into " "
-                */
-               while ((n+1 != tokens.end()) && (*n == ' ') && (*(n+1) == ' '))
-                       n++;
-
-               if ((last_pushed) && (*n == ':'))
-               {
-                       /* If we find a token thats not the first and starts with :,
-                        * this is the last token on the line
-                        */
-                       std::string::iterator curr = ++n;
-                       n = tokens.end();
-                       token = std::string(curr, tokens.end());
+               unsigned char A = charmap[(unsigned char)a[i]];
+               unsigned char B = charmap[(unsigned char)b[i]];
+               if (A > B)
+                       return false;
+               else if (A < B)
                        return true;
-               }
-
-               last_pushed = false;
-
-               if ((*n == ' ') || (n+1 == tokens.end()))
-               {
-                       /* If we find a space, or end of string, this is the end of a token.
-                        */
-                       last_starting_position = n+1;
-                       last_pushed = *n == ' ';
-
-                       std::string strip(lsp, n+1 == tokens.end() ? n+1  : n++);
-                       while ((strip.length()) && (strip.find_last_of(' ') == strip.length() - 1))
-                               strip.erase(strip.end() - 1);
-
-                       token = strip;
-                       return !token.empty();
-               }
-
-               n++;
        }
-       token.clear();
-       return false;
-}
-
-bool irc::tokenstream::GetToken(irc::string &token)
-{
-       std::string stdstring;
-       bool returnval = GetToken(stdstring);
-       token = assign(stdstring);
-       return returnval;
+       return (asize < bsize);
 }
 
-bool irc::tokenstream::GetToken(int &token)
+size_t irc::insensitive::operator()(const std::string &s) const
 {
-       std::string tok;
-       bool returnval = GetToken(tok);
-       token = ConvToInt(tok);
-       return returnval;
-}
-
-bool irc::tokenstream::GetToken(long &token)
-{
-       std::string tok;
-       bool returnval = GetToken(tok);
-       token = ConvToInt(tok);
-       return returnval;
+       /* XXX: NO DATA COPIES! :)
+        * The hash function here is practically
+        * a copy of the one in STL's hash_fun.h,
+        * only with *x replaced with national_case_insensitive_map[*x].
+        * This avoids a copy to use hash<const char*>
+        */
+       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;
 }
 
-irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator)
+irc::tokenstream::tokenstream(const std::string& msg, size_t start, size_t end)
+       : message(msg, start, end)
+       , position(0)
 {
-       last_starting_position = tokens.begin();
-       n = tokens.begin();
 }
 
-bool irc::sepstream::GetToken(std::string &token)
+bool irc::tokenstream::GetMiddle(std::string& token)
 {
-       std::string::iterator lsp = last_starting_position;
-
-       while (n != tokens.end())
+       // If we are past the end of the string we can't do anything.
+       if (position >= message.length())
        {
-               if ((*n == sep) || (n+1 == tokens.end()))
-               {
-                       last_starting_position = n+1;
-                       token = std::string(lsp, n+1 == tokens.end() ? n+1  : n++);
-
-                       while ((token.length()) && (token.find_last_of(sep) == token.length() - 1))
-                               token.erase(token.end() - 1);
-
-                       if (token.empty())
-                               n++;
-
-                       return n == tokens.end() ? false : true;
-               }
-
-               n++;
+               token.clear();
+               return false;
        }
 
-       token.clear();
-       return false;
-}
-
-const std::string irc::sepstream::GetRemaining()
-{
-       return std::string(n, tokens.end());
-}
-
-bool irc::sepstream::StreamEnd()
-{
-       return ((n + 1) == tokens.end());
-}
-
-irc::sepstream::~sepstream()
-{
-}
-
-std::string irc::hex(const unsigned char *raw, size_t rawsz)
-{
-       if (!rawsz)
-               return "";
-
-       /* EWW! This used to be using sprintf, which is WAY inefficient. -Special */
-
-       const char *hex = "0123456789abcdef";
-       static char hexbuf[MAXBUF];
-
-       size_t i, j;
-       for (i = 0, j = 0; j < rawsz; ++j)
+       // If we can't find another separator this is the last token in the message.
+       size_t separator = message.find(' ', position);
+       if (separator == std::string::npos)
        {
-               hexbuf[i++] = hex[raw[j] / 16];
-               hexbuf[i++] = hex[raw[j] % 16];
+               token.assign(message, position, std::string::npos);
+               position = message.length();
+               return true;
        }
-       hexbuf[i] = 0;
-
-       return hexbuf;
-}
-
-CoreExport const char* irc::Spacify(const char* n)
-{
-       static char x[MAXBUF];
-       strlcpy(x,n,MAXBUF);
-       for (char* y = x; *y; y++)
-               if (*y == '_')
-                       *y = ' ';
-       return x;
-}
-
 
-irc::modestacker::modestacker(bool add) : adding(add)
-{
-       sequence.clear();
-       sequence.push_back("");
+       token.assign(message, position, separator - position);
+       position = message.find_first_not_of(' ', separator);
+       return true;
 }
 
-void irc::modestacker::Push(char modeletter, const std::string &parameter)
+bool irc::tokenstream::GetTrailing(std::string& token)
 {
-       *(sequence.begin()) += modeletter;
-       sequence.push_back(parameter);
-}
+       // If we are past the end of the string we can't do anything.
+       if (position >= message.length())
+       {
+               token.clear();
+               return false;
+       }
 
-void irc::modestacker::Push(char modeletter)
-{
-       this->Push(modeletter,"");
-}
+       // If this is true then we have a <trailing> token!
+       if (message[position] == ':')
+       {
+               token.assign(message, position + 1, std::string::npos);
+               position = message.length();
+               return true;
+       }
 
-void irc::modestacker::PushPlus()
-{
-       this->Push('+',"");
+       // There is no <trailing> token so it must be a <middle> token.
+       return GetMiddle(token);
 }
 
-void irc::modestacker::PushMinus()
+irc::sepstream::sepstream(const std::string& source, char separator, bool allowempty)
+       : tokens(source), sep(separator), pos(0), allow_empty(allowempty)
 {
-       this->Push('-',"");
 }
 
-int irc::modestacker::GetStackedLine(std::vector<std::string> &result, int max_line_size)
+bool irc::sepstream::GetToken(std::string &token)
 {
-       if (sequence.empty())
+       if (this->StreamEnd())
        {
-               return 0;
+               token.clear();
+               return false;
        }
 
-       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))
+       if (!this->allow_empty)
        {
-               modeline += *(sequence[0].begin());
-               if (!sequence[1].empty())
+               this->pos = this->tokens.find_first_not_of(this->sep, this->pos);
+               if (this->pos == std::string::npos)
                {
-                       result.push_back(sequence[1]);
-                       size += nextsize; /* Account for mode character and whitespace */
+                       this->pos = this->tokens.length() + 1;
+                       token.clear();
+                       return false;
                }
-               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;
-}
+       size_t p = this->tokens.find(this->sep, this->pos);
+       if (p == std::string::npos)
+               p = this->tokens.length();
 
-irc::stringjoiner::stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end)
-{
-       if (end < begin)
-               return; // nothing to do here
+       token.assign(tokens, this->pos, p - this->pos);
+       this->pos = p + 1;
 
-       for (int v = begin; v < end; v++)
-               joined.append(sequence[v]).append(seperator);
-       joined.append(sequence[end]);
+       return true;
 }
 
-irc::stringjoiner::stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end)
+const std::string irc::sepstream::GetRemaining()
 {
-       if (end < begin)
-               return; // nothing to do here
-
-       for (int v = begin; v < end; v++)
-               joined.append(sequence[v]).append(seperator);
-       joined.append(sequence[end]);
+       return !this->StreamEnd() ? this->tokens.substr(this->pos) : "";
 }
 
-irc::stringjoiner::stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end)
+bool irc::sepstream::StreamEnd()
 {
-       if (end < begin)
-               return; // nothing to do here
-
-       for (int v = begin; v < end; v++)
-               joined.append(sequence[v]).append(seperator);
-       joined.append(sequence[end]);
+       return this->pos > this->tokens.length();
 }
 
-std::string& irc::stringjoiner::GetJoined()
+bool irc::sepstream::Contains(const std::string& value)
 {
-       return joined;
+       std::string token;
+       while (GetToken(token))
+               if (value == token)
+                       return true;
+
+       return false;
 }
 
 irc::portparser::portparser(const std::string &source, bool allow_overlapped)
@@ -528,10 +349,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))
                {
@@ -549,25 +369,3 @@ long irc::portparser::GetToken()
                return atoi(x.c_str());
        }
 }
-
-/*const std::basic_string& SearchAndReplace(std::string& text, const std::string& pattern, const std::string& replace)
-{
-       std::string 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)
-                       {
-                               replacement.append(replace);
-                               n = n + pattern.length() - 1;
-                       }
-                       else
-                       {
-                               replacement += text[n];
-                       }
-               }
-       }
-       text = replacement;
-       return text;
-}*/
index 439320c1e00d85334c29d3da845357b246d11cfa..846feab5072fd21e7c6ad63b0da9f7ed5b56fa94 100644 (file)
@@ -7,6 +7,7 @@
  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
  *   Copyright (C) 2006-2007 Oliver Lupton <oliverlupton@gmail.com>
  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   Copyright (C) 2003-2019 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
@@ -22,8 +23,6 @@
  */
 
 
-/* $Core */
-
 #ifdef _WIN32
 #define _CRT_RAND_S
 #include <stdlib.h>
 #include "exitcodes.h"
 #include <iostream>
 
-std::string InspIRCd::GetServerDescription(const std::string& servername)
-{
-       std::string description;
-
-       FOREACH_MOD(I_OnGetServerDescription,OnGetServerDescription(servername,description));
-
-       if (!description.empty())
-       {
-               return description;
-       }
-       else
-       {
-               // not a remote server that can be found, it must be me.
-               return Config->ServerDesc;
-       }
-}
-
 /* Find a user record by nickname and return a pointer to it */
 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;
-}
-
-User* InspIRCd::FindNick(const char* nick)
-{
-       if (isdigit(*nick))
-               return FindUUID(nick);
-
-       user_hash::iterator iter = this->Users->clientlist->find(nick);
-
-       if (iter == this->Users->clientlist->end())
-               return NULL;
-
-       return iter->second;
+       return FindNickOnly(nick);
 }
 
 User* InspIRCd::FindNickOnly(const std::string &nick)
 {
-       user_hash::iterator iter = this->Users->clientlist->find(nick);
-
-       if (iter == this->Users->clientlist->end())
-               return NULL;
-
-       return iter->second;
-}
-
-User* InspIRCd::FindNickOnly(const char* nick)
-{
-       user_hash::iterator iter = this->Users->clientlist->find(nick);
+       user_hash::iterator iter = this->Users->clientlist.find(nick);
 
-       if (iter == this->Users->clientlist->end())
+       if (iter == this->Users->clientlist.end())
                return NULL;
 
        return iter->second;
@@ -101,66 +53,26 @@ User* InspIRCd::FindNickOnly(const char* nick)
 
 User *InspIRCd::FindUUID(const std::string &uid)
 {
-       user_hash::iterator finduuid = this->Users->uuidlist->find(uid);
+       user_hash::iterator finduuid = this->Users->uuidlist.find(uid);
 
-       if (finduuid == this->Users->uuidlist->end())
+       if (finduuid == this->Users->uuidlist.end())
                return NULL;
 
        return finduuid->second;
 }
-
-User *InspIRCd::FindUUID(const char *uid)
-{
-       return FindUUID(std::string(uid));
-}
-
 /* find a channel record by channel name and return a pointer to it */
-Channel* InspIRCd::FindChan(const char* chan)
-{
-       chan_hash::iterator iter = chanlist->find(chan);
-
-       if (iter == chanlist->end())
-               /* Couldn't find it */
-               return NULL;
-
-       return iter->second;
-}
 
 Channel* InspIRCd::FindChan(const std::string &chan)
 {
-       chan_hash::iterator iter = chanlist->find(chan);
+       chan_hash::iterator iter = chanlist.find(chan);
 
-       if (iter == chanlist->end())
+       if (iter == chanlist.end())
                /* Couldn't find it */
                return NULL;
 
        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->WriteServ("NOTICE %s :%s",u->nick.c_str(),s.c_str());
-               }
-               else
-               {
-                       /* Unregistered connections receive ERROR, not a NOTICE */
-                       u->Write("ERROR :" + s);
-               }
-       }
-}
-
-/* return channel count */
-long InspIRCd::ChannelCount()
-{
-       return chanlist->size();
-}
-
 bool InspIRCd::IsValidMask(const std::string &mask)
 {
        const char* dest = mask.c_str();
@@ -190,7 +102,7 @@ bool InspIRCd::IsValidMask(const std::string &mask)
        if (exclamation != 1 || atsign != 1)
                return false;
 
-       if (mask.length() > 250)
+       if (mask.length() > ServerInstance->Config->Limits.GetMaxMask())
                return false;
 
        return true;
@@ -216,7 +128,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;
@@ -234,18 +147,20 @@ void InspIRCd::ProcessColors(file_cache& input)
        {
                std::string character;
                std::string replace;
-               special_chars(const std::string &c, const std::string &r) : character(c), replace(r) { }
-       }
-
-       special[] = {
-               special_chars("\\002", "\002"),  // Bold
-               special_chars("\\037", "\037"),  // underline
-               special_chars("\\003", "\003"),  // Color
-               special_chars("\\017", "\017"), // Stop colors
-               special_chars("\\u", "\037"),    // Alias for underline
-               special_chars("\\b", "\002"),    // Alias for Bold
-               special_chars("\\x", "\017"),    // Alias for stop
-               special_chars("\\c", "\003"),    // Alias for color
+               special_chars(const std::string& c, const std::string& r)
+                       : character(c)
+                       , replace(r)
+               {
+               }
+       } special[] = {
+               special_chars("\\b", "\x02"), // Bold
+               special_chars("\\c", "\x03"), // Color
+               special_chars("\\i", "\x1D"), // Italic
+               special_chars("\\m", "\x11"), // Monospace
+               special_chars("\\r", "\x16"), // Reverse
+               special_chars("\\s", "\x1E"), // Strikethrough
+               special_chars("\\u", "\x1F"), // Underline
+               special_chars("\\x", "\x0F"), // Reset
                special_chars("", "")
        };
 
@@ -281,47 +196,35 @@ void InspIRCd::ProcessColors(file_cache& input)
 }
 
 /* true for valid channel name, false else */
-bool IsChannelHandler::Call(const char *chname, size_t max)
+bool InspIRCd::DefaultIsChannel(const std::string& chname)
 {
-       const char *c = chname + 1;
+       if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
+               return false;
 
-       /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
-       if (!chname || *chname != '#')
-       {
+       if (chname[0] != '#')
                return false;
-       }
 
-       while (*c)
+       for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
        {
-               switch (*c)
+               switch (*i)
                {
                        case ' ':
                        case ',':
                        case 7:
                                return false;
                }
-
-               c++;
-       }
-
-       size_t len = c - chname;
-       /* too long a name - note funky pointer arithmetic here. */
-       if (len > max)
-       {
-                       return false;
        }
 
        return true;
 }
 
 /* true for valid nickname, false else */
-bool IsNickHandler::Call(const char* n, size_t max)
+bool InspIRCd::DefaultIsNick(const std::string& n)
 {
-       if (!n || !*n)
+       if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
                return false;
 
-       unsigned int p = 0;
-       for (const char* i = n; *i; i++, p++)
+       for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
        {
                if ((*i >= 'A') && (*i <= '}'))
                {
@@ -329,7 +232,7 @@ bool IsNickHandler::Call(const char* n, size_t max)
                        continue;
                }
 
-               if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n))
+               if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
                {
                        /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
                        continue;
@@ -339,17 +242,16 @@ bool IsNickHandler::Call(const char* n, size_t max)
                return false;
        }
 
-       /* too long? or not */
-       return (p <= max);
+       return true;
 }
 
 /* return true for good ident, false else */
-bool IsIdentHandler::Call(const char* n)
+bool InspIRCd::DefaultIsIdent(const std::string& n)
 {
-       if (!n || !*n)
+       if (n.empty())
                return false;
 
-       for (const char* i = n; *i; i++)
+       for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
        {
                if ((*i >= 'A') && (*i <= '}'))
                {
@@ -367,36 +269,73 @@ bool IsIdentHandler::Call(const char* n)
        return true;
 }
 
-bool IsSIDHandler::Call(const std::string &str)
+bool InspIRCd::IsHost(const std::string& host)
 {
-       /* Returns true if the string given is exactly 3 characters long,
-        * starts with a digit, and the other two characters are A-Z or digits
-        */
-       return ((str.length() == 3) && isdigit(str[0]) &&
-                       ((str[1] >= 'A' && str[1] <= 'Z') || isdigit(str[1])) &&
-                        ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
-}
+       // Hostnames must be non-empty and shorter than the maximum hostname length.
+       if (host.empty() || host.length() > ServerInstance->Config->Limits.MaxHost)
+               return false;
 
-/* open the proper logfile */
-bool InspIRCd::OpenLog(char**, int)
-{
-       if (!Config->cmdline.writelog) return true; // Skip opening default log if -nolog
+       unsigned int numdashes = 0;
+       unsigned int numdots = 0;
+       bool seendot = false;
+       const std::string::const_iterator hostend = host.end() - 1;
+       for (std::string::const_iterator iter = host.begin(); iter != host.end(); ++iter)
+       {
+               unsigned char chr = static_cast<unsigned char>(*iter);
 
-       if (Config->cmdline.startup_log.empty())
-               Config->cmdline.startup_log = LOG_PATH "/startup.log";
-       FILE* startup = fopen(Config->cmdline.startup_log.c_str(), "a+");
+               // If the current character is a label separator.
+               if (chr == '.')
+               {
+                       numdots++;
+
+                       // Consecutive separators are not allowed and dashes can not exist at the start or end
+                       // of labels and separators must only exist between labels.
+                       if (seendot || numdashes || iter == host.begin() || iter == hostend)
+                               return false;
+
+                       seendot = true;
+                       continue;
+               }
+
+               // If this point is reached then the character is not a dot.
+               seendot = false;
+
+               // If the current character is a dash.
+               if (chr == '-')
+               {
+                       // Consecutive separators are not allowed and dashes can not exist at the start or end
+                       // of labels and separators must only exist between labels.
+                       if (seendot || numdashes >= 2 || iter == host.begin() || iter == hostend)
+                               return false;
+
+                       numdashes += 1;
+                       continue;
+               }
+
+               // If this point is reached then the character is not a dash.
+               numdashes = 0;
+
+               // Alphanumeric characters are allowed at any position.
+               if ((chr >= '0' && chr <= '9') || (chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z'))
+                       continue;
 
-       if (!startup)
-       {
                return false;
        }
 
-       FileWriter* fw = new FileWriter(startup);
-       FileLogStream *f = new FileLogStream((Config->cmdline.forcedebug ? DEBUG : DEFAULT), fw);
-
-       this->Logs->AddLogType("*", f, true);
+       // Whilst simple hostnames (e.g. localhost) are valid we do not allow the server to use
+       // them to prevent issues with clients that differentiate between short client and server
+       // prefixes by checking whether the nickname contains a dot.
+       return numdots;
+}
 
-       return true;
+bool InspIRCd::IsSID(const std::string &str)
+{
+       /* Returns true if the string given is exactly 3 characters long,
+        * starts with a digit, and the other two characters are A-Z or digits
+        */
+       return ((str.length() == 3) && isdigit(str[0]) &&
+                       ((str[1] >= 'A' && str[1] <= 'Z') || isdigit(str[1])) &&
+                        ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
 }
 
 void InspIRCd::CheckRoot()
@@ -405,52 +344,49 @@ void InspIRCd::CheckRoot()
        if (geteuid() == 0)
        {
                std::cout << "ERROR: You are running an irc server as root! DO NOT DO THIS!" << std::endl << std::endl;
-               this->Logs->Log("STARTUP",DEFAULT,"Can't start as root");
+               this->Logs->Log("STARTUP", LOG_DEFAULT, "Can't start as root");
                Exit(EXIT_STATUS_ROOT);
        }
 #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->WriteServ("%d %s", numeric, copy_text.c_str());
-}
-
-void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...)
-{
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-       va_start (argsPtr, format);
-       vsnprintf(textbuffer, MAXBUF, format, argsPtr);
-       va_end(argsPtr);
-
-       this->SendWhoisLine(user, dest, numeric, std::string(textbuffer));
-}
-
-/** Refactored by Brain, Jun 2009. Much faster with some clever O(1) array
- * lookups and pointer maths.
+/** A lookup table of values for multiplier characters used by
+ * InspIRCd::Duration(). In this lookup table, the indexes for
+ * the ascii values 'm' and 'M' have the value '60', the indexes
+ * for the ascii values 'D' and 'd' have a value of '86400', etc.
  */
-long InspIRCd::Duration(const std::string &str)
+static const unsigned int duration_multi[] =
+{
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
+       0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
+       0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+bool InspIRCd::Duration(const std::string& str, unsigned long& duration)
 {
-       unsigned char multiplier = 0;
-       long total = 0;
-       long times = 1;
-       long subtotal = 0;
+       unsigned long total = 0;
+       unsigned long subtotal = 0;
 
        /* Iterate each item in the string, looking for number or multiplier */
-       for (std::string::const_reverse_iterator i = str.rbegin(); i != str.rend(); ++i)
+       for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
        {
                /* Found a number, queue it onto the current number */
                if ((*i >= '0') && (*i <= '9'))
                {
-                       subtotal = subtotal + ((*i - '0') * times);
-                       times = times * 10;
+                       subtotal = (subtotal * 10) + (*i - '0');
                }
                else
                {
@@ -458,49 +394,107 @@ long InspIRCd::Duration(const std::string &str)
                         * it multiplies the built up number by, multiply the total
                         * and reset the built up number.
                         */
-                       if (subtotal)
-                               total += subtotal * duration_multi[multiplier];
+                       unsigned int multiplier = duration_multi[static_cast<unsigned char>(*i)];
+                       if (multiplier == 0)
+                               return false;
+
+                       total += subtotal * multiplier;
 
                        /* Next subtotal please */
                        subtotal = 0;
-                       multiplier = *i;
-                       times = 1;
                }
        }
-       if (multiplier)
+       /* Any trailing values built up are treated as raw seconds */
+       duration = total + subtotal;
+       return true;
+}
+
+unsigned long InspIRCd::Duration(const std::string& str)
+{
+       unsigned long out = 0;
+       InspIRCd::Duration(str, out);
+       return out;
+}
+
+bool InspIRCd::IsValidDuration(const std::string& duration)
+{
+       for (std::string::const_iterator i = duration.begin(); i != duration.end(); ++i)
        {
-               total += subtotal * duration_multi[multiplier];
-               subtotal = 0;
+               unsigned char c = *i;
+               if (((c >= '0') && (c <= '9')))
+                       continue;
+
+               if (!duration_multi[c])
+                       return false;
        }
-       /* Any trailing values built up are treated as raw seconds */
-       return total + subtotal;
+       return true;
 }
 
-bool InspIRCd::ULine(const std::string& sserver)
+std::string InspIRCd::DurationString(time_t duration)
 {
-       if (sserver.empty())
-               return true;
+       time_t years = duration / 31536000;
+       time_t weeks = (duration / 604800) % 52;
+       time_t days = (duration / 86400) % 7;
+       time_t hours = (duration / 3600) % 24;
+       time_t minutes = (duration / 60) % 60;
+       time_t seconds = duration % 60;
+
+       std::string ret;
+
+       if (years)
+               ret = ConvToStr(years) + "y";
+       if (weeks)
+               ret += ConvToStr(weeks) + "w";
+       if (days)
+               ret += ConvToStr(days) + "d";
+       if (hours)
+               ret += ConvToStr(hours) + "h";
+       if (minutes)
+               ret += ConvToStr(minutes) + "m";
+       if (seconds)
+               ret += ConvToStr(seconds) + "s";
+
+       return ret;
+}
+
+std::string InspIRCd::Format(va_list& vaList, const char* formatString)
+{
+       static std::vector<char> formatBuffer(1024);
+
+       while (true)
+       {
+               va_list dst;
+               va_copy(dst, vaList);
+
+               int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
+               va_end(dst);
+
+               if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
+               {
+                       break;
+               }
+
+               formatBuffer.resize(formatBuffer.size() * 2);
+       }
 
-       return (Config->ulines.find(sserver.c_str()) != Config->ulines.end());
+       return std::string(&formatBuffer[0]);
 }
 
-bool InspIRCd::SilentULine(const std::string& sserver)
+std::string InspIRCd::Format(const char* formatString, ...)
 {
-       std::map<irc::string,bool>::iterator n = Config->ulines.find(sserver.c_str());
-       if (n != Config->ulines.end())
-               return n->second;
-       else
-               return false;
+       std::string ret;
+       VAFORMAT(ret, formatString, 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;
@@ -514,36 +508,24 @@ 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 %Y %H:%M:%S";
 
-// You should only pass a single character to this.
-void InspIRCd::AddExtBanChar(char c)
-{
-       std::string &tok = Config->data005;
-       std::string::size_type ebpos = tok.find(" EXTBAN=,");
+       char buffer[512];
+       if (!strftime(buffer, sizeof(buffer), format, timeinfo))
+               buffer[0] = '\0';
 
-       if (ebpos == std::string::npos)
-       {
-               tok.append(" EXTBAN=,");
-               tok.push_back(c);
-       }
-       else
-       {
-               ebpos += 9;
-               while (isalpha(tok[ebpos]) && tok[ebpos] < c)
-                       ebpos++;
-               tok.insert(ebpos, 1, c);
-       }
+       return buffer;
 }
 
-std::string InspIRCd::GenRandomStr(int length, bool printable)
+std::string InspIRCd::GenRandomStr(unsigned int length, bool printable)
 {
        char* buf = new char[length];
        GenRandom(buf, length);
        std::string rv;
        rv.resize(length);
-       for(int i=0; i < length; i++)
+       for(size_t i = 0; i < length; i++)
                rv[i] = printable ? 0x3F + (buf[i] & 0x3F) : buf[i];
        delete[] buf;
        return rv;
@@ -559,10 +541,13 @@ unsigned long InspIRCd::GenRandomInt(unsigned long max)
 }
 
 // This is overridden by a higher-quality algorithm when SSL support is loaded
-void GenRandomHandler::Call(char *output, size_t max)
+void InspIRCd::DefaultGenRandom(char* output, size_t max)
 {
-       for(unsigned int i=0; i < max; i++)
-#ifdef _WIN32
+#if defined HAS_ARC4RANDOM_BUF
+       arc4random_buf(output, max);
+#else
+       for (unsigned int i = 0; i < max; ++i)
+# ifdef _WIN32
        {
                unsigned int uTemp;
                if(rand_s(&uTemp) != 0)
@@ -570,32 +555,8 @@ void GenRandomHandler::Call(char *output, size_t max)
                else
                        output[i] = uTemp;
        }
-#else
+# else
                output[i] = random();
+# endif
 #endif
 }
-
-ModResult OnCheckExemptionHandler::Call(User* user, Channel* chan, const std::string& restriction)
-{
-       unsigned int mypfx = chan->GetPrefixValue(user);
-       char minmode = 0;
-       std::string current;
-
-       irc::spacesepstream defaultstream(ServerInstance->Config->ConfValue("options")->getString("exemptchanops"));
-
-       while (defaultstream.GetToken(current))
-       {
-               std::string::size_type pos = current.find(':');
-               if (pos == std::string::npos)
-                       continue;
-               if (current.substr(0,pos) == restriction)
-                       minmode = current[pos+1];
-       }
-
-       ModeHandler* mh = ServerInstance->Modes->FindMode(minmode, MODETYPE_CHANNEL);
-       if (mh && mypfx >= mh->GetPrefixRank())
-               return MOD_RES_ALLOW;
-       if (mh || minmode == '*')
-               return MOD_RES_DENY;
-       return MOD_RES_PASSTHRU;
-}
index 66f9bfdc56ca0de5edba55836444c8031a74c710..b6c25210427d450168a98a6b95714bc9842e41b9 100644 (file)
@@ -26,9 +26,7 @@
  */
 
 
-/* $Core */
 #include "inspircd.h"
-#include "inspircd_version.h"
 #include <signal.h>
 
 #ifndef _WIN32
        #include <sys/resource.h>
        #include <dlfcn.h>
        #include <getopt.h>
-
-       /* Some systems don't define RUSAGE_SELF. This should fix them. */
-       #ifndef RUSAGE_SELF
-               #define RUSAGE_SELF 0
-       #endif
-
        #include <pwd.h> // setuid
        #include <grp.h> // setgid
 #else
 #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;
 
@@ -78,26 +64,17 @@ unsigned const char *national_case_insensitive_map = rfc_case_insensitive_map;
  */
 const char* ExitCodes[] =
 {
-               "No error", /* 0 */
-               "DIE command", /* 1 */
-               "execv() failed", /* 2 */
-               "Internal error", /* 3 */
-               "Config file error", /* 4 */
-               "Logfile error", /* 5 */
-               "POSIX fork failed", /* 6 */
-               "Bad commandline parameters", /* 7 */
-               "No ports could be bound", /* 8 */
-               "Can't write PID file", /* 9 */
-               "SocketEngine could not initialize", /* 10 */
-               "Refusing to start up as root", /* 11 */
-               "Found a <die> tag!", /* 12 */
-               "Couldn't load module on startup", /* 13 */
-               "Could not create windows forked process", /* 14 */
-               "Received SIGTERM", /* 15 */
-               "Bad command handler loaded", /* 16 */
-               "RegisterServiceCtrlHandler failed", /* 17 */
-               "UpdateSCMStatus failed", /* 18 */
-               "CreateEvent failed" /* 19 */
+               "No error",                                                             // 0
+               "DIE command",                                                  // 1
+               "Config file error",                                    // 2
+               "Logfile error",                                                // 3
+               "POSIX fork failed",                                    // 4
+               "Bad commandline parameters",                   // 5
+               "Can't write PID file",                                 // 6
+               "SocketEngine could not initialize",    // 7
+               "Refusing to start up as root",                 // 8
+               "Couldn't load module on startup",              // 9
+               "Received SIGTERM"                                              // 10
 };
 
 template<typename T> static void DeleteZero(T*&n)
@@ -109,21 +86,19 @@ template<typename T> static void DeleteZero(T*&n)
 
 void InspIRCd::Cleanup()
 {
+       // Close all listening sockets
        for (unsigned int i = 0; i < ports.size(); i++)
        {
-               /* This calls the constructor and closes the listening socket */
                ports[i]->cull();
                delete ports[i];
        }
        ports.clear();
 
-       /* Close all client sockets, or the new process inherits them */
-       LocalUserList::reverse_iterator i = Users->local_users.rbegin();
-       while (i != this->Users->local_users.rend())
-       {
-               User* u = *i++;
-               Users->QuitUser(u, "Server shutdown");
-       }
+       // Disconnect all local users
+       const std::string quitmsg = "Server shutting down";
+       const UserManager::LocalList& list = Users.GetLocalUsers();
+       while (!list.empty())
+               ServerInstance->Users.QuitUser(list.front(), quitmsg);
 
        GlobalCulls.Apply();
        Modules->UnloadAll();
@@ -131,103 +106,15 @@ void InspIRCd::Cleanup()
        /* Delete objects dynamically allocated in constructor (destructor would be more appropriate, but we're likely exiting) */
        /* Must be deleted before modes as it decrements modelines */
        if (FakeClient)
+       {
+               delete FakeClient->server;
                FakeClient->cull();
-       if (Res)
-               Res->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->Res);
-       DeleteZero(this->chanlist);
-       DeleteZero(this->PI);
-       DeleteZero(this->Threads);
-       DeleteZero(this->Timers);
-       DeleteZero(this->SE);
-       /* Close logging */
-       this->Logs->CloseLogs();
-       DeleteZero(this->Logs);
-}
-
-void InspIRCd::Restart(const std::string &reason)
-{
-       /* SendError flushes each client's queue,
-        * regardless of writeability state
-        */
-       this->SendError(reason);
-
-       /* Figure out our filename (if theyve renamed it, we're boned) */
-       std::string me;
-
-       char** argv = Config->cmdline.argv;
-
-#ifdef _WIN32
-       char module[MAX_PATH];
-       if (GetModuleFileNameA(NULL, module, MAX_PATH))
-               me = module;
-#else
-       me = argv[0];
-#endif
-
-       this->Cleanup();
-
-       if (execv(me.c_str(), argv) == -1)
-       {
-               /* Will raise a SIGABRT if not trapped */
-               throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno));
-       }
-}
-
-void InspIRCd::ResetMaxBans()
-{
-       for (chan_hash::const_iterator i = chanlist->begin(); i != chanlist->end(); i++)
-               i->second->ResetMaxBans();
-}
-
-/** Because hash_map doesn't free its buckets when we delete items, we occasionally
- * recreate the hash to free them up.
- * We do this by copying the entries from the old hash to a new hash, causing all
- * empty buckets to be weeded out of the hash.
- * Since this is quite expensive, it's not done very often.
- */
-void InspIRCd::RehashUsersAndChans()
-{
-       user_hash* old_users = Users->clientlist;
-       Users->clientlist = new user_hash;
-       for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++)
-               Users->clientlist->insert(*n);
-       delete old_users;
-
-       user_hash* old_uuid = Users->uuidlist;
-       Users->uuidlist = new user_hash;
-       for (user_hash::const_iterator n = old_uuid->begin(); n != old_uuid->end(); n++)
-               Users->uuidlist->insert(*n);
-       delete old_uuid;
-
-       chan_hash* old_chans = chanlist;
-       chanlist = new chan_hash;
-       for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++)
-               chanlist->insert(*n);
-       delete old_chans;
-
-       // 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 = Users->local_users.begin(); i != Users->local_users.end(); i++)
-       {
-               (**i).already_sent = 0;
-               (**i).RemoveExpiredInvites();
-       }
-
-       // HACK: ELines are not expired properly at the moment but it can't be fixed as
-       // the 2.0 XLine system is a spaghetti nightmare. Instead we skip over expired
-       // ELines in XLineManager::CheckELines() and expire them here instead.
-       ServerInstance->XLines->GetAll("E");
+       SocketEngine::Deinit();
+       Logs->CloseLogs();
 }
 
 void InspIRCd::SetSignals()
@@ -243,15 +130,10 @@ void InspIRCd::SetSignals()
        signal(SIGTERM, InspIRCd::SetSignal);
 }
 
-void InspIRCd::QuickExit(int status)
-{
-       exit(status);
-}
-
 // Required for returning the proper value of EXIT_SUCCESS for the parent process
 static void VoidSignalHandler(int signalreceived)
 {
-       exit(0);
+       exit(EXIT_STATUS_NOERROR);
 }
 
 bool InspIRCd::DaemonSeed()
@@ -260,11 +142,11 @@ bool InspIRCd::DaemonSeed()
        std::cout << "InspIRCd Process ID: " << con_green << GetCurrentProcessId() << con_reset << std::endl;
        return true;
 #else
-       // Do not use QuickExit here: It will exit with status SIGTERM which would break e.g. daemon scripts
+       // Do not use exit() here: It will exit with status SIGTERM which would break e.g. daemon scripts
        signal(SIGTERM, VoidSignalHandler);
 
-       int childpid;
-       if ((childpid = fork ()) < 0)
+       int childpid = fork();
+       if (childpid < 0)
                return false;
        else if (childpid > 0)
        {
@@ -277,7 +159,7 @@ bool InspIRCd::DaemonSeed()
                 */
                while (kill(childpid, 0) != -1)
                        sleep(1);
-               exit(0);
+               exit(EXIT_STATUS_NOERROR);
        }
        setsid ();
        std::cout << "InspIRCd Process ID: " << con_green << getpid() << con_reset << std::endl;
@@ -287,13 +169,13 @@ bool InspIRCd::DaemonSeed()
        rlimit rl;
        if (getrlimit(RLIMIT_CORE, &rl) == -1)
        {
-               this->Logs->Log("STARTUP",DEFAULT,"Failed to getrlimit()!");
+               this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to getrlimit()!");
                return false;
        }
        rl.rlim_cur = rl.rlim_max;
 
        if (setrlimit(RLIMIT_CORE, &rl) == -1)
-                       this->Logs->Log("STARTUP",DEFAULT,"setrlimit() failed, cannot increase coredump size.");
+                       this->Logs->Log("STARTUP", LOG_DEFAULT, "setrlimit() failed, cannot increase coredump size.");
 
        return true;
 #endif
@@ -302,9 +184,15 @@ bool InspIRCd::DaemonSeed()
 void InspIRCd::WritePID(const std::string& filename, bool exitonfail)
 {
 #ifndef _WIN32
+       if (!ServerInstance->Config->cmdline.writepid)
+       {
+               this->Logs->Log("STARTUP", LOG_DEFAULT, "--nopid specified on command line; PID file not written.");
+               return;
+       }
+
        std::string fname(filename);
        if (fname.empty())
-               fname = DATA_PATH "/inspircd.pid";
+               fname = ServerInstance->Config->Paths.PrependData("inspircd.pid");
        std::ofstream outfile(fname.c_str());
        if (outfile.is_open())
        {
@@ -312,93 +200,50 @@ void InspIRCd::WritePID(const std::string& filename, bool exitonfail)
                outfile.close();
        }
        else
-       {\r
-               if (exitonfail)\r
+       {
+               if (exitonfail)
                        std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
-               this->Logs->Log("STARTUP",DEFAULT,"Failed to write PID-file '%s'%s",fname.c_str(), (exitonfail ? ", exiting." : ""));\r
+               this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s'%s", fname.c_str(), (exitonfail ? ", exiting." : ""));
                if (exitonfail)
-                       Exit(EXIT_STATUS_PID);\r
+                       Exit(EXIT_STATUS_PID);
        }
 #endif
 }
 
 InspIRCd::InspIRCd(int argc, char** argv) :
-        ConfigFileName(CONFIG_PATH "/inspircd.conf"),
+        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.
          */
-        NICKForced("NICKForced", NULL),
-        OperQuit("OperQuit", NULL),
-        GenRandom(&HandleGenRandom),
-        IsChannel(&HandleIsChannel),
-        IsSID(&HandleIsSID),
-        Rehash(&HandleRehash),
-        IsNick(&HandleIsNick),
-        IsIdent(&HandleIsIdent),
-        FloodQuitUser(&HandleFloodQuitUser),
-        OnCheckExemption(&HandleOnCheckExemption)
+        GenRandom(&DefaultGenRandom),
+        IsChannel(&DefaultIsChannel),
+        IsNick(&DefaultIsNick),
+        IsIdent(&DefaultIsIdent)
 {
        ServerInstance = this;
 
-       Extensions.Register(&NICKForced);
-       Extensions.Register(&OperQuit);
-
        FailedPortList pl;
+       // Flag variables passed to getopt_long() later
        int do_version = 0, do_nofork = 0, do_debug = 0,
-           do_nolog = 0, do_root = 0, do_testsuite = 0;    /* flag variables */
-       int c = 0;
+           do_nolog = 0, do_nopid = 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->chanlist = 0;
        this->Config = 0;
-       this->SNO = 0;
-       this->BanCache = 0;
-       this->Modules = 0;
-       this->stats = 0;
-       this->Timers = 0;
-       this->Parser = 0;
        this->XLines = 0;
-       this->Modes = 0;
-       this->Res = 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;
-
-       SE = CreateSocketEngine();
-
-       this->Threads = new ThreadEngine;
-
-       /* Default implementation does nothing */
-       this->PI = new ProtocolInterface;
-
-       this->s_signal = 0;
-
-       // Create base manager classes early, so nothing breaks
-       this->Users = new UserManager;
-
-       this->Users->clientlist = new user_hash();
-       this->Users->uuidlist = new user_hash();
-       this->chanlist = new chan_hash();
+       SocketEngine::Init();
 
        this->Config = new ServerConfig;
-       this->SNO = new SnomaskManager;
-       this->BanCache = new BanCacheManager;
-       this->Modules = new ModuleManager();
-       this->stats = new serverstats();
-       this->Timers = new TimerManager;
-       this->Parser = new CommandParser;
+       dynamic_reference_base::reset_all();
        this->XLines = new XLineManager;
 
        this->Config->cmdline.argv = argv;
@@ -424,31 +269,46 @@ InspIRCd::InspIRCd(int argc, char** argv) :
        srandom(TIME.tv_nsec ^ TIME.tv_sec);
 #endif
 
+       {
+               ServiceProvider* provs[] =
+               {
+                       &rfcevents.numeric, &rfcevents.join, &rfcevents.part, &rfcevents.kick, &rfcevents.quit, &rfcevents.nick,
+                       &rfcevents.mode, &rfcevents.topic, &rfcevents.privmsg, &rfcevents.invite, &rfcevents.ping, &rfcevents.pong,
+                       &rfcevents.error
+               };
+               Modules.AddServices(provs, sizeof(provs)/sizeof(provs[0]));
+       }
+
        struct option longopts[] =
        {
                { "nofork",     no_argument,            &do_nofork,     1       },
-               { "logfile",    required_argument,      NULL,           'f'     },
                { "config",     required_argument,      NULL,           'c'     },
                { "debug",      no_argument,            &do_debug,      1       },
                { "nolog",      no_argument,            &do_nolog,      1       },
+               { "nopid",      no_argument,            &do_nopid,      1       },
                { "runasroot",  no_argument,            &do_root,       1       },
                { "version",    no_argument,            &do_version,    1       },
-               { "testsuite",  no_argument,            &do_testsuite,  1       },
                { 0, 0, 0, 0 }
        };
 
+       int c;
        int index;
-       while ((c = getopt_long(argc, argv, ":c:f:", longopts, &index)) != -1)
+       while ((c = getopt_long(argc, argv, ":c:", longopts, &index)) != -1)
        {
                switch (c)
                {
-                       case 'f':
-                               /* Log filename was set */
-                               Config->cmdline.startup_log = optarg;
-                       break;
                        case 'c':
                                /* Config filename was set */
                                ConfigFileName = optarg;
+#ifdef _WIN32
+                               TCHAR configPath[MAX_PATH + 1];
+                               if (GetFullPathName(optarg, MAX_PATH, configPath, NULL) > 0)
+                                       ConfigFileName = configPath;
+#else
+                               char configPath[PATH_MAX + 1];
+                               if (realpath(optarg, configPath))
+                                       ConfigFileName = configPath;
+#endif
                        break;
                        case 0:
                                /* getopt_long_only() set an int variable, just keep going */
@@ -458,19 +318,16 @@ InspIRCd::InspIRCd(int argc, char** argv) :
                        default:
                                /* Fall through to handle other weird values too */
                                std::cout << "Unknown parameter '" << argv[optind-1] << "'" << std::endl;
-                               std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--debug] [--logfile <filename>] " << std::endl <<
-                                       std::string(static_cast<int>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version] [--config <config>] [--testsuite]" << std::endl;
+                               std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--nopid] [--debug] [--config <config>]" << std::endl <<
+                                       std::string(static_cast<size_t>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version]" << std::endl;
                                Exit(EXIT_STATUS_ARGV);
                        break;
                }
        }
 
-       if (do_testsuite)
-               do_nofork = do_debug = true;
-
        if (do_version)
        {
-               std::cout << std::endl << VERSION << " r" << REVISION << std::endl;
+               std::cout << std::endl << INSPIRCD_VERSION << std::endl;
                Exit(EXIT_STATUS_NOERROR);
        }
 
@@ -484,28 +341,23 @@ InspIRCd::InspIRCd(int argc, char** argv) :
        Config->cmdline.nofork = (do_nofork != 0);
        Config->cmdline.forcedebug = (do_debug != 0);
        Config->cmdline.writelog = !do_nolog;
-       Config->cmdline.TestSuite = (do_testsuite != 0);
+       Config->cmdline.writepid = !do_nopid;
 
        if (do_debug)
        {
-               FileWriter* fw = new FileWriter(stdout);
-               FileLogStream* fls = new FileLogStream(RAWIO, fw);
+               FileWriter* fw = new FileWriter(stdout, 1);
+               FileLogStream* fls = new FileLogStream(LOG_RAWIO, fw);
                Logs->AddLogTypes("*", fls, true);
        }
-       else if (!this->OpenLog(argv, argc))
-       {
-               std::cout << "ERROR: Could not open initial logfile " << Config->cmdline.startup_log << ": " << strerror(errno) << std::endl << std::endl;
-               Exit(EXIT_STATUS_LOG);
-       }
 
-       if (!ServerConfig::FileExists(ConfigFileName.c_str()))
+       if (!FileSystem::FileExists(ConfigFileName))
        {
 #ifdef _WIN32
                /* Windows can (and defaults to) hide file extensions, so let's play a bit nice for windows users. */
                std::string txtconf = this->ConfigFileName;
                txtconf.append(".txt");
 
-               if (ServerConfig::FileExists(txtconf.c_str()))
+               if (FileSystem::FileExists(txtconf))
                {
                        ConfigFileName = txtconf;
                }
@@ -513,34 +365,27 @@ InspIRCd::InspIRCd(int argc, char** argv) :
 #endif
                {
                        std::cout << "ERROR: Cannot open config file: " << ConfigFileName << std::endl << "Exiting..." << std::endl;
-                       this->Logs->Log("STARTUP",DEFAULT,"Unable to open config file %s", ConfigFileName.c_str());
+                       this->Logs->Log("STARTUP", LOG_DEFAULT, "Unable to open config file %s", ConfigFileName.c_str());
                        Exit(EXIT_STATUS_CONFIG);
                }
        }
 
-       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;\r
-       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 << std::endl;
+       std::cout << "For contributors & authors: " << con_green << "See /INFO Output" << con_reset << std::endl;
 
 #ifndef _WIN32
        if (!do_root)
                this->CheckRoot();
        else
        {
-               std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl\r
-               << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl\r
-               << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl\r
-               << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl\r
-               << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl\r
-               << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl\r
-               << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl\r
-               << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl;\r
+               std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl
+               << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl
+               << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl
+               << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl
+               << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl
+               << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl
+               << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl
+               << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl;
                sleep(20);
        }
 #endif
@@ -552,47 +397,31 @@ InspIRCd::InspIRCd(int argc, char** argv) :
                if (!this->DaemonSeed())
                {
                        std::cout << "ERROR: could not go into daemon mode. Shutting down." << std::endl;
-                       Logs->Log("STARTUP", DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
+                       Logs->Log("STARTUP", LOG_DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
                        Exit(EXIT_STATUS_FORK);
                }
        }
 
-       SE->RecoverFromFork();
+       SocketEngine::RecoverFromFork();
 
-       /* During startup we don't actually initialize this
-        * in the thread engine.
+       /* During startup we read the configuration now, not in
+        * a seperate thread
         */
        this->Config->Read();
        this->Config->Apply(NULL, "");
        Logs->OpenFileLogs();
 
-       this->Res = new DNS();
-
-       /*
-        * Initialise SID/UID.
-        * For an explanation as to exactly how this works, and why it works this way, see GetUID().
-        *   -- w00t
-        */
+       // If we don't have a SID, generate one based on the server name and the server description
        if (Config->sid.empty())
-       {
-               // Generate one
-               unsigned int sid = 0;
-               char sidstr[4];
-
-               for (const char* x = Config->ServerName.c_str(); *x; ++x)
-                       sid = 5 * sid + *x;
-               for (const char* y = Config->ServerDesc.c_str(); *y; ++y)
-                       sid = 5 * sid + *y;
-               sprintf(sidstr, "%03d", sid % 1000);
+               Config->sid = UIDGenerator::GenerateSID(Config->ServerName, Config->ServerDesc);
 
-               Config->sid = sidstr;
-       }
+       // Initialize the UID generator with our sid
+       this->UIDGen.init(Config->sid);
 
-       /* set up fake client again this time with the correct uid */
-       this->FakeClient = new FakeUser(Config->sid, Config->ServerName);
+       // Create the server user for this server
+       this->FakeClient = new FakeUser(Config->sid, Config->ServerName, Config->ServerDesc);
 
-       // Get XLine to do it's thing.
-       this->XLines->CheckELines();
+       // This is needed as all new XLines are marked pending until ApplyLines() is called
        this->XLines->ApplyLines();
 
        int bounditems = BindPorts(pl);
@@ -601,9 +430,8 @@ InspIRCd::InspIRCd(int argc, char** argv) :
 
        this->Modules->LoadAll();
 
-       /* Just in case no modules were loaded - fix for bug #101 */
-       this->BuildISupport();
-       Config->ApplyDisabledCommands(Config->DisabledCommands);
+       // Build ISupport as ModuleManager::LoadAll() does not do it
+       this->ISupport.Build();
 
        if (!pl.empty())
        {
@@ -613,13 +441,13 @@ InspIRCd::InspIRCd(int argc, char** argv) :
                int j = 1;
                for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
                {
-                       std::cout << j << ".\tAddress: " << (i->first.empty() ? "<all>" : i->first) << " \tReason: " << i->second << std::endl;
+                       std::cout << j << ".\tAddress: " << i->first.str() << " \tReason: " << strerror(i->second) << std::endl;
                }
 
                std::cout << std::endl << "Hint: Try using a public IP instead of blank or *" << std::endl;
        }
 
-       std::cout << "InspIRCd is now running as '" << Config->ServerName << "'[" << Config->GetSID() << "] with " << SE->GetMaxFds() << " max open sockets" << std::endl;
+       std::cout << "InspIRCd is now running as '" << Config->ServerName << "'[" << Config->GetSID() << "] with " << SocketEngine::GetMaxFds() << " max open sockets" << std::endl;
 
 #ifndef _WIN32
        if (!Config->cmdline.nofork)
@@ -627,7 +455,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
                if (kill(getppid(), SIGTERM) == -1)
                {
                        std::cout << "Error killing parent process: " << strerror(errno) << std::endl;
-                       Logs->Log("STARTUP", DEFAULT, "Error killing parent process: %s",strerror(errno));
+                       Logs->Log("STARTUP", LOG_DEFAULT, "Error killing parent process: %s",strerror(errno));
                }
        }
 
@@ -641,7 +469,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
         *
         *    -- nenolod
         */
-       if ((!do_nofork) && (!do_testsuite) && (!Config->cmdline.forcedebug))
+       if ((!do_nofork) && (!Config->cmdline.forcedebug))
        {
                int fd = open("/dev/null", O_RDWR);
 
@@ -650,16 +478,16 @@ InspIRCd::InspIRCd(int argc, char** argv) :
                fclose(stdout);
 
                if (dup2(fd, STDIN_FILENO) < 0)
-                       Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stdin.");
+                       Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdin.");
                if (dup2(fd, STDOUT_FILENO) < 0)
-                       Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stdout.");
+                       Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdout.");
                if (dup2(fd, STDERR_FILENO) < 0)
-                       Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stderr.");
+                       Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stderr.");
                close(fd);
        }
        else
        {
-               Logs->Log("STARTUP", DEFAULT,"Keeping pseudo-tty open as we are running in the foreground.");
+               Logs->Log("STARTUP", LOG_DEFAULT, "Keeping pseudo-tty open as we are running in the foreground.");
        }
 #else
        /* Set win32 service as running, if we are running as a service */
@@ -671,68 +499,53 @@ InspIRCd::InspIRCd(int argc, char** argv) :
                FreeConsole();
        }
 
-       QueryPerformanceFrequency(&stats->QPFrequency);
+       QueryPerformanceFrequency(&stats.QPFrequency);
 #endif
 
-       Logs->Log("STARTUP", DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SE->GetMaxFds());
+       Logs->Log("STARTUP", LOG_DEFAULT, "Startup complete as '%s'[%s], %lu max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SocketEngine::GetMaxFds());
 
 #ifndef _WIN32
-       std::string SetUser = Config->ConfValue("security")->getString("runasuser");
-       std::string SetGroup = Config->ConfValue("security")->getString("runasgroup");
+       ConfigTag* security = Config->ConfValue("security");
+
+       const std::string SetGroup = security->getString("runasgroup");
        if (!SetGroup.empty())
        {
-               int ret;
-
-               // setgroups
-               ret = setgroups(0, NULL);
-
-               if (ret == -1)
+               errno = 0;
+               if (setgroups(0, NULL) == -1)
                {
-                       this->Logs->Log("SETGROUPS", DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
-                       this->QuickExit(0);
+                       this->Logs->Log("STARTUP", LOG_DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
+                       exit(EXIT_STATUS_CONFIG);
                }
 
-               // setgid
-               struct group *g;
-
-               errno = 0;
-               g = getgrnam(SetGroup.c_str());
-
+               struct group* g = getgrnam(SetGroup.c_str());
                if (!g)
                {
-                       this->Logs->Log("SETGUID", DEFAULT, "getgrnam(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno));
-                       this->QuickExit(0);
+                       this->Logs->Log("STARTUP", LOG_DEFAULT, "getgrnam(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno));
+                       exit(EXIT_STATUS_CONFIG);
                }
 
-               ret = setgid(g->gr_gid);
-
-               if (ret == -1)
+               if (setgid(g->gr_gid) == -1)
                {
-                       this->Logs->Log("SETGUID", DEFAULT, "setgid() failed (wrong group?): %s", strerror(errno));
-                       this->QuickExit(0);
+                       this->Logs->Log("STARTUP", LOG_DEFAULT, "setgid(%d) failed (wrong group?): %s", g->gr_gid, strerror(errno));
+                       exit(EXIT_STATUS_CONFIG);
                }
        }
 
+       const std::string SetUser = security->getString("runasuser");
        if (!SetUser.empty())
        {
-               // setuid
-               struct passwd *u;
-
                errno = 0;
-               u = getpwnam(SetUser.c_str());
-
+               struct passwd* u = getpwnam(SetUser.c_str());
                if (!u)
                {
-                       this->Logs->Log("SETGUID", DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno));
-                       this->QuickExit(0);
+                       this->Logs->Log("STARTUP", LOG_DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno));
+                       exit(EXIT_STATUS_CONFIG);
                }
 
-               int ret = setuid(u->pw_uid);
-
-               if (ret == -1)
+               if (setuid(u->pw_uid) == -1)
                {
-                       this->Logs->Log("SETGUID", DEFAULT, "setuid() failed (wrong user?): %s", strerror(errno));
-                       this->QuickExit(0);
+                       this->Logs->Log("STARTUP", LOG_DEFAULT, "setuid(%d) failed (wrong user?): %s", u->pw_uid, strerror(errno));
+                       exit(EXIT_STATUS_CONFIG);
                }
        }
 
@@ -760,16 +573,8 @@ void InspIRCd::UpdateTime()
 #endif
 }
 
-int InspIRCd::Run()
+void InspIRCd::Run()
 {
-       /* See if we're supposed to be running the test suite rather than entering the mainloop */
-       if (Config->cmdline.TestSuite)
-       {
-               TestSuite* ts = new TestSuite;
-               delete ts;
-               Exit(0);
-       }
-
        UpdateTime();
        time_t OLDTIME = TIME.tv_sec;
 
@@ -783,7 +588,7 @@ int InspIRCd::Run()
                if (this->ConfigThread && this->ConfigThread->IsDone())
                {
                        /* Rehash has completed */
-                       this->Logs->Log("CONFIG",DEBUG,"Detected ConfigThread exiting, tidying up...");
+                       this->Logs->Log("CONFIG", LOG_DEBUG, "Detected ConfigThread exiting, tidying up...");
 
                        this->ConfigThread->Finish();
 
@@ -803,45 +608,50 @@ int 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));
-                       }
-                       else if (TIME.tv_sec > OLDTIME + 2)
+                       if (Config->TimeSkipWarn)
                        {
-                               SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)(TIME.tv_sec - OLDTIME));
+                               time_t timediff = TIME.tv_sec - OLDTIME;
+
+                               if (timediff > Config->TimeSkipWarn)
+                                       SNO->WriteToSnoMask('a', "\002Performance warning!\002 Server clock jumped forwards by %lu seconds!", timediff);
+
+                               else if (timediff < -Config->TimeSkipWarn)
+                                       SNO->WriteToSnoMask('a', "\002Performance warning!\002 Server clock jumped backwards by %lu seconds!", labs(timediff));
                        }
-\r
+
                        OLDTIME = TIME.tv_sec;
 
                        if ((TIME.tv_sec % 3600) == 0)
                        {
-                               this->RehashUsersAndChans();
-                               FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
+                               FOREACH_MOD(OnGarbageCollect, ());
+
+                               // HACK: ELines are not expired properly at the moment but it can't be fixed as
+                               // the 2.0 XLine system is a spaghetti nightmare. Instead we skip over expired
+                               // ELines in XLineManager::CheckELines() and expire them here instead.
+                               XLines->GetAll("E");
                        }
 
-                       Timers->TickTimers(TIME.tv_sec);
-                       this->DoBackgroundUserStuff();
+                       Timers.TickTimers(TIME.tv_sec);
+                       Users->DoBackgroundUserStuff();
 
                        if ((TIME.tv_sec % 5) == 0)
                        {
-                               FOREACH_MOD(I_OnBackgroundTimer,OnBackgroundTimer(TIME.tv_sec));
+                               FOREACH_MOD(OnBackgroundTimer, (TIME.tv_sec));
                                SNO->FlushSnotices();
                        }
                }
@@ -853,8 +663,8 @@ int InspIRCd::Run()
                 * This will cause any read or write events to be
                 * dispatched to their handlers.
                 */
-               this->SE->DispatchTrialWrites();
-               this->SE->DispatchEvents();
+               SocketEngine::DispatchTrialWrites();
+               SocketEngine::DispatchEvents();
 
                /* if any users were quit, take them out */
                GlobalCulls.Apply();
@@ -866,25 +676,6 @@ int InspIRCd::Run()
                        s_signal = 0;
                }
        }
-
-       return 0;
-}
-
-/**********************************************************************************/
-
-/**
- * An ircd in five lines! bwahahaha. ahahahahaha. ahahah *cough*.
- */
-
-/* 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)
- */
-bool InspIRCd::AllModulesReportReady(LocalUser* user)
-{
-       ModResult res;
-       FIRST_MOD_RESULT(OnCheckReady, res, (user));
-       return (res == MOD_RES_PASSTHRU);
 }
 
 sig_atomic_t InspIRCd::s_signal = 0;
index 356904f741b35e8efbcfe5bac61cdaf98721f31e..59d9558a4ecd25a83930ff9a2935e853080d39b3 100644 (file)
 
 
 #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()
 {
@@ -47,12 +45,12 @@ BufferedSocket::BufferedSocket(int newfd)
        this->fd = newfd;
        this->state = I_CONNECTED;
        if (fd > -1)
-               ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
+               SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
 }
 
-void BufferedSocket::DoConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip)
+void BufferedSocket::DoConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned int maxtime)
 {
-       BufferedSocketError err = BeginConnect(ipaddr, aport, maxtime, connectbindip);
+       BufferedSocketError err = BeginConnect(dest, bind, maxtime);
        if (err != I_ERR_NONE)
        {
                state = I_ERROR;
@@ -61,44 +59,23 @@ void BufferedSocket::DoConnect(const std::string &ipaddr, int aport, unsigned lo
        }
 }
 
-BufferedSocketError BufferedSocket::BeginConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip)
-{
-       irc::sockets::sockaddrs addr, bind;
-       if (!irc::sockets::aptosa(ipaddr, aport, addr))
-       {
-               ServerInstance->Logs->Log("SOCKET", DEBUG, "BUG: Hostname passed to BufferedSocket, rather than an IP address!");
-               return I_ERR_CONNECT;
-       }
-
-       bind.sa.sa_family = 0;
-       if (!connectbindip.empty())
-       {
-               if (!irc::sockets::aptosa(connectbindip, 0, bind))
-               {
-                       return I_ERR_BIND;
-               }
-       }
-
-       return BeginConnect(addr, bind, maxtime);
-}
-
-BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned long timeout)
+BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned int timeout)
 {
        if (fd < 0)
-               fd = socket(dest.sa.sa_family, SOCK_STREAM, 0);
+               fd = socket(dest.family(), SOCK_STREAM, 0);
 
        if (fd < 0)
                return I_ERR_SOCKET;
 
-       if (bind.sa.sa_family != 0)
+       if (bind.family() != 0)
        {
-               if (ServerInstance->SE->Bind(fd, bind) < 0)
+               if (SocketEngine::Bind(fd, bind) < 0)
                        return I_ERR_BIND;
        }
 
-       ServerInstance->SE->NonBlocking(fd);
+       SocketEngine::NonBlocking(fd);
 
-       if (ServerInstance->SE->Connect(this, &dest.sa, sa_size(dest)) == -1)
+       if (SocketEngine::Connect(this, dest) == -1)
        {
                if (errno != EINPROGRESS)
                        return I_ERR_CONNECT;
@@ -106,13 +83,13 @@ BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs&
 
        this->state = I_CONNECTING;
 
-       if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE | FD_WRITE_WILL_BLOCK))
+       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());
-       ServerInstance->Timers->AddTimer(this->Timeout);
+       this->Timeout = new SocketTimeout(this->GetFd(), this, timeout);
+       ServerInstance->Timers.AddTimer(this->Timeout);
 
-       ServerInstance->Logs->Log("SOCKET", DEBUG,"BufferedSocket::DoConnect success");
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BufferedSocket::DoConnect success");
        return I_ERR_NONE;
 }
 
@@ -122,23 +99,18 @@ void StreamSocket::Close()
        {
                // final chance, dump as much of the sendq as we can
                DoWrite();
-               if (IOHook)
+
+               IOHook* hook = GetIOHook();
+               DelIOHook();
+               while (hook)
                {
-                       try
-                       {
-                               IOHook->OnStreamSocketClose(this);
-                       }
-                       catch (CoreException& modexcept)
-                       {
-                               ServerInstance->Logs->Log("SOCKET", DEFAULT,"%s threw an exception: %s",
-                                       modexcept.GetSource(), modexcept.GetReason());
-                       }
-                       IOHook = NULL;
+                       hook->OnStreamSocketClose(this);
+                       IOHook* const nexthook = GetNextHook(hook);
+                       delete hook;
+                       hook = nexthook;
                }
-               ServerInstance->SE->Shutdown(this, 2);
-               ServerInstance->SE->DelFd(this);
-               ServerInstance->SE->Close(this);
-               fd = -1;
+               SocketEngine::Shutdown(this, 2);
+               SocketEngine::Close(this);
        }
 }
 
@@ -153,67 +125,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 (IOHook)
+       if (!hook)
+               return ReadToRecvQ(rq);
+
+       IOHookMiddle* const iohm = IOHookMiddle::ToMiddleHook(hook);
+       if (iohm)
        {
-               int rv = -1;
-               try
-               {
-                       rv = IOHook->OnStreamSocketRead(this, recvq);
-               }
-               catch (CoreException& modexcept)
-               {
-                       ServerInstance->Logs->Log("SOCKET", DEFAULT, "%s threw an exception: %s",
-                               modexcept.GetSource(), modexcept.GetReason());
-                       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 = ServerInstance->SE->Recv(this, ReadBuffer, ServerInstance->Config->NetBufferSize, 0);
+               int n = SocketEngine::Recv(this, ReadBuffer, ServerInstance->Config->NetBufferSize, 0);
                if (n == ServerInstance->Config->NetBufferSize)
                {
-                       ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
-                       recvq.append(ReadBuffer, n);
-                       OnDataReady();
+                       SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
+                       rq.append(ReadBuffer, n);
                }
                else if (n > 0)
                {
-                       ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ);
-                       recvq.append(ReadBuffer, n);
-                       OnDataReady();
+                       SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ);
+                       rq.append(ReadBuffer, n);
                }
                else if (n == 0)
                {
                        error = "Connection closed";
-                       ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+                       SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+                       return -1;
                }
                else if (SocketEngine::IgnoreError())
                {
-                       ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_READ_WILL_BLOCK);
+                       SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_READ_WILL_BLOCK);
+                       return 0;
                }
                else if (errno == EINTR)
                {
-                       ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
+                       SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
+                       return 0;
                }
                else
                {
                        error = SocketEngine::LastError();
-                       ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+                       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 +205,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", DEBUG, "DoWrite on errored or closed socket");
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DoWrite on errored or closed socket");
                return;
        }
 
-#ifndef DISABLE_WRITEV
-       if (IOHook)
-#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 (IOHook)
-                               {
-                                       rv = IOHook->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 = ServerInstance->SE->Send(this, front.data(), itemlen, 0);
-                                       if (rv == 0)
-                                       {
-                                               SetError("Connection closed");
-                                               return;
-                                       }
-                                       else if (rv < 0)
-                                       {
-                                               if (errno == EINTR || SocketEngine::IgnoreError())
-                                                       ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
-                                               else
-                                                       SetError(SocketEngine::LastError());
-                                               return;
-                                       }
-                                       else if (rv < itemlen)
-                                       {
-                                               ServerInstance->SE->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())
-                                                       ServerInstance->SE->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", DEBUG,"%s threw an exception: %s",
-                               modexcept.GetSource(), modexcept.GetReason());
+                       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 +263,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 += iovecs[j].iov_len;
+                               }
+                               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 +291,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;
                                        }
                                }
@@ -407,38 +329,43 @@ void StreamSocket::DoWrite()
                if (!error.empty())
                {
                        // error - kill all events
-                       ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+                       SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
                }
                else
                {
-                       ServerInstance->SE->ChangeEventMask(this, eventChange);
+                       SocketEngine::ChangeEventMask(this, eventChange);
                }
-       }
-#endif
+}
+
+bool StreamSocket::OnSetEndPoint(const irc::sockets::sockaddrs& local, const irc::sockets::sockaddrs& remote)
+{
+       return false;
 }
 
 void StreamSocket::WriteData(const std::string &data)
 {
        if (fd < 0)
        {
-               ServerInstance->Logs->Log("SOCKET", DEBUG, "Attempt to write data to dead socket: %s",
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to write data to dead socket: %s",
                        data.c_str());
                return;
        }
 
        /* Append the data to the back of the queue ready for writing */
        sendq.push_back(data);
-       sendq_len += data.length();
 
-       ServerInstance->SE->ChangeEventMask(this, FD_ADD_TRIAL_WRITE);
+       SocketEngine::ChangeEventMask(this, FD_ADD_TRIAL_WRITE);
 }
 
-void SocketTimeout::Tick(time_t)
+bool SocketTimeout::Tick(time_t)
 {
-       ServerInstance->Logs->Log("SOCKET", DEBUG,"SocketTimeout::Tick");
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SocketTimeout::Tick");
 
-       if (ServerInstance->SE->GetRef(this->sfd) != this->sock)
-               return;
+       if (SocketEngine::GetRef(this->sfd) != this->sock)
+       {
+               delete this;
+               return false;
+       }
 
        if (this->sock->state == I_CONNECTING)
        {
@@ -454,90 +381,143 @@ void SocketTimeout::Tick(time_t)
        }
 
        this->sock->Timeout = NULL;
+       delete this;
+       return false;
 }
 
 void BufferedSocket::OnConnected() { }
 void BufferedSocket::OnTimeout() { return; }
 
-void BufferedSocket::DoWrite()
+void BufferedSocket::OnEventHandlerWrite()
 {
        if (state == I_CONNECTING)
        {
                state = I_CONNECTED;
                this->OnConnected();
-               if (GetIOHook())
-                       GetIOHook()->OnStreamSocketConnect(this);
-               else
-                       ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
+               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)
        {
-               ServerInstance->Timers->DelTimer(Timeout);
-               Timeout = NULL;
+               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", DEFAULT, "Caught exception in socket processing on FD %d - '%s'",
-                       fd, ex.GetReason());
+               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", DEBUG, "Error on FD %d - '%s'", fd, error.c_str());
+               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;
+}
index 534b7ce008558a32ac466eaa786ea1f6c1194560..faf34be91aacacf16c377f2a6cfe1c837ba68d4c 100644 (file)
 
 #include "inspircd.h"
 
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef HAS_STRLCPY
-CoreExport size_t strlcat(char *dst, const char *src, size_t siz)
-{
-       char *d = dst;
-       const char *s = src;
-       size_t n = siz, dlen;
-
-       while (n-- != 0 && *d != '\0')
-               d++;
-
-       dlen = d - dst;
-       n = siz - dlen;
-
-       if (n == 0)
-               return(dlen + strlen(s));
-
-       while (*s != '\0')
-       {
-               if (n != 1)
-               {
-                       *d++ = *s;
-                       n--;
-               }
-
-               s++;
-       }
-
-       *d = '\0';
-       return(dlen + (s - src)); /* count does not include NUL */
-}
-
-CoreExport size_t strlcpy(char *dst, const char *src, size_t siz)
-{
-       char *d = dst;
-       const char *s = src;
-       size_t n = siz;
-
-       /* Copy as many bytes as will fit */
-       if (n != 0 && --n != 0)
-       {
-               do
-               {
-                       if ((*d++ = *s++) == 0)
-                               break;
-               } while (--n != 0);
-       }
-
-       /* Not enough room in dst, add NUL and traverse rest of src */
-       if (n == 0)
-       {
-               if (siz != 0)
-                       *d = '\0'; /* NUL-terminate dst */
-               while (*s++);
-       }
-
-       return(s - src - 1); /* count does not include NUL */
-}
-#endif
-
-CoreExport int charlcat(char* x,char y,int z)
-{
-       char* x__n = x;
-       int v = 0;
-
-       while(*x__n++)
-               v++;
-
-       if (v < z - 1)
-       {
-               *--x__n = y;
-               *++x__n = 0;
-       }
-
-       return v;
-}
-
-CoreExport bool charremove(char* mp, char remove)
-{
-       char* mptr = mp;
-       bool shift_down = false;
-
-       while (*mptr)
-       {
-               if (*mptr == remove)
-               shift_down = true;
-
-               if (shift_down)
-                       *mptr = *(mptr+1);
-
-               mptr++;
-       }
-
-       return shift_down;
-}
-
 static const char hextable[] = "0123456789abcdef";
 
-std::string BinToHex(const std::string& data)
+std::string BinToHex(const void* raw, size_t l)
 {
-       int l = data.length();
+       const char* data = static_cast<const char*>(raw);
        std::string rv;
        rv.reserve(l * 2);
-       for(int i=0; i < l; i++)
+       for (size_t i = 0; i < l; i++)
        {
                unsigned char c = data[i];
-               rv.append(1, hextable[c >> 4]);
-               rv.append(1, hextable[c & 0xF]);
+               rv.push_back(hextable[c >> 4]);
+               rv.push_back(hextable[c & 0xF]);
        }
        return rv;
 }
@@ -230,3 +108,124 @@ 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);
+}
+
+void TokenList::AddList(const std::string& tokenlist)
+{
+       std::string token;
+       irc::spacesepstream tokenstream(tokenlist);
+       while (tokenstream.GetToken(token))
+       {
+               if (token[0] == '-')
+                       Remove(token.substr(1));
+               else
+                       Add(token);
+       }
+}
+void TokenList::Add(const std::string& token)
+{
+       // If the token is empty or contains just whitespace it is invalid.
+       if (token.empty() || token.find_first_not_of(" \t") == std::string::npos)
+               return;
+
+       // If the token is a wildcard entry then permissive mode has been enabled.
+       if (token == "*")
+       {
+               permissive = true;
+               tokens.clear();
+               return;
+       }
+
+       // If we are in permissive mode then remove the token from the token list.
+       // Otherwise, add it to the token list.
+       if (permissive)
+               tokens.erase(token);
+       else
+               tokens.insert(token);
+}
+
+void TokenList::Clear()
+{
+       permissive = false;
+       tokens.clear();
+}
+
+bool TokenList::Contains(const std::string& token) const
+{
+       // If we are in permissive mode and the token is in the list
+       // then we don't have it.
+       if (permissive && tokens.find(token) != tokens.end())
+               return false;
+
+       // If we are not in permissive mode and the token is not in
+       // the list then we don't have it.
+       if (!permissive && tokens.find(token) == tokens.end())
+               return false;
+
+       // We have the token!
+       return true;
+}
+
+void TokenList::Remove(const std::string& token)
+{
+       // If the token is empty or contains just whitespace it is invalid.
+       if (token.empty() || token.find_first_not_of(" \t") == std::string::npos)
+               return;
+
+       // If the token is a wildcard entry then permissive mode has been disabled.
+       if (token == "*")
+       {
+               permissive = false;
+               tokens.clear();
+               return;
+       }
+
+       // If we are in permissive mode then add the token to the token list.
+       // Otherwise, remove it from the token list.
+       if (permissive)
+               tokens.insert(token);
+       else
+               tokens.erase(token);
+}
+
+std::string TokenList::ToString() const
+{
+       std::string buffer(permissive ? "* " : "-* ");
+       buffer.append(stdalgo::string::join(tokens));
+       return buffer;
+}
+
+bool TokenList::operator==(const TokenList& other) const
+{
+       // Both sets must be in the same mode to be equal.
+       if (permissive != other.permissive)
+               return false;
+
+       // Both sets must be the same size to be equal.
+       if (tokens.size() != other.tokens.size())
+               return false;
+
+       for (insp::flat_set<std::string>::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
+       {
+               // Both sets must contain the same tokens to be equal.
+               if (other.tokens.find(*iter) == other.tokens.end())
+                       return false;
+       }
+
+       return true;
+}
index ae11c3b48238b3155cfb2b52c9de0f2dac97ecb2..60ee0b449c0d24ef3c1b1452a78f7c23009aa3e3 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "socketengine.h"
+#include "iohook.h"
+
+#ifndef _WIN32
+#include <netinet/tcp.h>
+#endif
 
 ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_to)
        : bind_tag(tag)
+       , bind_sa(bind_to)
 {
-       irc::sockets::satoap(bind_to, bind_addr, bind_port);
-       bind_desc = irc::sockets::satouser(bind_to);
-
-       fd = socket(bind_to.sa.sa_family, SOCK_STREAM, 0);
+       fd = socket(bind_to.family(), SOCK_STREAM, 0);
 
        if (this->fd == -1)
                return;
@@ -40,7 +41,7 @@ ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_t
         * is "::" or an IPv6 address, disable support so that an IPv4 bind will
         * work on the port (by us or another application).
         */
-       if (bind_to.sa.sa_family == AF_INET6)
+       if (bind_to.family() == AF_INET6)
        {
                std::string addr = tag->getString("address");
                /* This must be >= sizeof(DWORD) on Windows */
@@ -51,23 +52,53 @@ ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_t
        }
 #endif
 
-       ServerInstance->SE->SetReuse(fd);
-       int rv = ServerInstance->SE->Bind(this->fd, bind_to);
+       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 = ServerInstance->SE->Listen(this->fd, ServerInstance->Config->MaxConn);
+               rv = SocketEngine::Listen(this->fd, ServerInstance->Config->MaxConn);
+
+       // Default defer to on for TLS listeners because in TLS the client always speaks first
+       int timeout = tag->getDuration("defer", (tag->getString("ssl").empty() ? 0 : 3));
+       if (timeout && !rv)
+       {
+#if defined TCP_DEFER_ACCEPT
+               setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(timeout));
+#elif defined SO_ACCEPTFILTER
+               struct accept_filter_arg afa;
+               memset(&afa, 0, sizeof(afa));
+               strcpy(afa.af_name, "dataready");
+               setsockopt(fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
+#endif
+       }
 
        if (rv < 0)
        {
                int errstore = errno;
-               ServerInstance->SE->Shutdown(this, 2);
-               ServerInstance->SE->Close(this);
+               SocketEngine::Shutdown(this, 2);
+               SocketEngine::Close(this->GetFd());
                this->fd = -1;
                errno = errstore;
        }
        else
        {
-               ServerInstance->SE->NonBlocking(this->fd);
-               ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+               SocketEngine::NonBlocking(this->fd);
+               SocketEngine::AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+
+               this->ResetIOHookProvider();
        }
 }
 
@@ -75,58 +106,39 @@ ListenSocket::~ListenSocket()
 {
        if (this->GetFd() > -1)
        {
-               ServerInstance->SE->DelFd(this);
-               ServerInstance->Logs->Log("SOCKET", DEBUG,"Shut down listener on fd %d", this->fd);
-               ServerInstance->SE->Shutdown(this, 2);
-               if (ServerInstance->SE->Close(this) != 0)
-                       ServerInstance->Logs->Log("SOCKET", DEBUG,"Failed to cancel listener: %s", strerror(errno));
-               this->fd = -1;
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Shut down listener on fd %d", this->fd);
+               SocketEngine::Shutdown(this, 2);
+
+               if (SocketEngine::Close(this) != 0)
+                       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Failed to cancel listener: %s", strerror(errno));
+
+               if (bind_sa.family() == AF_UNIX && unlink(bind_sa.un.sun_path))
+                       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Failed to unlink UNIX socket: %s", strerror(errno));
        }
 }
 
-/* Just seperated into another func for tidiness really.. */
-void ListenSocket::AcceptInternal()
+void ListenSocket::OnEventHandlerRead()
 {
        irc::sockets::sockaddrs client;
-       irc::sockets::sockaddrs server;
+       irc::sockets::sockaddrs server(bind_sa);
 
        socklen_t length = sizeof(client);
-       int incomingSockfd = ServerInstance->SE->Accept(this, &client.sa, &length);
+       int incomingSockfd = SocketEngine::Accept(this, &client.sa, &length);
 
-       ServerInstance->Logs->Log("SOCKET",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_sa.str().c_str(), incomingSockfd);
        if (incomingSockfd < 0)
        {
-               ServerInstance->stats->statsRefused++;
+               ServerInstance->stats.Refused++;
                return;
        }
 
        socklen_t sz = sizeof(server);
        if (getsockname(incomingSockfd, &server.sa, &sz))
        {
-               ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
-               irc::sockets::aptosa(bind_addr, bind_port, server);
-       }
-
-       /*
-        * XXX -
-        * this is done as a safety check to keep the file descriptors within range of fd_ref_table.
-        * its a pretty big but for the moment valid assumption:
-        * file descriptors are handed out starting at 0, and are recycled as theyre freed.
-        * therefore if there is ever an fd over 65535, 65536 clients must be connected to the
-        * irc server at once (or the irc server otherwise initiating this many connections, files etc)
-        * which for the time being is a physical impossibility (even the largest networks dont have more
-        * than about 10,000 users on ONE server!)
-        */
-       if (incomingSockfd >= ServerInstance->SE->GetMaxFds())
-       {
-               ServerInstance->Logs->Log("SOCKET", DEBUG, "Server is full");
-               ServerInstance->SE->Shutdown(incomingSockfd, 2);
-               ServerInstance->SE->Close(incomingSockfd);
-               ServerInstance->stats->statsRefused++;
-               return;
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Can't get peername: %s", strerror(errno));
        }
 
-       if (client.sa.sa_family == AF_INET6)
+       if (client.family() == AF_INET6)
        {
                /*
                 * This case is the be all and end all patch to catch and nuke 4in6
@@ -155,15 +167,23 @@ void ListenSocket::AcceptInternal()
                        memcpy(&server.in4.sin_addr.s_addr, server.in6.sin6_addr.s6_addr + 12, sizeof(uint32_t));
                }
        }
+       else if (client.family() == AF_UNIX)
+       {
+               // Clients connecting via UNIX sockets don't have paths so give them
+               // the server path as defined in RFC 1459 section 8.1.1.
+               //
+               // strcpy is safe here because sizeof(sockaddr_un.sun_path) is equal on both.
+               strcpy(client.un.sun_path, server.un.sun_path);
+       }
 
-       ServerInstance->SE->NonBlocking(incomingSockfd);
+       SocketEngine::NonBlocking(incomingSockfd);
 
        ModResult res;
        FIRST_MOD_RESULT(OnAcceptConnection, res, (incomingSockfd, this, &client, &server));
        if (res == MOD_RES_PASSTHRU)
        {
                std::string type = bind_tag->getString("type", "clients");
-               if (type == "clients")
+               if (stdalgo::string::equalsci(type, "clients"))
                {
                        ServerInstance->Users->AddUser(incomingSockfd, this, &client, &server);
                        res = MOD_RES_ALLOW;
@@ -171,29 +191,34 @@ void ListenSocket::AcceptInternal()
        }
        if (res == MOD_RES_ALLOW)
        {
-               ServerInstance->stats->statsAccept++;
+               ServerInstance->stats.Accept++;
        }
        else
        {
-               ServerInstance->stats->statsRefused++;
-               ServerInstance->Logs->Log("SOCKET",DEFAULT,"Refusing connection on %s - %s",
-                       bind_desc.c_str(), res == MOD_RES_DENY ? "Connection refused by module" : "Module for this port not found");
-               ServerInstance->SE->Close(incomingSockfd);
+               ServerInstance->stats.Refused++;
+               ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Refusing connection on %s - %s",
+                       bind_sa.str().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",DEFAULT,"ListenSocket::HandleEvent() received a socket engine error event! well shit! '%s'", strerror(err));
-                       break;
-               case EVENT_WRITE:
-                       ServerInstance->Logs->Log("SOCKET",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());
        }
+
+       std::string provname = bind_tag->getString("ssl");
+       if (!provname.empty())
+               provname.insert(0, "ssl/");
+
+       // SSL should be the last
+       iohookprovs.back().SetProvider(provname);
 }
diff --git a/src/listmode.cpp b/src/listmode.cpp
new file mode 100644 (file)
index 0000000..1ce0da5
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * 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"
+#include "listmode.h"
+
+ListModeBase::ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string& eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy)
+       : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_LIST)
+       , listnumeric(lnum)
+       , endoflistnumeric(eolnum)
+       , endofliststring(eolstr)
+       , tidy(autotidy)
+       , extItem(name + "_mode_list", ExtensionItem::EXT_CHANNEL, Creator)
+{
+       list = true;
+}
+
+void ListModeBase::DisplayList(User* user, Channel* channel)
+{
+       ChanData* cd = extItem.get(channel);
+       if (cd)
+       {
+               for (ModeList::const_iterator it = cd->list.begin(); it != cd->list.end(); ++it)
+               {
+                       user->WriteNumeric(listnumeric, channel->name, it->mask, it->setter, (unsigned long) it->time);
+               }
+       }
+       user->WriteNumeric(endoflistnumeric, channel->name, endofliststring);
+}
+
+void ListModeBase::DisplayEmptyList(User* user, Channel* channel)
+{
+       user->WriteNumeric(endoflistnumeric, channel->name, endofliststring);
+}
+
+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++)
+               {
+                       changelist.push_remove(this, it->mask);
+               }
+       }
+}
+
+void ListModeBase::DoRehash()
+{
+       ConfigTagList tags = ServerInstance->Config->ConfTags("maxlist");
+       limitlist newlimits;
+       for (ConfigIter i = tags.first; i != tags.second; i++)
+       {
+               ConfigTag* c = i->second;
+
+               const std::string mname = c->getString("mode");
+               if (!mname.empty() && !stdalgo::string::equalsci(mname, name) && !(mname.length() == 1 && GetModeChar() == mname[0]))
+                       continue;
+
+               ListLimit limit(c->getString("chan", "*"), c->getUInt("limit", 0));
+
+               if (limit.mask.empty())
+                       throw ModuleException(InspIRCd::Format("<maxlist:chan> is empty, at %s", c->getTagLocation().c_str()));
+
+               if (limit.limit <= 0)
+                       throw ModuleException(InspIRCd::Format("<maxlist:limit> must be non-zero, at %s", c->getTagLocation().c_str()));
+
+               newlimits.push_back(limit);
+       }
+
+       // 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.
+       newlimits.push_back(ListLimit("*", DEFAULT_LIST_SIZE));
+
+       // Most of the time our settings are unchanged, so we can avoid iterating the chanlist
+       if (chanlimits == newlimits)
+               return;
+
+       chanlimits.swap(newlimits);
+
+       const chan_hash& chans = ServerInstance->GetChans();
+       for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+       {
+               ChanData* cd = extItem.get(i->second);
+               if (cd)
+                       cd->maxitems = -1;
+       }
+}
+
+unsigned int ListModeBase::FindLimit(const std::string& channame)
+{
+       for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); ++it)
+       {
+               if (InspIRCd::Match(channame, it->mask))
+               {
+                       // We have a pattern matching the channel
+                       return it->limit;
+               }
+       }
+       return DEFAULT_LIST_SIZE;
+}
+
+unsigned int ListModeBase::GetLimitInternal(const std::string& channame, ChanData* cd)
+{
+       if (cd->maxitems < 0)
+               cd->maxitems = FindLimit(channame);
+       return cd->maxitems;
+}
+
+unsigned int ListModeBase::GetLimit(Channel* channel)
+{
+       ChanData* cd = extItem.get(channel);
+       if (!cd) // just find the limit
+               return FindLimit(channel->name);
+
+       return GetLimitInternal(channel->name, cd);
+}
+
+unsigned int ListModeBase::GetLowerLimit()
+{
+       unsigned int limit = UINT_MAX;
+       for (limitlist::iterator iter = chanlimits.begin(); iter != chanlimits.end(); ++iter)
+       {
+               if (iter->limit < limit)
+                       limit = iter->limit;
+       }
+       return limit == UINT_MAX ? DEFAULT_LIST_SIZE : limit;
+}
+
+ModeAction ListModeBase::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
+{
+       // Try and grab the list
+       ChanData* cd = extItem.get(channel);
+
+       if (adding)
+       {
+               if (tidy)
+                       ModeParser::CleanMask(parameter);
+
+               if (parameter.length() > 250)
+                       return MODEACTION_DENY;
+
+               // If there was no list
+               if (!cd)
+               {
+                       // Make one
+                       cd = new ChanData;
+                       extItem.set(channel, cd);
+               }
+
+               // Check if the item already exists in the list
+               for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++)
+               {
+                       if (parameter == it->mask)
+                       {
+                               /* Give a subclass a chance to error about this */
+                               TellAlreadyOnList(source, channel, parameter);
+
+                               // it does, deny the change
+                               return MODEACTION_DENY;
+                       }
+               }
+
+               if ((IS_LOCAL(source)) && (cd->list.size() >= GetLimitInternal(channel->name, cd)))
+               {
+                       /* List is full, give subclass a chance to send a custom message */
+                       TellListTooLong(source, channel, parameter);
+                       return MODEACTION_DENY;
+               }
+
+               /* Ok, it *could* be allowed, now give someone subclassing us
+                * a chance to validate the parameter.
+                * The param is passed by reference, so they can both modify it
+                * and tell us if we allow it or not.
+                *
+                * eg, the subclass could:
+                * 1) allow
+                * 2) 'fix' parameter and then allow
+                * 3) deny
+                */
+               if (ValidateParam(source, channel, parameter))
+               {
+                       // And now add the mask onto the list...
+                       cd->list.push_back(ListItem(parameter, source->nick, ServerInstance->Time()));
+                       return MODEACTION_ALLOW;
+               }
+               else
+               {
+                       /* If they deny it they have the job of giving an error message */
+                       return MODEACTION_DENY;
+               }
+       }
+       else
+       {
+               // We're taking the mode off
+               if (cd)
+               {
+                       for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); ++it)
+                       {
+                               if (parameter == it->mask)
+                               {
+                                       stdalgo::vector::swaperase(cd->list, it);
+                                       return MODEACTION_ALLOW;
+                               }
+                       }
+               }
+
+               /* Tried to remove something that wasn't set */
+               TellNotSet(source, channel, parameter);
+               return MODEACTION_DENY;
+       }
+}
+
+bool ListModeBase::ValidateParam(User*, Channel*, std::string&)
+{
+       return true;
+}
+
+void ListModeBase::OnParameterMissing(User*, User*, Channel*)
+{
+       // Intentionally left blank.
+}
+
+void ListModeBase::TellListTooLong(User* source, Channel* channel, std::string& parameter)
+{
+       source->WriteNumeric(ERR_BANLISTFULL, channel->name, parameter, mode, InspIRCd::Format("Channel %s list is full", name.c_str()));
+}
+
+void ListModeBase::TellAlreadyOnList(User* source, Channel* channel, std::string& parameter)
+{
+       source->WriteNumeric(ERR_LISTMODEALREADYSET, channel->name, parameter, mode, InspIRCd::Format("Channel %s list already contains %s", name.c_str(), parameter.c_str()));
+}
+
+void ListModeBase::TellNotSet(User* source, Channel* channel, std::string& parameter)
+{
+       source->WriteNumeric(ERR_LISTMODENOTSET, channel->name, parameter, mode, InspIRCd::Format("Channel %s list does not contain %s", name.c_str(), parameter.c_str()));
+}
index 89b2be019721289c241b4839bb591820215b1b84..b9f9bae0b7c866795d0382b593524bd7807543ec 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-#include "filelogger.h"
-
 /*
  * Suggested implementation...
  *     class LogManager
  *
  */
 
+const char LogStream::LogHeader[] =
+       "Log started for " INSPIRCD_VERSION " (" MODULE_INIT_STR ")";
+
 LogManager::LogManager()
+       : Logging(false)
 {
-       Logging = false;
 }
 
 LogManager::~LogManager()
@@ -76,49 +77,49 @@ void LogManager::OpenFileLogs()
        {
                ConfigTag* tag = i->second;
                std::string method = tag->getString("method");
-               if (method != "file")
+               if (!stdalgo::string::equalsci(method, "file"))
                {
                        continue;
                }
                std::string type = tag->getString("type");
                std::string level = tag->getString("level");
-               int loglevel = DEFAULT;
-               if (level == "rawio")
+               LogLevel loglevel = LOG_DEFAULT;
+               if (stdalgo::string::equalsci(level, "rawio"))
                {
-                       loglevel = RAWIO;
+                       loglevel = LOG_RAWIO;
                        ServerInstance->Config->RawLog = true;
                }
-               else if (level == "debug")
+               else if (stdalgo::string::equalsci(level, "debug"))
                {
-                       loglevel = DEBUG;
+                       loglevel = LOG_DEBUG;
                }
-               else if (level == "verbose")
+               else if (stdalgo::string::equalsci(level, "verbose"))
                {
-                       loglevel = VERBOSE;
+                       loglevel = LOG_VERBOSE;
                }
-               else if (level == "default")
+               else if (stdalgo::string::equalsci(level, "default"))
                {
-                       loglevel = DEFAULT;
+                       loglevel = LOG_DEFAULT;
                }
-               else if (level == "sparse")
+               else if (stdalgo::string::equalsci(level, "sparse"))
                {
-                       loglevel = SPARSE;
+                       loglevel = LOG_SPARSE;
                }
-               else if (level == "none")
+               else if (stdalgo::string::equalsci(level, "none"))
                {
-                       loglevel = NONE;
+                       loglevel = LOG_NONE;
                }
                FileWriter* fw;
-               std::string target = tag->getString("target");
+               std::string target = ServerInstance->Config->Paths.PrependLog(tag->getString("target"));
                std::map<std::string, FileWriter*>::iterator fwi = logmap.find(target);
                if (fwi == logmap.end())
                {
-                       char realtarget[MAXBUF];
+                       char realtarget[256];
                        time_t time = ServerInstance->Time();
                        struct tm *mytime = gmtime(&time);
-                       strftime(realtarget, MAXBUF, target.c_str(), mytime);
+                       strftime(realtarget, sizeof(realtarget), target.c_str(), mytime);
                        FILE* f = fopen(realtarget, "a");
-                       fw = new FileWriter(f);
+                       fw = new FileWriter(f, tag->getUInt("flush", 20, 1, UINT_MAX));
                        logmap.insert(std::make_pair(target, fw));
                }
                else
@@ -126,7 +127,7 @@ void LogManager::OpenFileLogs()
                        fw = fwi->second;
                }
                FileLogStream* fls = new FileLogStream(loglevel, fw);
-               fls->OnLog(SPARSE, "HEADER", InspIRCd::LogHeader);
+               fls->OnLog(LOG_SPARSE, "HEADER", LogStream::LogHeader);
                AddLogTypes(type, fls, true);
        }
 }
@@ -135,13 +136,16 @@ void LogManager::CloseLogs()
 {
        if (ServerInstance->Config && ServerInstance->Config->cmdline.forcedebug)
                return;
-       std::map<std::string, std::vector<LogStream*> >().swap(LogStreams); /* Clear it */
-       std::map<LogStream*, std::vector<std::string> >().swap(GlobalLogStreams); /* Clear it */
+
+       LogStreams.clear();
+       GlobalLogStreams.clear();
+
        for (std::map<LogStream*, int>::iterator i = AllLogStreams.begin(); i != AllLogStreams.end(); ++i)
        {
                delete i->first;
        }
-       std::map<LogStream*, int>().swap(AllLogStreams); /* And clear it */
+
+       AllLogStreams.clear();
 }
 
 void LogManager::AddLogTypes(const std::string &types, LogStream* l, bool autoclose)
@@ -187,36 +191,13 @@ void LogManager::AddLogTypes(const std::string &types, LogStream* l, bool autocl
 
 bool LogManager::AddLogType(const std::string &type, LogStream *l, bool autoclose)
 {
-       std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
-
-       if (i != LogStreams.end())
-       {
-               i->second.push_back(l);
-       }
-       else
-       {
-               std::vector<LogStream *> v;
-               v.push_back(l);
-               LogStreams[type] = v;
-       }
+       LogStreams[type].push_back(l);
 
        if (type == "*")
-       {
                GlobalLogStreams.insert(std::make_pair(l, std::vector<std::string>()));
-       }
 
        if (autoclose)
-       {
-               std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
-               if (ai == AllLogStreams.end())
-               {
-                       AllLogStreams.insert(std::make_pair(l, 1));
-               }
-               else
-               {
-                       ++ai->second;
-               }
-       }
+               AllLogStreams[l]++;
 
        return true;
 }
@@ -225,24 +206,20 @@ 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))
                {
-                       if (it == i->second.end())
-                               continue;
-                       i->second.erase(it);
+                       // Keep erasing while it exists
                }
        }
-       std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
-       if (gi != GlobalLogStreams.end())
-       {
-               GlobalLogStreams.erase(gi);
-       }
+
+       GlobalLogStreams.erase(l);
+
        std::map<LogStream*, int>::iterator ai = AllLogStreams.begin();
        if (ai == AllLogStreams.end())
        {
                return; /* Done. */
        }
+
        delete ai->first;
        AllLogStreams.erase(ai);
 }
@@ -252,17 +229,13 @@ bool LogManager::DelLogType(const std::string &type, LogStream *l)
        std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
        if (type == "*")
        {
-               std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
-               if (gi != GlobalLogStreams.end()) GlobalLogStreams.erase(gi);
+               GlobalLogStreams.erase(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,24 +266,17 @@ bool LogManager::DelLogType(const std::string &type, LogStream *l)
        return true;
 }
 
-void LogManager::Log(const std::string &type, int loglevel, const char *fmt, ...)
+void LogManager::Log(const std::string &type, LogLevel loglevel, const char *fmt, ...)
 {
        if (Logging)
-       {
                return;
-       }
-
-       va_list a;
-       static char buf[65536];
-
-       va_start(a, fmt);
-       vsnprintf(buf, 65536, fmt, a);
-       va_end(a);
 
-       this->Log(type, loglevel, std::string(buf));
+       std::string buf;
+       VAFORMAT(buf, fmt, fmt);
+       this->Log(type, loglevel, buf);
 }
 
-void LogManager::Log(const std::string &type, int loglevel, const std::string &msg)
+void LogManager::Log(const std::string &type, LogLevel loglevel, const std::string &msg)
 {
        if (Logging)
        {
@@ -321,7 +287,7 @@ void LogManager::Log(const std::string &type, int loglevel, const std::string &m
 
        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;
                }
@@ -342,8 +308,10 @@ void LogManager::Log(const std::string &type, int loglevel, const std::string &m
 }
 
 
-FileWriter::FileWriter(FILE* logfile)
-: log(logfile), writeops(0)
+FileWriter::FileWriter(FILE* logfile, unsigned int flushcount)
+       : log(logfile)
+       , flush(flushcount)
+       , writeops(0)
 {
 }
 
@@ -354,8 +322,8 @@ void FileWriter::WriteLogLine(const std::string &line)
 // XXX: For now, just return. Don't throw an exception. It'd be nice to find out if this is happening, but I'm terrified of breaking so close to final release. -- w00t
 //             throw CoreException("FileWriter::WriteLogLine called with a closed logfile");
 
-       fprintf(log,"%s",line.c_str());
-       if (writeops++ % 20)
+       fputs(line.c_str(), log);
+       if (++writeops % flush == 0)
        {
                fflush(log);
        }
index 0f5d457834ba33538a28abb713c193751b9f6d71..aa4d50b08ac21deee4c511cde8d75b2cb9b34dd9 100644 (file)
 
 #include "inspircd.h"
 
-/* +s (secret) */
-/* +p (private) */
-/* +m (moderated) */
-/* +t (only (half) ops can change topic) */
-/* +n (no external messages) */
-/* +i (invite only) */
-/* +w (see wallops) */
-/* +i (invisible) */
-#include "modes/simplemodes.h"
-/* +b (bans) */
-#include "modes/cmode_b.h"
-/* +k (keyed channel) */
-#include "modes/cmode_k.h"
-/* +l (channel user limit) */
-#include "modes/cmode_l.h"
-/* +o (channel op) */
-#include "modes/cmode_o.h"
-/* +v (channel voice) */
-#include "modes/cmode_v.h"
-/* +o (operator) */
-#include "modes/umode_o.h"
-/* +s (server notice masks) */
-#include "modes/umode_s.h"
-
-ModeHandler::ModeHandler(Module* Creator, const std::string& Name, char modeletter, ParamSpec Params, ModeType type)
-       : ServiceProvider(Creator, Name, SERVICE_MODE), m_paramtype(TR_TEXT),
-       parameters_taken(Params), mode(modeletter), prefix(0), oper(false),
-       list(false), m_type(type), levelrequired(HALFOP_VALUE)
+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)
+       , parameters_taken(Params)
+       , mode(modeletter)
+       , oper(false)
+       , list(false)
+       , m_type(type)
+       , type_id(mclass)
+       , ranktoset(HALFOP_VALUE)
+       , ranktounset(HALFOP_VALUE)
 {
 }
 
 CullResult ModeHandler::cull()
 {
-       if (ServerInstance->Modes)
+       if (ServerInstance)
                ServerInstance->Modes->DelMode(this);
        return classbase::cull();
 }
@@ -67,31 +50,21 @@ ModeHandler::~ModeHandler()
 {
 }
 
-bool ModeHandler::IsListMode()
-{
-       return list;
-}
-
-unsigned int ModeHandler::GetPrefixRank()
-{
-       return 0;
-}
-
-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 "";
 }
@@ -116,6 +89,11 @@ void ModeHandler::DisplayEmptyList(User*, Channel*)
 
 void ModeHandler::OnParameterMissing(User* user, User* dest, Channel* channel)
 {
+       const std::string message = InspIRCd::Format("You must specify a parameter for the %s mode", name.c_str());
+       if (channel)
+               user->WriteNumeric(Numerics::InvalidModeParameter(channel, this, "*", message));
+       else
+               user->WriteNumeric(Numerics::InvalidModeParameter(dest, this, "*", message));
 }
 
 bool ModeHandler::ResolveModeConflict(std::string& theirs, const std::string& ours, Channel*)
@@ -123,11 +101,17 @@ 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
                remove a mode we don't have, deny. */
-       if (dest->IsModeSet(this->GetModeChar()) == adding)
+       if (dest->IsModeSet(this) == adding)
                return MODEACTION_DENY;
 
        /* adding will be either true or false, depending on if we
@@ -136,7 +120,7 @@ ModeAction SimpleUserModeHandler::OnModeChange(User* source, User* dest, Channel
                aren't removing a mode we don't have, we don't have to do any
                other checks here to see if it's true or false, just add or
                remove the mode */
-       dest->SetMode(this->GetModeChar(), adding);
+       dest->SetMode(this, adding);
 
        return MODEACTION_ALLOW;
 }
@@ -146,7 +130,7 @@ ModeAction SimpleChannelModeHandler::OnModeChange(User* source, User* dest, Chan
 {
        /* We're either trying to add a mode we already have or
                remove a mode we don't have, deny. */
-       if (channel->IsModeSet(this->GetModeChar()) == adding)
+       if (channel->IsModeSet(this) == adding)
                return MODEACTION_DENY;
 
        /* adding will be either true or false, depending on if we
@@ -155,120 +139,124 @@ ModeAction SimpleChannelModeHandler::OnModeChange(User* source, User* dest, Chan
                aren't removing a mode we don't have, we don't have to do any
                other checks here to see if it's true or false, just add or
                remove the mode */
-       channel->SetMode(this->GetModeChar(), adding);
+       channel->SetMode(this, adding);
 
        return MODEACTION_ALLOW;
 }
 
-ModeAction ParamChannelModeHandler::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ModeWatcher::ModeWatcher(Module* Creator, const std::string& modename, ModeType type)
+       : mode(modename), m_type(type), creator(Creator)
 {
-       if (adding && !ParamValidate(parameter))
-               return MODEACTION_DENY;
-       std::string now = channel->GetModeParameter(this);
-       if (parameter == now)
-               return MODEACTION_DENY;
-       if (adding)
-               channel->SetModeParam(this, parameter);
-       else
-               channel->SetModeParam(this, "");
-       return MODEACTION_ALLOW;
+       ServerInstance->Modes->AddModeWatcher(this);
 }
 
-bool ParamChannelModeHandler::ParamValidate(std::string& parameter)
+ModeWatcher::~ModeWatcher()
 {
-       return true;
+       ServerInstance->Modes->DelModeWatcher(this);
 }
 
-ModeWatcher::ModeWatcher(Module* Creator, char modeletter, ModeType type)
-       : mode(modeletter), m_type(type), creator(Creator)
+bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool)
 {
+       return true;
 }
 
-ModeWatcher::~ModeWatcher()
+void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool)
 {
 }
 
-char ModeWatcher::GetModeChar()
+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(PrefixChar)
+       , prefixrank(Rank)
+       , selfremove(true)
 {
-       return mode;
+       list = true;
 }
 
-ModeType ModeWatcher::GetModeType()
+ModResult PrefixMode::AccessCheck(User* src, Channel*, std::string& value, bool adding)
 {
-       return m_type;
+       if (!adding && src->nick == value && selfremove)
+               return MOD_RES_ALLOW;
+       return MOD_RES_PASSTHRU;
 }
 
-bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool, ModeType)
+ModeAction PrefixMode::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
 {
-       return true;
-}
+       User* target;
+       if (IS_LOCAL(source))
+               target = ServerInstance->FindNickOnly(parameter);
+       else
+               target = ServerInstance->FindNick(parameter);
 
-void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool, ModeType)
-{
+       if (!target)
+       {
+               source->WriteNumeric(Numerics::NoSuchNick(parameter));
+               return MODEACTION_DENY;
+       }
+
+       Membership* memb = chan->GetUser(target);
+       if (!memb)
+               return MODEACTION_DENY;
+
+       parameter = target->nick;
+       return (memb->SetPrefix(this, adding) ? MODEACTION_ALLOW : MODEACTION_DENY);
 }
 
-User* ModeParser::SanityChecks(User *user, const char *dest, Channel *chan, int)
+void PrefixMode::Update(unsigned int rank, unsigned int setrank, unsigned int unsetrank, bool selfrm)
 {
-       User *d;
-       if ((!user) || (!dest) || (!chan) || (!*dest))
-       {
-               return NULL;
-       }
-       d = ServerInstance->FindNick(dest);
-       if (!d)
-       {
-               user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), dest);
-               return NULL;
-       }
-       return d;
+       prefixrank = rank;
+       ranktoset = setrank;
+       ranktounset = unsetrank;
+       selfremove = selfrm;
 }
 
-void ModeParser::DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text)
+ModeAction ParamModeBase::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
 {
-       if (targetchannel)
+       if (adding)
        {
-               /* Display channel's current mode string */
-               user->WriteNumeric(RPL_CHANNELMODEIS, "%s %s +%s",user->nick.c_str(), targetchannel->name.c_str(), targetchannel->ChanModes(targetchannel->HasUser(user)));
-               user->WriteNumeric(RPL_CHANNELCREATED, "%s %s %lu", user->nick.c_str(), targetchannel->name.c_str(), (unsigned long)targetchannel->age);
-               return;
+               if (chan->GetModeParameter(this) == parameter)
+                       return MODEACTION_DENY;
+
+               if (OnSet(source, chan, parameter) != MODEACTION_ALLOW)
+                       return MODEACTION_DENY;
+
+               chan->SetMode(this, true);
+
+               // Handler might have changed the parameter internally
+               parameter.clear();
+               this->GetParameter(chan, parameter);
        }
        else
        {
-               if (targetuser == user || user->HasPrivPermission("users/auspex"))
-               {
-                       /* Display user's current mode string */
-                       user->WriteNumeric(RPL_UMODEIS, "%s :+%s",targetuser->nick.c_str(),targetuser->FormatModes());
-                       if (IS_OPER(targetuser))
-                               user->WriteNumeric(RPL_SNOMASKIS, "%s +%s :Server notice mask", targetuser->nick.c_str(), targetuser->FormatNoticeMasks());
-                       return;
-               }
-               else
-               {
-                       user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't view modes for other users", user->nick.c_str());
-                       return;
-               }
+               if (!chan->IsModeSet(this))
+                       return MODEACTION_DENY;
+               this->OnUnsetInternal(source, chan);
+               chan->SetMode(this, false);
        }
+       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;
-       unsigned char mask = chan ? MASK_CHANNEL : MASK_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, modechar, parameter, adding, pcnt));
+       FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mh, parameter, adding));
 
        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);
@@ -277,7 +265,7 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
                        return MODEACTION_DENY;
                if (MOD_RESULT == MOD_RES_PASSTHRU)
                {
-                       unsigned int neededrank = mh->GetLevelRequired();
+                       unsigned int neededrank = mh->GetLevelRequired(adding);
                        /* Compare our rank on the channel against the rank of the required prefix,
                         * allow if >= ours. Because mIRC and xchat throw a tizz if the modes shown
                         * in NAMES(X) are not in rank order, we know the most powerful mode is listed
@@ -286,11 +274,12 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
                        unsigned int ourrank = chan->GetPrefixValue(user);
                        if (ourrank < neededrank)
                        {
-                               ModeHandler* 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)
                                {
-                                       ModeHandler *privmh = FindMode(c, MODETYPE_CHANNEL);
-                                       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())
@@ -298,148 +287,76 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool
                                        }
                                }
                                if (neededmh)
-                                       user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must have channel %s access or above to %sset channel mode %c",
-                                               user->nick.c_str(), 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 %s :You cannot %sset channel mode %c",
-                                               user->nick.c_str(), 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;
                        }
                }
        }
 
-       unsigned char handler_id = (modechar - 'A') | mask;
-
-       for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
+       // Ask mode watchers whether this mode change is OK
+       std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mh->name);
+       for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
        {
-               if ((*watchers)->BeforeMode(user, targetuser, chan, parameter, adding, type) == false)
-                       return MODEACTION_DENY;
-               /* A module whacked the parameter completely, and there was one. abort. */
-               if (pcnt && parameter.empty())
-                       return MODEACTION_DENY;
-       }
-
-       if (IS_LOCAL(user) && !IS_OPER(user))
-       {
-               char* disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes;
-               if (disabled[modechar - 'A'])
+               ModeWatcher* mw = i->second;
+               if (mw->GetModeType() == type)
                {
-                       user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - %s mode %c has been locked by the administrator",
-                               user->nick.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
-                       return MODEACTION_DENY;
+                       if (!mw->BeforeMode(user, targetuser, chan, parameter, adding))
+                               return MODEACTION_DENY;
+
+                       // A module whacked the parameter completely, and there was one. Abort.
+                       if ((needs_param) && (parameter.empty()))
+                               return MODEACTION_DENY;
                }
        }
 
-       if (adding && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(modechar, type))
+       if ((chan || (!chan && adding)) && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(mh))
        {
                /* It's an oper only mode, and they don't have access to it. */
-               if (IS_OPER(user))
+               if (user->IsOper())
                {
-                       user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to set %s mode %c",
-                                       user->nick.c_str(), user->oper->NameStr(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+                       user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper type %s does not have access to %sset %s mode %c",
+                                       user->oper->name.c_str(), adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
                }
                else
                {
-                       user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Only operators may set %s mode %c",
-                                       user->nick.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+                       user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Only operators may %sset %s mode %c",
+                                       adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
                }
                return MODEACTION_DENY;
        }
 
-       if (mh->GetTranslateType() == TR_NICK)
-       {
-               User* prefixtarget;
-               if (IS_LOCAL(user))
-                       prefixtarget = ServerInstance->FindNickOnly(parameter);
-               else
-                       prefixtarget = ServerInstance->FindNick(parameter);
-
-               if (!prefixtarget)
-               {
-                       user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameter.c_str());
-                       return MODEACTION_DENY;
-               }
-       }
-
-       if (mh->GetPrefixRank() && chan)
-       {
-               User* user_to_prefix = ServerInstance->FindNick(parameter);
-               if (!user_to_prefix)
-                       return MODEACTION_DENY;
-               if (!chan->SetPrefix(user_to_prefix, modechar, adding))
-                       return MODEACTION_DENY;
-       }
-
        /* 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;
 
-       for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
-               (*watchers)->AfterMode(user, targetuser, chan, parameter, adding, type);
+       itpair = modewatchermap.equal_range(mh->name);
+       for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
+       {
+               ModeWatcher* mw = i->second;
+               if (mw->GetModeType() == type)
+                       mw->AfterMode(user, targetuser, chan, parameter, adding);
+       }
 
        return MODEACTION_ALLOW;
 }
 
-void ModeParser::Process(const std::vector<std::string>& parameters, User *user, bool merge)
+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;
-
-       LastParse.clear();
-       LastParseParams.clear();
-       LastParseTranslate.clear();
-
-       if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser))))
-       {
-               user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(),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) || ServerInstance->ULine(user->server) || MOD_RESULT == MOD_RES_ALLOW)
-               SkipAccessChecks = true;
-       else if (MOD_RESULT == MOD_RES_DENY)
-               return;
-
-       if (targetuser && !SkipAccessChecks && user != targetuser)
-       {
-               user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't change mode for other users", user->nick.c_str());
-               return;
-       }
-
-       std::string mode_sequence = parameters[1];
+       if (endindex > parameters.size())
+               endindex = parameters.size();
 
-       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++)
        {
@@ -454,143 +371,154 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User *user,
                if (!mh)
                {
                        /* No mode handler? Unknown mode character then. */
-                       user->WriteServ("%d %s %c :is unknown mode char to me", type == MODETYPE_CHANNEL ? 472 : 501, user->nick.c_str(), modechar);
+                       user->WriteNumeric(type == MODETYPE_CHANNEL ? ERR_UNKNOWNMODE : ERR_UNKNOWNSNOMASK, modechar, "is an unknown mode character");
                        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);
+               if ((mh->NeedsParam(adding)) && (param_at < endindex))
+                       parameter = parameters[param_at++];
+
+               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)
+{
+       LastChangeList.clear();
+
+       unsigned int modes_processed = 0;
+       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 a mode change has been given for a mode that does not exist then reject
+               // it. This can happen when core_reloadmodule attempts to restore a mode that
+               // no longer exists.
+               if (!mh)
                        continue;
-               }
-               else if (pcnt)
+
+               // 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))
                {
-                       parameter = parameters[param_at++];
-                       /* Make sure the user isn't trying to slip in an invalid parameter */
-                       if ((parameter.empty()) || (parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos))
+                       // 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 (merge && targetchannel && targetchannel->IsModeSet(modechar) && !mh->IsListMode())
-                       {
-                               std::string ours = targetchannel->GetModeParameter(modechar);
-                               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 ? '+' : '-';
-               if (needed_pm != output_pm)
-               {
-                       output_pm = needed_pm;
-                       output_mode.append(1, output_pm);
-               }
-               output_mode.append(1, modechar);
+               LastChangeList.push(mh, item.adding, item.param);
 
-               if (pcnt)
-               {
-                       TranslateType tt = mh->GetTranslateType();
-                       if (tt == TR_NICK)
-                       {
-                               User* u = ServerInstance->FindNick(parameter);
-                               if (u)
-                                       parameter = u->nick;
-                       }
-                       output_parameters << " " << parameter;
-                       LastParseParams.push_back(parameter);
-                       LastParseTranslate.push_back(tt);
-               }
-
-               if ( (output_mode.length() + output_parameters.str().length() > 450)
-                               || (output_mode.length() > 100)
-                               || (LastParseParams.size() > ServerInstance->Config->Limits.MaxModes))
+               if (LastChangeList.size() >= ServerInstance->Config->Limits.MaxModes)
                {
                        /* mode sequence is getting too long */
                        break;
                }
        }
 
-       LastParseParams[0] = output_mode;
-
-       if (!output_mode.empty())
+       if (!LastChangeList.empty())
        {
-               LastParse = targetchannel ? targetchannel->name : targetuser->nick;
-               LastParse.append(" ");
-               LastParse.append(output_mode);
-               LastParse.append(output_parameters.str());
-
+               ClientProtocol::Events::Mode modeevent(user, targetchannel, targetuser, LastChangeList);
                if (targetchannel)
                {
-                       targetchannel->WriteChannel(user, "MODE %s", LastParse.c_str());
-                       FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, LastParseParams, LastParseTranslate));
+                       targetchannel->Write(modeevent);
                }
                else
                {
-                       targetuser->WriteFrom(user, "MODE %s", LastParse.c_str());
-                       FOREACH_MOD(I_OnMode,OnMode(user, targetuser, TYPE_USER, LastParseParams, LastParseTranslate));
+                       LocalUser* localtarget = IS_LOCAL(targetuser);
+                       if (localtarget)
+                               localtarget->Send(modeevent);
                }
+
+               FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastChangeList, flags));
        }
-       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);
-       }
+
+       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, mletter, "", true, 0));
+               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 %s :You do not have access to view the +%c list",
-                               user->nick.c_str(), chan->name.c_str(), mletter);
-                       display = false;
-               }
-
-               unsigned char handler_id = (mletter - 'A') | MASK_CHANNEL;
 
-               for(ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
+               // Ask mode watchers whether it's OK to show the list
+               std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mh->name);
+               for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
                {
-                       std::string dummyparam;
+                       ModeWatcher* mw = i->second;
+                       if (mw->GetModeType() == MODETYPE_CHANNEL)
+                       {
+                               std::string dummyparam;
 
-                       if (!((*watchers)->BeforeMode(user, NULL, chan, dummyparam, true, MODETYPE_CHANNEL)))
-                               display = false;
+                               if (!mw->BeforeMode(user, NULL, chan, dummyparam, true))
+                               {
+                                       // A mode watcher doesn't want us to show the list
+                                       display = false;
+                                       break;
+                               }
+                       }
                }
+
                if (display)
                        mh->DisplayList(user, chan);
                else
@@ -598,11 +526,6 @@ void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_s
        }
 }
 
-const std::string& ModeParser::GetLastParse()
-{
-       return LastParse;
-}
-
 void ModeParser::CleanMask(std::string &mask)
 {
        std::string::size_type pos_of_pling = mask.find_first_of('!');
@@ -639,50 +562,87 @@ void ModeParser::CleanMask(std::string &mask)
        }
 }
 
-bool ModeParser::AddMode(ModeHandler* mh)
+ModeHandler::Id ModeParser::AllocateModeId(ModeType mt)
 {
-       unsigned char mask = 0;
-       unsigned char pos = 0;
+       for (ModeHandler::Id i = 0; i != MODEID_MAX; ++i)
+       {
+               if (!modehandlersbyid[mt][i])
+                       return i;
+       }
 
-       /* Yes, i know, this might let people declare modes like '_' or '^'.
-        * If they do that, thats their problem, and if i ever EVER see an
-        * official InspIRCd developer do that, i'll beat them with a paddle!
-        */
-       if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z') || (mh->GetPrefix() > 126))
-               return false;
+       throw ModuleException("Out of ModeIds");
+}
+
+void ModeParser::AddMode(ModeHandler* mh)
+{
+       if (!ModeParser::IsModeChar(mh->GetModeChar()))
+               throw ModuleException(InspIRCd::Format("Mode letter for %s is invalid: %c",
+                       mh->name.c_str(), mh->GetModeChar()));
 
        /* A mode prefix of ',' is not acceptable, it would fuck up server to server.
         * A mode prefix of ':' will fuck up both server to server, and client to server.
         * A mode prefix of '#' will mess up /whois and /privmsg
         */
-       if ((mh->GetPrefix() == ',') || (mh->GetPrefix() == ':') || (mh->GetPrefix() == '#'))
-               return false;
+       PrefixMode* pm = mh->IsPrefixMode();
+       if (pm)
+       {
+               if ((pm->GetPrefix() > 126) || (pm->GetPrefix() == ',') || (pm->GetPrefix() == ':') || (pm->GetPrefix() == '#'))
+                       throw ModuleException(InspIRCd::Format("Mode prefix for %s is invalid: %c",
+                               mh->name.c_str(), pm->GetPrefix()));
 
-       if (mh->GetPrefix() && FindPrefix(mh->GetPrefix()))
-               return false;
+               PrefixMode* otherpm = FindPrefix(pm->GetPrefix());
+               if (otherpm)
+                       throw ModuleException(InspIRCd::Format("Mode prefix for %s already used by %s from %s: %c",
+                               mh->name.c_str(), otherpm->name.c_str(), otherpm->creator->ModuleSourceFile.c_str(), pm->GetPrefix()));
+       }
 
-       mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
-       pos = (mh->GetModeChar()-65) | mask;
+       ModeHandler*& slot = modehandlers[mh->GetModeType()][mh->GetModeChar()-65];
+       if (slot)
+               throw ModuleException(InspIRCd::Format("Mode letter for %s already used by %s from %s: %c",
+                       mh->name.c_str(), slot->name.c_str(), slot->creator->ModuleSourceFile.c_str(), mh->GetModeChar()));
 
-       if (modehandlers[pos])
-               return false;
+       // The mode needs an id if it is either a user mode, a simple mode (flag) or a parameter mode.
+       // Otherwise (for listmodes and prefix modes) the id remains MODEID_MAX, which is invalid.
+       ModeHandler::Id modeid = MODEID_MAX;
+       if ((mh->GetModeType() == MODETYPE_USER) || (mh->IsParameterMode()) || (!mh->IsListMode()))
+               modeid = AllocateModeId(mh->GetModeType());
 
-       modehandlers[pos] = mh;
-       return true;
+       std::pair<ModeHandlerMap::iterator, bool> res = modehandlersbyname[mh->GetModeType()].insert(std::make_pair(mh->name, mh));
+       if (!res.second)
+       {
+               ModeHandler* othermh = res.first->second;
+               throw ModuleException(InspIRCd::Format("Mode name %s already used by %c from %s",
+                       mh->name.c_str(), othermh->GetModeChar(), othermh->creator->ModuleSourceFile.c_str()));
+       }
+
+       // Everything is fine, add the mode
+
+       // If we allocated an id for this mode then save it and put the mode handler into the slot
+       if (modeid != MODEID_MAX)
+       {
+               mh->modeid = modeid;
+               modehandlersbyid[mh->GetModeType()][modeid] = mh;
+       }
+
+       slot = mh;
+       if (pm)
+               mhlist.prefix.push_back(pm);
+       else if (mh->IsListModeBase())
+               mhlist.list.push_back(mh->IsListModeBase());
 }
 
 bool ModeParser::DelMode(ModeHandler* mh)
 {
-       unsigned char mask = 0;
-       unsigned char pos = 0;
-
-       if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))
+       if (!ModeParser::IsModeChar(mh->GetModeChar()))
                return false;
 
-       mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
-       pos = (mh->GetModeChar()-65) | mask;
+       ModeHandlerMap& mhmap = modehandlersbyname[mh->GetModeType()];
+       ModeHandlerMap::iterator mhmapit = mhmap.find(mh->name);
+       if ((mhmapit == mhmap.end()) || (mhmapit->second != mh))
+               return false;
 
-       if (modehandlers[pos] != mh)
+       ModeHandler*& slot = modehandlers[mh->GetModeType()][mh->GetModeChar()-65];
+       if (slot != mh)
                return false;
 
        /* Note: We can't stack here, as we have modes potentially being removed across many different channels.
@@ -691,106 +651,83 @@ bool ModeParser::DelMode(ModeHandler* mh)
        switch (mh->GetModeType())
        {
                case MODETYPE_USER:
-                       for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); )
+               {
+                       const user_hash& users = ServerInstance->Users->GetUsers();
+                       for (user_hash::const_iterator i = users.begin(); i != users.end(); )
                        {
                                User* user = i->second;
                                ++i;
                                mh->RemoveMode(user);
                        }
+               }
                break;
                case MODETYPE_CHANNEL:
-                       for (chan_hash::iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); )
+               {
+                       const chan_hash& chans = ServerInstance->GetChans();
+                       for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); )
                        {
                                // The channel may not be in the hash after RemoveMode(), see m_permchannels
                                Channel* chan = i->second;
                                ++i;
-                               mh->RemoveMode(chan);
+
+                               Modes::ChangeList changelist;
+                               mh->RemoveMode(chan, changelist);
+                               this->Process(ServerInstance->FakeClient, chan, NULL, changelist, MODE_LOCALONLY);
                        }
+               }
                break;
        }
 
-       modehandlers[pos] = NULL;
-
+       mhmap.erase(mhmapit);
+       if (mh->GetId() != MODEID_MAX)
+               modehandlersbyid[mh->GetModeType()][mh->GetId()] = NULL;
+       slot = NULL;
+       if (mh->IsPrefixMode())
+               mhlist.prefix.erase(std::find(mhlist.prefix.begin(), mhlist.prefix.end(), mh->IsPrefixMode()));
+       else if (mh->IsListModeBase())
+               mhlist.list.erase(std::find(mhlist.list.begin(), mhlist.list.end(), mh->IsListModeBase()));
        return true;
 }
 
-ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
-{
-       unsigned char mask = 0;
-       unsigned char pos = 0;
-
-       if ((modeletter < 'A') || (modeletter > 'z'))
-               return NULL;
-
-       mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
-       pos = (modeletter-65) | mask;
-
-       return modehandlers[pos];
-}
-
-std::string ModeParser::UserModeList()
+ModeHandler* ModeParser::FindMode(const std::string& modename, ModeType mt)
 {
-       char modestr[256];
-       int pointer = 0;
+       ModeHandlerMap& mhmap = modehandlersbyname[mt];
+       ModeHandlerMap::const_iterator it = mhmap.find(modename);
+       if (it != mhmap.end())
+               return it->second;
 
-       for (unsigned char mode = 'A'; mode <= 'z'; mode++)
-       {
-               unsigned char pos = (mode-65) | MASK_USER;
-
-               if (modehandlers[pos])
-                       modestr[pointer++] = mode;
-       }
-       modestr[pointer++] = 0;
-       return modestr;
+       return NULL;
 }
 
-std::string ModeParser::ChannelModeList()
+ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
 {
-       char modestr[256];
-       int pointer = 0;
-
-       for (unsigned char mode = 'A'; mode <= 'z'; mode++)
-       {
-               unsigned char pos = (mode-65) | MASK_CHANNEL;
+       if (!ModeParser::IsModeChar(modeletter))
+               return NULL;
 
-               if (modehandlers[pos])
-                       modestr[pointer++] = mode;
-       }
-       modestr[pointer++] = 0;
-       return modestr;
+       return modehandlers[mt][modeletter-65];
 }
 
-std::string ModeParser::ParaModeList()
+PrefixMode* ModeParser::FindPrefixMode(unsigned char modeletter)
 {
-       char modestr[256];
-       int pointer = 0;
-
-       for (unsigned char mode = 'A'; mode <= 'z'; mode++)
-       {
-               unsigned char pos = (mode-65) | MASK_CHANNEL;
-
-               if ((modehandlers[pos]) && (modehandlers[pos]->GetNumParams(true)))
-                       modestr[pointer++] = mode;
-       }
-       modestr[pointer++] = 0;
-       return modestr;
+       ModeHandler* mh = FindMode(modeletter, MODETYPE_CHANNEL);
+       if (!mh)
+               return NULL;
+       return mh->IsPrefixMode();
 }
 
-ModeHandler* ModeParser::FindPrefix(unsigned const char pfxletter)
+PrefixMode* ModeParser::FindPrefix(unsigned const char pfxletter)
 {
-       for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+       const PrefixModeList& list = GetPrefixModes();
+       for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
        {
-               unsigned char pos = (mode-65) | MASK_CHANNEL;
-
-               if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix() == pfxletter))
-               {
-                       return modehandlers[pos];
-               }
+               PrefixMode* pm = *i;
+               if (pm->GetPrefix() == pfxletter)
+                       return pm;
        }
        return NULL;
 }
 
-std::string ModeParser::GiveModeList(ModeMasks m)
+std::string ModeParser::GiveModeList(ModeType mt)
 {
        std::string type1;      /* Listmodes EXCEPT those with a prefix */
        std::string type2;      /* Modes that take a param when adding or removing */
@@ -799,37 +736,38 @@ std::string ModeParser::GiveModeList(ModeMasks m)
 
        for (unsigned char mode = 'A'; mode <= 'z'; mode++)
        {
-               unsigned char pos = (mode-65) | m;
+               ModeHandler* mh = modehandlers[mt][mode-65];
                 /* One parameter when adding */
-               if (modehandlers[pos])
+               if (mh)
                {
-                       if (modehandlers[pos]->GetNumParams(true))
+                       if (mh->NeedsParam(true))
                        {
-                               if ((modehandlers[pos]->IsListMode()) && (!modehandlers[pos]->GetPrefix()))
+                               PrefixMode* pm = mh->IsPrefixMode();
+                               if ((mh->IsListMode()) && ((!pm) || (pm->GetPrefix() == 0)))
                                {
-                                       type1 += modehandlers[pos]->GetModeChar();
+                                       type1 += mh->GetModeChar();
                                }
                                else
                                {
                                        /* ... and one parameter when removing */
-                                       if (modehandlers[pos]->GetNumParams(false))
+                                       if (mh->NeedsParam(false))
                                        {
                                                /* But not a list mode */
-                                               if (!modehandlers[pos]->GetPrefix())
+                                               if (!pm)
                                                {
-                                                       type2 += modehandlers[pos]->GetModeChar();
+                                                       type2 += mh->GetModeChar();
                                                }
                                        }
                                        else
                                        {
                                                /* No parameters when removing */
-                                               type3 += modehandlers[pos]->GetModeChar();
+                                               type3 += mh->GetModeChar();
                                        }
                                }
                        }
                        else
                        {
-                               type4 += modehandlers[pos]->GetModeChar();
+                               type4 += mh->GetModeChar();
                        }
                }
        }
@@ -839,7 +777,7 @@ std::string ModeParser::GiveModeList(ModeMasks m)
 
 struct PrefixModeSorter
 {
-       bool operator()(ModeHandler* lhs, ModeHandler* rhs)
+       bool operator()(PrefixMode* lhs, PrefixMode* rhs)
        {
                return lhs->GetPrefixRank() < rhs->GetPrefixRank();
        }
@@ -849,20 +787,18 @@ std::string ModeParser::BuildPrefixes(bool lettersAndModes)
 {
        std::string mletters;
        std::string mprefixes;
-       std::vector<ModeHandler*> prefixes;
+       std::vector<PrefixMode*> prefixes;
 
-       for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+       const PrefixModeList& list = GetPrefixModes();
+       for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
        {
-               unsigned char pos = (mode-65) | MASK_CHANNEL;
-
-               if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix()))
-               {
-                       prefixes.push_back(modehandlers[pos]);
-               }
+               PrefixMode* pm = *i;
+               if (pm->GetPrefix())
+                       prefixes.push_back(pm);
        }
 
        std::sort(prefixes.begin(), prefixes.end(), PrefixModeSorter());
-       for (std::vector<ModeHandler*>::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
+       for (std::vector<PrefixMode*>::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
        {
                mletters += (*n)->GetPrefix();
                mprefixes += (*n)->GetModeChar();
@@ -871,149 +807,69 @@ std::string ModeParser::BuildPrefixes(bool lettersAndModes)
        return lettersAndModes ? "(" + mprefixes + ")" + mletters : mletters;
 }
 
-bool ModeParser::AddModeWatcher(ModeWatcher* mw)
+void ModeParser::AddModeWatcher(ModeWatcher* mw)
 {
-       unsigned char mask = 0;
-       unsigned char pos = 0;
-
-       if (!mw)
-               return false;
-
-       if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))
-               return false;
-
-       mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
-       pos = (mw->GetModeChar()-65) | mask;
-
-       modewatchers[pos].push_back(mw);
-
-       return true;
+       modewatchermap.insert(std::make_pair(mw->GetModeName(), mw));
 }
 
 bool ModeParser::DelModeWatcher(ModeWatcher* mw)
 {
-       unsigned char mask = 0;
-       unsigned char pos = 0;
-
-       if (!mw)
-               return false;
-
-       if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))
-               return false;
-
-       mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
-       pos = (mw->GetModeChar()-65) | mask;
-
-       ModeWatchIter a = std::find(modewatchers[pos].begin(),modewatchers[pos].end(),mw);
-
-       if (a == modewatchers[pos].end())
+       std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mw->GetModeName());
+       for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
        {
-               return false;
+               if (i->second == mw)
+               {
+                       modewatchermap.erase(i);
+                       return true;
+               }
        }
 
-       modewatchers[pos].erase(a);
-
-       return true;
+       return false;
 }
 
-/** This default implementation can remove simple user modes
- */
-void ModeHandler::RemoveMode(User* user, irc::modestacker* stack)
+void ModeHandler::RemoveMode(User* user)
 {
+       // Remove the mode if it's set on the user
        if (user->IsModeSet(this->GetModeChar()))
        {
-               if (stack)
-               {
-                       stack->Push(this->GetModeChar());
-               }
-               else
-               {
-                       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);
-               }
+               Modes::ChangeList changelist;
+               changelist.push_remove(this);
+               ServerInstance->Modes->Process(ServerInstance->FakeClient, NULL, user, changelist, ModeParser::MODE_LOCALONLY);
        }
 }
 
-/** This default implementation can remove simple channel modes
- * (no parameters)
- */
-void ModeHandler::RemoveMode(Channel* channel, irc::modestacker* stack)
+void ModeHandler::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
 {
-       if (channel->IsModeSet(this->GetModeChar()))
+       if (channel->IsModeSet(this))
        {
-               if (stack)
-               {
-                       stack->Push(this->GetModeChar());
-               }
+               if (this->NeedsParam(false))
+                       // Removing this mode requires a parameter
+                       changelist.push_remove(this, channel->GetModeParameter(this));
                else
-               {
-                       std::vector<std::string> parameters;
-                       parameters.push_back(channel->name);
-                       parameters.push_back("-");
-                       parameters[1].push_back(this->GetModeChar());
-                       ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
-               }
+                       changelist.push_remove(this);
        }
 }
 
-struct builtin_modes
+void PrefixMode::RemoveMode(Channel* chan, Modes::ChangeList& changelist)
 {
-       ModeChannelSecret s;
-       ModeChannelPrivate p;
-       ModeChannelModerated m;
-       ModeChannelTopicOps t;
-
-       ModeChannelNoExternal n;
-       ModeChannelInviteOnly i;
-       ModeChannelKey k;
-       ModeChannelLimit l;
-
-       ModeChannelBan b;
-       ModeChannelOp o;
-       ModeChannelVoice v;
-
-       ModeUserWallops uw;
-       ModeUserInvisible ui;
-       ModeUserOperator uo;
-       ModeUserServerNoticeMask us;
-
-       void init(ModeParser* modes)
+       const Channel::MemberMap& userlist = chan->GetUsers();
+       for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
        {
-               modes->AddMode(&s);
-               modes->AddMode(&p);
-               modes->AddMode(&m);
-               modes->AddMode(&t);
-               modes->AddMode(&n);
-               modes->AddMode(&i);
-               modes->AddMode(&k);
-               modes->AddMode(&l);
-               modes->AddMode(&b);
-               modes->AddMode(&o);
-               modes->AddMode(&v);
-               modes->AddMode(&uw);
-               modes->AddMode(&ui);
-               modes->AddMode(&uo);
-               modes->AddMode(&us);
+               if (i->second->HasMode(this))
+                       changelist.push_remove(this, i->first->nick);
        }
-};
+}
 
-static builtin_modes static_modes;
+bool ModeParser::IsModeChar(char chr)
+{
+       return ((chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z'));
+}
 
 ModeParser::ModeParser()
 {
        /* Clear mode handler list */
        memset(modehandlers, 0, sizeof(modehandlers));
-
-       /* Last parse string */
-       LastParse.clear();
-
-       seq = 0;
-       memset(&sent, 0, sizeof(sent));
-
-       static_modes.init(this);
+       memset(modehandlersbyid, 0, sizeof(modehandlersbyid));
 }
 
 ModeParser::~ModeParser()
diff --git a/src/modes/cmode_b.cpp b/src/modes/cmode_b.cpp
deleted file mode 100644 (file)
index 5383f40..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   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/>.
- */
-
-
-#include "inspircd.h"
-#include <string>
-#include <vector>
-#include "inspircd_config.h"
-#include "configreader.h"
-#include "hash_map.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "inspstring.h"
-#include "hashcomp.h"
-#include "modes/cmode_b.h"
-
-ModeChannelBan::ModeChannelBan() : ModeHandler(NULL, "ban", 'b', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
-       list = true;
-}
-
-ModeAction ModeChannelBan::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-{
-       int status = channel->GetPrefixValue(source);
-       /* Call the correct method depending on wether we're adding or removing the mode */
-       if (adding)
-       {
-               this->AddBan(source, parameter, channel, status);
-       }
-       else
-       {
-               this->DelBan(source, parameter, channel, status);
-       }
-       /* If the method above 'ate' the parameter by reducing it to an empty string, then
-        * it won't matter wether we return ALLOW or DENY here, as an empty string overrides
-        * the return value and is always MODEACTION_DENY if the mode is supposed to have
-        * a parameter.
-        */
-       return MODEACTION_ALLOW;
-}
-
-void ModeChannelBan::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
-       BanList copy;
-
-       for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
-       {
-               copy.push_back(*i);
-       }
-
-       for (BanList::iterator i = copy.begin(); i != copy.end(); i++)
-       {
-               if (stack)
-               {
-                       stack->Push(this->GetModeChar(), i->data);
-               }
-               else
-               {
-                       std::vector<std::string> parameters; parameters.push_back(channel->name); parameters.push_back("-b"); parameters.push_back(i->data);
-                       ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
-               }
-       }
-}
-
-void ModeChannelBan::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-void ModeChannelBan::DisplayList(User* user, Channel* channel)
-{
-       /* Display the channel banlist */
-       for (BanList::reverse_iterator i = channel->bans.rbegin(); i != channel->bans.rend(); ++i)
-       {
-               user->WriteServ("367 %s %s %s %s %lu",user->nick.c_str(), channel->name.c_str(), i->data.c_str(), i->set_by.c_str(), (unsigned long)i->set_time);
-       }
-       user->WriteServ("368 %s %s :End of channel ban list",user->nick.c_str(), channel->name.c_str());
-       return;
-}
-
-void ModeChannelBan::DisplayEmptyList(User* user, Channel* channel)
-{
-       user->WriteServ("368 %s %s :End of channel ban list",user->nick.c_str(), channel->name.c_str());
-}
-
-std::string& ModeChannelBan::AddBan(User *user, std::string &dest, Channel *chan, int)
-{
-       if ((!user) || (!chan))
-       {
-               ServerInstance->Logs->Log("MODE",DEFAULT,"*** BUG *** AddBan was given an invalid parameter");
-               dest.clear();
-               return dest;
-       }
-
-       /* Attempt to tidy the mask */
-       ModeParser::CleanMask(dest);
-       /* If the mask was invalid, we exit */
-       if (dest.empty() || dest.length() > 250)
-               return dest;
-
-       long maxbans = chan->GetMaxBans();
-       if (IS_LOCAL(user) && ((unsigned)chan->bans.size() >= (unsigned)maxbans))
-       {
-               user->WriteServ("478 %s %s %c :Channel ban list for %s is full (maximum entries for this channel is %ld)",
-                       user->nick.c_str(), chan->name.c_str(), mode, chan->name.c_str(), maxbans);
-               dest.clear();
-               return dest;
-       }
-
-       ModResult MOD_RESULT;
-       FIRST_MOD_RESULT(OnAddBan, MOD_RESULT, (user,chan,dest));
-       if (MOD_RESULT == MOD_RES_DENY)
-       {
-               dest.clear();
-               return dest;
-       }
-
-       for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
-       {
-               if (i->data == dest)
-               {
-                       /* dont allow a user to set the same ban twice */
-                       dest.clear();
-                       return dest;
-               }
-       }
-
-       b.set_time = ServerInstance->Time();
-       b.data.assign(dest, 0, MAXBUF);
-       b.set_by.assign(user->nick, 0, 64);
-       chan->bans.push_back(b);
-       return dest;
-}
-
-std::string& ModeChannelBan::DelBan(User *user, std::string& dest, Channel *chan, int)
-{
-       if ((!user) || (!chan))
-       {
-               ServerInstance->Logs->Log("MODE",DEFAULT,"*** BUG *** TakeBan was given an invalid parameter");
-               dest.clear();
-               return dest;
-       }
-
-       for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
-       {
-               if (!strcasecmp(i->data.c_str(), dest.c_str()))
-               {
-                       ModResult MOD_RESULT;
-                       FIRST_MOD_RESULT(OnDelBan, MOD_RESULT, (user, chan, dest));
-                       if (MOD_RESULT == MOD_RES_DENY)
-                       {
-                               dest.clear();
-                               return dest;
-                       }
-                       dest = i->data;
-                       chan->bans.erase(i);
-                       return dest;
-               }
-       }
-       dest.clear();
-       return dest;
-}
-
diff --git a/src/modes/cmode_k.cpp b/src/modes/cmode_k.cpp
deleted file mode 100644 (file)
index 400333f..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   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/>.
- */
-
-
-#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/cmode_k.h"
-
-ModeChannelKey::ModeChannelKey() : ModeHandler(NULL, "key", 'k', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
-}
-
-void ModeChannelKey::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
-       /** +k needs a parameter when being removed,
-        * so we have a special-case RemoveMode here for it
-        */
-
-       if (channel->IsModeSet('k'))
-       {
-               if (stack)
-               {
-                       stack->Push('k', channel->GetModeParameter('k'));
-               }
-               else
-               {
-                       std::vector<std::string> parameters;
-                       parameters.push_back(channel->name);
-                       parameters.push_back("-k");
-                       parameters.push_back(channel->GetModeParameter('k'));
-                       ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
-               }
-       }
-}
-
-void ModeChannelKey::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-ModeAction ModeChannelKey::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-{
-       bool exists = channel->IsModeSet('k');
-       if (IS_LOCAL(source))
-       {
-               if (exists == adding)
-                       return MODEACTION_DENY;
-               if (exists && (parameter != channel->GetModeParameter('k')))
-               {
-                       /* Key is currently set and the correct key wasnt given */
-                       return MODEACTION_DENY;
-               }
-       } else {
-               if (exists && adding && parameter == channel->GetModeParameter('k'))
-               {
-                       /* no-op, don't show */
-                       return MODEACTION_DENY;
-               }
-       }
-
-       /* invalid keys */
-       if (!parameter.length())
-               return MODEACTION_DENY;
-
-       if (parameter.rfind(' ') != std::string::npos)
-               return MODEACTION_DENY;
-
-       if (adding)
-       {
-               std::string ckey;
-               ckey.assign(parameter, 0, 32);
-               parameter = ckey;
-               channel->SetModeParam('k', parameter);
-       }
-       else
-       {
-               channel->SetModeParam('k', "");
-       }
-       return MODEACTION_ALLOW;
-}
diff --git a/src/modes/cmode_l.cpp b/src/modes/cmode_l.cpp
deleted file mode 100644 (file)
index 001d058..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   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/>.
- */
-
-
-#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/cmode_l.h"
-
-ModeChannelLimit::ModeChannelLimit() : ParamChannelModeHandler(NULL, "limit", 'l')
-{
-}
-
-bool ModeChannelLimit::ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel*)
-{
-       /* When TS is equal, the higher channel limit wins */
-       return (atoi(their_param.c_str()) < atoi(our_param.c_str()));
-}
-
-bool ModeChannelLimit::ParamValidate(std::string &parameter)
-{
-       int limit = atoi(parameter.c_str());
-
-       if (limit < 0)
-               return false;
-
-       parameter = ConvToStr(limit);
-       return true;
-}
diff --git a/src/modes/cmode_o.cpp b/src/modes/cmode_o.cpp
deleted file mode 100644 (file)
index 0a13b39..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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>
- *
- * 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 "configreader.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "modes/cmode_o.h"
-
-ModeChannelOp::ModeChannelOp() : ModeHandler(NULL, "op", 'o', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
-       list = true;
-       prefix = '@';
-       levelrequired = OP_VALUE;
-       m_paramtype = TR_NICK;
-}
-
-unsigned int ModeChannelOp::GetPrefixRank()
-{
-       return OP_VALUE;
-}
-
-void ModeChannelOp::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
-       const UserMembList* clist = channel->GetUsers();
-
-       for (UserMembCIter i = clist->begin(); i != clist->end(); i++)
-       {
-               if (stack)
-                       stack->Push(this->GetModeChar(), i->first->nick);
-               else
-               {
-                       std::vector<std::string> parameters;
-                       parameters.push_back(channel->name);
-                       parameters.push_back("-o");
-                       parameters.push_back(i->first->nick);
-                       ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
-               }
-       }
-}
-
-void ModeChannelOp::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-ModeAction ModeChannelOp::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-{
-       return MODEACTION_ALLOW;
-}
diff --git a/src/modes/cmode_v.cpp b/src/modes/cmode_v.cpp
deleted file mode 100644 (file)
index 4a00f60..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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>
- *
- * 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 "configreader.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "modes/cmode_v.h"
-
-ModeChannelVoice::ModeChannelVoice() : ModeHandler(NULL, "voice", 'v', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
-       list = true;
-       prefix = '+';
-       levelrequired = HALFOP_VALUE;
-       m_paramtype = TR_NICK;
-}
-
-unsigned int ModeChannelVoice::GetPrefixRank()
-{
-       return VOICE_VALUE;
-}
-
-void ModeChannelVoice::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
-       const UserMembList* clist = channel->GetUsers();
-
-       for (UserMembCIter i = clist->begin(); i != clist->end(); i++)
-       {
-               if (stack)
-                       stack->Push(this->GetModeChar(), i->first->nick);
-               else
-               {
-                       std::vector<std::string> parameters;
-                       parameters.push_back(channel->name);
-                       parameters.push_back("-v");
-                       parameters.push_back(i->first->nick);
-                       ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
-               }
-       }
-}
-
-void ModeChannelVoice::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-ModeAction ModeChannelVoice::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-{
-       return MODEACTION_ALLOW;
-}
diff --git a/src/modes/umode_o.cpp b/src/modes/umode_o.cpp
deleted file mode 100644 (file)
index a5f590b..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   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/>.
- */
-
-
-#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/umode_o.h"
-
-ModeUserOperator::ModeUserOperator() : ModeHandler(NULL, "oper", 'o', PARAM_NONE, MODETYPE_USER)
-{
-       oper = true;
-}
-
-ModeAction ModeUserOperator::OnModeChange(User* source, User* dest, Channel*, std::string&, bool adding)
-{
-       /* Only opers can execute this class at all */
-       if (!ServerInstance->ULine(source->server) && !IS_OPER(source))
-               return MODEACTION_DENY;
-
-       /* Not even opers can GIVE the +o mode, only take it away */
-       if (adding)
-               return MODEACTION_DENY;
-
-       /* Set the bitfields.
-        * Note that oper status is only given in cmd_oper.cpp
-        * NOT here. It is impossible to directly set +o without
-        * verifying as an oper and getting an opertype assigned
-        * to your User!
-        */
-       char snomask = IS_LOCAL(dest) ? 'o' : 'O';
-       ServerInstance->SNO->WriteToSnoMask(snomask, "User %s de-opered (by %s)", dest->nick.c_str(),
-               source->nick.empty() ? source->server.c_str() : source->nick.c_str());
-       dest->UnOper();
-
-       return MODEACTION_ALLOW;
-}
diff --git a/src/modes/umode_s.cpp b/src/modes/umode_s.cpp
deleted file mode 100644 (file)
index 1b782ae..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   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/>.
- */
-
-
-#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/umode_s.h"
-
-ModeUserServerNoticeMask::ModeUserServerNoticeMask() : ModeHandler(NULL, "snomask", 's', PARAM_SETONLY, MODETYPE_USER)
-{
-       oper = true;
-}
-
-ModeAction ModeUserServerNoticeMask::OnModeChange(User* source, User* dest, Channel*, std::string &parameter, bool adding)
-{
-       /* Set the array fields */
-       if (adding)
-       {
-               /* Fix for bug #310 reported by Smartys */
-               if (!dest->modes[UM_SNOMASK])
-                       dest->snomasks.reset();
-
-               dest->modes[UM_SNOMASK] = true;
-               parameter = dest->ProcessNoticeMasks(parameter.c_str());
-               return MODEACTION_ALLOW;
-       }
-       else
-       {
-               if (dest->modes[UM_SNOMASK] != false)
-               {
-                       dest->modes[UM_SNOMASK] = false;
-                       return MODEACTION_ALLOW;
-               }
-       }
-
-       /* Allow the change */
-       return MODEACTION_DENY;
-}
-
-std::string ModeUserServerNoticeMask::GetUserParameter(User* user)
-{
-       std::string masks = user->FormatNoticeMasks();
-       if (masks.length())
-               masks = "+" + masks;
-       return masks;
-}
-
-void ModeUserServerNoticeMask::OnParameterMissing(User* user, User* dest, Channel* channel)
-{
-       user->WriteServ("NOTICE %s :*** The user mode +s requires a parameter (server notice mask). Please provide a parameter, e.g. '+s +*'.",
-                       user->nick.c_str());
-}
-
diff --git a/src/modmanager_dynamic.cpp b/src/modmanager_dynamic.cpp
deleted file mode 100644 (file)
index 050f41c..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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
- * 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 "xline.h"
-#include "socket.h"
-#include "socketengine.h"
-#include "command_parse.h"
-#include "dns.h"
-#include "exitcodes.h"
-#include <iostream>
-
-#ifndef _WIN32
-#include <dirent.h>
-#endif
-
-#ifndef PURE_STATIC
-
-bool ModuleManager::Load(const std::string& filename, bool defer)
-{
-       /* Don't allow people to specify paths for modules, it doesn't work as expected */
-       if (filename.find('/') != std::string::npos)
-       {
-               LastModuleError = "You can't load modules with a path: " + filename;
-               return false;
-       }
-
-       char modfile[MAXBUF];
-       snprintf(modfile,MAXBUF,"%s/%s",ServerInstance->Config->ModPath.c_str(),filename.c_str());
-
-       if (!ServerConfig::FileExists(modfile))
-       {
-               LastModuleError = "Module file could not be found: " + filename;
-               ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
-               return false;
-       }
-
-       if (Modules.find(filename) != Modules.end())
-       {
-               LastModuleError = "Module " + filename + " is already loaded, cannot load a module twice!";
-               ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
-               return false;
-       }
-
-       Module* newmod = NULL;
-       DLLManager* newhandle = new DLLManager(modfile);
-
-       try
-       {
-               newmod = newhandle->CallInit();
-
-               if (newmod)
-               {
-                       newmod->ModuleSourceFile = filename;
-                       newmod->ModuleDLLManager = newhandle;
-                       newmod->dying = false;
-                       Modules[filename] = newmod;
-                       std::string version = newhandle->GetVersion();
-                       if (defer)
-                       {
-                               ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)",
-                                       filename.c_str(), version.c_str());
-                       }
-                       else
-                       {
-                               newmod->init();
-
-                               Version v = newmod->GetVersion();
-                               ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)%s",
-                                       filename.c_str(), version.c_str(), (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
-                       }
-               }
-               else
-               {
-                       LastModuleError = "Unable to load " + filename + ": " + newhandle->LastError();
-                       ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
-                       delete newhandle;
-                       return false;
-               }
-       }
-       catch (CoreException& modexcept)
-       {
-               // failure in module constructor
-               if (newmod)
-               {
-                       DoSafeUnload(newmod);
-                       ServerInstance->GlobalCulls.AddItem(newhandle);
-               }
-               else
-                       delete newhandle;
-               LastModuleError = "Unable to load " + filename + ": " + modexcept.GetReason();
-               ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
-               return false;
-       }
-
-       this->ModCount++;
-       if (defer)
-               return true;
-
-       FOREACH_MOD(I_OnLoadModule,OnLoadModule(newmod));
-       /* We give every module a chance to re-prioritize when we introduce a new one,
-        * not just the one thats loading, as the new module could affect the preference
-        * of others
-        */
-       for(int tries = 0; tries < 20; tries++)
-       {
-               prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
-               for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
-                       n->second->Prioritize();
-
-               if (prioritizationState == PRIO_STATE_LAST)
-                       break;
-               if (tries == 19)
-                       ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + filename);
-       }
-
-       ServerInstance->BuildISupport();
-       return true;
-}
-
-namespace {
-       struct UnloadAction : public HandlerBase0<void>
-       {
-               Module* const mod;
-               UnloadAction(Module* m) : mod(m) {}
-               void Call()
-               {
-                       DLLManager* dll = mod->ModuleDLLManager;
-                       ServerInstance->Modules->DoSafeUnload(mod);
-                       ServerInstance->GlobalCulls.Apply();
-                       delete dll;
-                       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.c_str());
-                       if (callback)
-                               callback->Call(rv);
-                       ServerInstance->GlobalCulls.AddItem(this);
-               }
-       };
-}
-
-bool ModuleManager::Unload(Module* mod)
-{
-       if (!CanUnload(mod))
-               return false;
-       ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
-       return true;
-}
-
-void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
-{
-       if (CanUnload(mod))
-               ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
-       else if (callback)
-               callback->Call(false);
-}
-
-/* We must load the modules AFTER initializing the socket engine, now */
-void ModuleManager::LoadAll()
-{
-       ModCount = 0;
-
-       std::cout << std::endl << "Loading core commands";
-       fflush(stdout);
-
-       DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
-       if (library)
-       {
-               dirent* entry = NULL;
-               while (0 != (entry = readdir(library)))
-               {
-                       if (InspIRCd::Match(entry->d_name, "cmd_*.so", ascii_case_insensitive_map))
-                       {
-                               std::cout << ".";
-                               fflush(stdout);
-
-                               if (!Load(entry->d_name, true))
-                               {
-                                       ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
-                                       std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
-                                       ServerInstance->Exit(EXIT_STATUS_MODULE);
-                               }
-                       }
-               }
-               closedir(library);
-               std::cout << std::endl;
-       }
-
-       ConfigTagList tags = ServerInstance->Config->ConfTags("module");
-       for(ConfigIter i = tags.first; i != tags.second; ++i)
-       {
-               ConfigTag* tag = i->second;
-               std::string name = tag->getString("name");
-               std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
-
-               if (!this->Load(name, true))
-               {
-                       ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
-                       std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
-                       ServerInstance->Exit(EXIT_STATUS_MODULE);
-               }
-       }
-
-       for(std::map<std::string, Module*>::iterator i = Modules.begin(); i != Modules.end(); i++)
-       {
-               Module* mod = i->second;
-               try
-               {
-                       ServerInstance->Logs->Log("MODULE", DEBUG, "Initializing %s", i->first.c_str());
-                       mod->init();
-               }
-               catch (CoreException& modexcept)
-               {
-                       LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
-                       ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
-                       std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
-                       ServerInstance->Exit(EXIT_STATUS_MODULE);
-               }
-       }
-
-       /* We give every module a chance to re-prioritize when we introduce a new one,
-        * not just the one thats loading, as the new module could affect the preference
-        * of others
-        */
-       for(int tries = 0; tries < 20; tries++)
-       {
-               prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
-               for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
-                       n->second->Prioritize();
-
-               if (prioritizationState == PRIO_STATE_LAST)
-                       break;
-               if (tries == 19)
-               {
-                       ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected");
-                       ServerInstance->Exit(EXIT_STATUS_MODULE);
-               }
-       }
-}
-
-#endif
diff --git a/src/modmanager_static.cpp b/src/modmanager_static.cpp
deleted file mode 100644 (file)
index cea40c7..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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
- * 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/>.
- */
-
-
-#define MODNAME cmd_all
-
-#include "inspircd.h"
-#include "exitcodes.h"
-#include <iostream>
-
-#ifdef PURE_STATIC
-
-typedef std::map<std::string, AllModuleList*> modmap;
-static std::vector<AllCommandList::fn>* cmdlist = NULL;
-static modmap* modlist = NULL;
-
-AllCommandList::AllCommandList(fn cmd)
-{
-       if (!cmdlist)
-               cmdlist = new std::vector<AllCommandList::fn>();
-       cmdlist->push_back(cmd);
-}
-
-AllModuleList::AllModuleList(AllModuleList::fn mod, const std::string& Name) : init(mod), name(Name)
-{
-       if (!modlist)
-               modlist = new modmap();
-       modlist->insert(std::make_pair(Name, this));
-}
-
-class AllModule : public Module
-{
-       std::vector<Command*> cmds;
- public:
-       AllModule()
-       {
-               if (!cmdlist)
-                       return;
-               try
-               {
-                       cmds.reserve(cmdlist->size());
-                       for(std::vector<AllCommandList::fn>::iterator i = cmdlist->begin(); i != cmdlist->end(); ++i)
-                       {
-                               Command* c = (*i)(this);
-                               cmds.push_back(c);
-                               ServerInstance->AddCommand(c);
-                       }
-               }
-               catch (...)
-               {
-                       this->AllModule::~AllModule();
-                       throw;
-               }
-       }
-
-       ~AllModule()
-       {
-               for(std::vector<Command*>::iterator i = cmds.begin(); i != cmds.end(); ++i)
-                       delete *i;
-       }
-
-       Version GetVersion()
-       {
-               return Version("All commands", VF_VENDOR|VF_CORE);
-       }
-};
-
-MODULE_INIT(AllModule)
-
-bool ModuleManager::Load(const std::string& name, bool defer)
-{
-       modmap::iterator it = modlist->find(name);
-       if (it == modlist->end())
-               return false;
-       Module* mod = NULL;
-       try
-       {
-               mod = (*it->second->init)();
-               mod->ModuleSourceFile = name;
-               mod->ModuleDLLManager = NULL;
-               mod->dying = false;
-               Modules[name] = mod;
-               if (defer)
-               {
-                       ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s", name.c_str());
-                       return true;
-               }
-               else
-               {
-                       mod->init();
-               }
-       }
-       catch (CoreException& modexcept)
-       {
-               if (mod)
-                       DoSafeUnload(mod);
-               ServerInstance->Logs->Log("MODULE", DEFAULT, "Unable to load " + name + ": " + modexcept.GetReason());
-               return false;
-       }
-       FOREACH_MOD(I_OnLoadModule,OnLoadModule(mod));
-       /* We give every module a chance to re-prioritize when we introduce a new one,
-        * not just the one thats loading, as the new module could affect the preference
-        * of others
-        */
-       for(int tries = 0; tries < 20; tries++)
-       {
-               prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
-               for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
-                       n->second->Prioritize();
-
-               if (prioritizationState == PRIO_STATE_LAST)
-                       break;
-               if (tries == 19)
-                       ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + name);
-       }
-
-       ServerInstance->BuildISupport();
-       return true;
-}
-
-namespace {
-       struct UnloadAction : public HandlerBase0<void>
-       {
-               Module* const mod;
-               UnloadAction(Module* m) : mod(m) {}
-               void Call()
-               {
-                       ServerInstance->Modules->DoSafeUnload(mod);
-                       ServerInstance->GlobalCulls.Apply();
-                       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()
-               {
-                       std::string name = mod->ModuleSourceFile;
-                       ServerInstance->Modules->DoSafeUnload(mod);
-                       ServerInstance->GlobalCulls.Apply();
-                       bool rv = ServerInstance->Modules->Load(name.c_str());
-                       if (callback)
-                               callback->Call(rv);
-                       ServerInstance->GlobalCulls.AddItem(this);
-               }
-       };
-}
-
-bool ModuleManager::Unload(Module* mod)
-{
-       if (!CanUnload(mod))
-               return false;
-       ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
-       return true;
-}
-
-void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
-{
-       if (CanUnload(mod))
-               ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
-       else if (callback)
-               callback->Call(false);
-}
-
-void ModuleManager::LoadAll()
-{
-       Load("cmd_all", true);
-       Load("cmd_whowas.so", true);
-       Load("cmd_lusers.so", true);
-
-       ConfigTagList tags = ServerInstance->Config->ConfTags("module");
-       for(ConfigIter i = tags.first; i != tags.second; ++i)
-       {
-               ConfigTag* tag = i->second;
-               std::string name = tag->getString("name");
-               std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
-
-               if (!this->Load(name, true))
-               {
-                       ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
-                       std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
-                       ServerInstance->Exit(EXIT_STATUS_MODULE);
-               }
-       }
-
-       for(std::map<std::string, Module*>::iterator i = Modules.begin(); i != Modules.end(); i++)
-       {
-               Module* mod = i->second;
-               try 
-               {
-                       mod->init();
-               }
-               catch (CoreException& modexcept)
-               {
-                       LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
-                       ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
-                       std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
-                       ServerInstance->Exit(EXIT_STATUS_MODULE);
-               }
-       }
-
-       /* We give every module a chance to re-prioritize when we introduce a new one,
-        * not just the one thats loading, as the new module could affect the preference
-        * of others
-        */
-       for(int tries = 0; tries < 20; tries++)
-       {
-               prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
-               for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
-                       n->second->Prioritize();
-
-               if (prioritizationState == PRIO_STATE_LAST)
-                       break;
-               if (tries == 19)
-               {
-                       ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected");
-                       ServerInstance->Exit(EXIT_STATUS_MODULE);
-               }
-       }
-}
-
-#endif
diff --git a/src/modulemanager.cpp b/src/modulemanager.cpp
new file mode 100644 (file)
index 0000000..5ed7d12
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   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
+ * 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 "exitcodes.h"
+#include <iostream>
+
+#ifndef _WIN32
+#include <dirent.h>
+#endif
+
+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 (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))
+       {
+               LastModuleError = "Module file could not be found: " + filename;
+               ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+               return false;
+       }
+
+       if (Modules.find(filename) != Modules.end())
+       {
+               LastModuleError = "Module " + filename + " is already loaded, cannot load a module twice!";
+               ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+               return false;
+       }
+
+       Module* newmod = NULL;
+       DLLManager* newhandle = new DLLManager(moduleFile.c_str());
+       ServiceList newservices;
+       if (!defer)
+               this->NewServices = &newservices;
+
+       try
+       {
+               newmod = newhandle->CallInit();
+               this->NewServices = NULL;
+
+               if (newmod)
+               {
+                       newmod->ModuleSourceFile = filename;
+                       newmod->ModuleDLLManager = newhandle;
+                       Modules[filename] = newmod;
+                       std::string version = newhandle->GetVersion();
+                       if (version.empty())
+                               version.assign("unknown");
+                       if (defer)
+                       {
+                               ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s (Module version %s)",
+                                       filename.c_str(), version.c_str());
+                       }
+                       else
+                       {
+                               ConfigStatus confstatus;
+
+                               AttachAll(newmod);
+                               AddServices(newservices);
+                               newmod->init();
+                               newmod->ReadConfig(confstatus);
+
+                               Version v = newmod->GetVersion();
+                               ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s (Module version %s)%s",
+                                       filename.c_str(), version.c_str(), (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
+                       }
+               }
+               else
+               {
+                       LastModuleError = "Unable to load " + filename + ": " + newhandle->LastError();
+                       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+                       delete newhandle;
+                       return false;
+               }
+       }
+       catch (CoreException& modexcept)
+       {
+               this->NewServices = NULL;
+
+               // failure in module constructor
+               if (newmod)
+               {
+                       DoSafeUnload(newmod);
+                       ServerInstance->GlobalCulls.AddItem(newhandle);
+               }
+               else
+                       delete newhandle;
+               LastModuleError = "Unable to load " + filename + ": " + modexcept.GetReason();
+               ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+               return false;
+       }
+
+       if (defer)
+               return true;
+
+       FOREACH_MOD(OnLoadModule, (newmod));
+       PrioritizeHooks();
+       ServerInstance->ISupport.Build();
+       return true;
+}
+
+/* We must load the modules AFTER initializing the socket engine, now */
+void ModuleManager::LoadCoreModules(std::map<std::string, ServiceList>& servicemap)
+{
+       std::cout << std::endl << "Loading core commands" << std::flush;
+
+       DIR* library = opendir(ServerInstance->Config->Paths.Module.c_str());
+       if (library)
+       {
+               dirent* entry = NULL;
+               while (0 != (entry = readdir(library)))
+               {
+                       if (InspIRCd::Match(entry->d_name, "core_*.so", ascii_case_insensitive_map))
+                       {
+                               std::cout << "." << std::flush;
+
+                               this->NewServices = &servicemap[entry->d_name];
+
+                               if (!Load(entry->d_name, true))
+                               {
+                                       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, this->LastError());
+                                       std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
+                                       ServerInstance->Exit(EXIT_STATUS_MODULE);
+                               }
+                       }
+               }
+               closedir(library);
+               std::cout << std::endl;
+       }
+}
index 7df7579bf84172afbce39afc41206bc0d9000e36..952c115d2be81ea3a4efec08ef47e8442dc00859 100644 (file)
  */
 
 
+#include <iostream>
 #include "inspircd.h"
-#include "xline.h"
-#include "socket.h"
-#include "socketengine.h"
-#include "command_parse.h"
-#include "dns.h"
 #include "exitcodes.h"
 
 #ifndef _WIN32
        #include <dirent.h>
 #endif
 
-static std::vector<dynamic_reference_base*>* dynrefs = NULL;
+static insp::intrusive_list<dynamic_reference_base>* dynrefs = NULL;
 
 void dynamic_reference_base::reset_all()
 {
        if (!dynrefs)
                return;
-       for(unsigned int i = 0; i < dynrefs->size(); i++)
-               (*dynrefs)[i]->ClearCache();
+       for (insp::intrusive_list<dynamic_reference_base>::iterator i = dynrefs->begin(); i != dynrefs->end(); ++i)
+               (*i)->resolve();
 }
 
 // Version is a simple class for holding a modules version number
@@ -56,130 +52,112 @@ Version::Version(const std::string &desc, int flags, const std::string& linkdata
 {
 }
 
-Request::Request(Module* src, Module* dst, const char* idstr)
-: id(idstr), source(src), dest(dst)
-{
-}
-
-void Request::Send()
-{
-       if (dest)
-               dest->OnRequest(*this);
-}
-
-Event::Event(Module* src, const std::string &eventid) : source(src), id(eventid) { }
+// These declarations define the behavours of the base class Module (which does nothing at all)
 
-void Event::Send()
+Module::Module()
+       : ModuleDLLManager(NULL)
+       , dying(false)
 {
-       FOREACH_MOD(I_OnEvent,OnEvent(*this));
 }
 
-// These declarations define the behavours of the base class Module (which does nothing at all)
-
-Module::Module() { }
 CullResult Module::cull()
 {
        return classbase::cull();
 }
+
 Module::~Module()
 {
 }
 
-ModResult      Module::OnSendSnotice(char &snomask, std::string &type, const std::string &message) { return MOD_RES_PASSTHRU; }
-void           Module::OnUserConnect(LocalUser*) { }
-void           Module::OnUserQuit(User*, const std::string&, const std::string&) { }
-void           Module::OnUserDisconnect(LocalUser*) { }
-void           Module::OnUserJoin(Membership*, bool, bool, CUList&) { }
-void           Module::OnPostJoin(Membership*) { }
-void           Module::OnUserPart(Membership*, std::string&, CUList&) { }
-void           Module::OnPreRehash(User*, const std::string&) { }
-void           Module::OnModuleRehash(User*, const std::string&) { }
-void           Module::OnRehash(User*) { }
-ModResult      Module::OnUserPreJoin(User*, Channel*, const char*, std::string&, const std::string&) { return MOD_RES_PASSTHRU; }
-void           Module::OnMode(User*, void*, int, const std::vector<std::string>&, const std::vector<TranslateType>&) { }
-void           Module::OnOper(User*, const std::string&) { }
-void           Module::OnPostOper(User*, const std::string&, const std::string &) { }
-void           Module::OnPostDeoper(User*) { }
-void           Module::OnInfo(User*) { }
-void           Module::OnWhois(User*, User*) { }
-ModResult      Module::OnUserPreInvite(User*, User*, Channel*, time_t) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnUserPreMessage(User*, void*, int, std::string&, char, CUList&) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnUserPreNotice(User*, void*, int, std::string&, char, CUList&) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnUserPreNick(User*, const std::string&) { return MOD_RES_PASSTHRU; }
-void           Module::OnUserPostNick(User*, const std::string&) { }
-ModResult      Module::OnPreMode(User*, User*, Channel*, const std::vector<std::string>&) { return MOD_RES_PASSTHRU; }
-void           Module::On005Numeric(std::string&) { }
-ModResult      Module::OnKill(User*, User*, const std::string&) { return MOD_RES_PASSTHRU; }
-void           Module::OnLoadModule(Module*) { }
-void           Module::OnUnloadModule(Module*) { }
-void           Module::OnBackgroundTimer(time_t) { }
-ModResult      Module::OnPreCommand(std::string&, std::vector<std::string>&, LocalUser*, bool, const std::string&) { return MOD_RES_PASSTHRU; }
-void           Module::OnPostCommand(const std::string&, const std::vector<std::string>&, LocalUser*, CmdResult, const std::string&) { }
-void           Module::OnUserInit(LocalUser*) { }
-ModResult      Module::OnCheckReady(LocalUser*) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnUserRegister(LocalUser*) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnUserPreKick(User*, Membership*, const std::string&) { return MOD_RES_PASSTHRU; }
-void           Module::OnUserKick(User*, Membership*, const std::string&, CUList&) { }
-ModResult      Module::OnRawMode(User*, Channel*, const char, const std::string &, bool, int) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnCheckInvite(User*, Channel*) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnCheckKey(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnCheckLimit(User*, Channel*) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnCheckChannelBan(User*, Channel*) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnCheckBan(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnExtBanCheck(User*, Channel*, char) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnStats(char, User*, string_list&) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnChangeLocalUserHost(LocalUser*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnChangeLocalUserGECOS(LocalUser*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnPreTopicChange(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
-void           Module::OnEvent(Event&) { }
-void           Module::OnRequest(Request&) { }
-ModResult      Module::OnPassCompare(Extensible* ex, const std::string &password, const std::string &input, const std::string& hashtype) { return MOD_RES_PASSTHRU; }
-void           Module::OnGlobalOper(User*) { }
-void           Module::OnPostConnect(User*) { }
-ModResult      Module::OnAddBan(User*, Channel*, const std::string &) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnDelBan(User*, Channel*, const std::string &) { return MOD_RES_PASSTHRU; }
-void           Module::OnStreamSocketAccept(StreamSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { }
-int            Module::OnStreamSocketWrite(StreamSocket*, std::string&) { return -1; }
-void           Module::OnStreamSocketClose(StreamSocket*) { }
-void           Module::OnStreamSocketConnect(StreamSocket*) { }
-int            Module::OnStreamSocketRead(StreamSocket*, std::string&) { return -1; }
-void           Module::OnUserMessage(User*, void*, int, const std::string&, char, const CUList&) { }
-void           Module::OnUserNotice(User*, void*, int, const std::string&, char, const CUList&) { }
-void           Module::OnRemoteKill(User*, User*, const std::string&, const std::string&) { }
-void           Module::OnUserInvite(User*, User*, Channel*, time_t) { }
-void           Module::OnPostTopicChange(User*, Channel*, const std::string&) { }
-void           Module::OnGetServerDescription(const std::string&, std::string&) { }
-void           Module::OnSyncUser(User*, Module*, void*) { }
-void           Module::OnSyncChannel(Channel*, Module*, void*) { }
-void           Module::OnSyncNetwork(Module*, void*) { }
-void           Module::ProtoSendMode(void*, TargetTypeFlags, void*, const std::vector<std::string>&, const std::vector<TranslateType>&) { }
-void           Module::OnDecodeMetaData(Extensible*, const std::string&, const std::string&) { }
-void           Module::ProtoSendMetaData(void*, Extensible*, const std::string&, const std::string&) { }
-void           Module::OnWallops(User*, const std::string&) { }
-void           Module::OnChangeHost(User*, const std::string&) { }
-void           Module::OnChangeName(User*, const std::string&) { }
-void           Module::OnChangeIdent(User*, const std::string&) { }
-void           Module::OnAddLine(User*, XLine*) { }
-void           Module::OnDelLine(User*, XLine*) { }
-void           Module::OnExpireLine(XLine*) { }
-void           Module::OnCleanup(int, void*) { }
-ModResult      Module::OnChannelPreDelete(Channel*) { return MOD_RES_PASSTHRU; }
-void           Module::OnChannelDelete(Channel*) { }
-ModResult      Module::OnSetAway(User*, const std::string &) { return MOD_RES_PASSTHRU; }
-ModResult      Module::OnWhoisLine(User*, User*, int&, std::string&) { return MOD_RES_PASSTHRU; }
-void           Module::OnBuildNeighborList(User*, UserChanList&, std::map<User*,bool>&) { }
-void           Module::OnGarbageCollect() { }
-ModResult      Module::OnSetConnectClass(LocalUser* user, ConnectClass* myclass) { return MOD_RES_PASSTHRU; }
-void           Module::OnText(User*, void*, int, const std::string&, char, CUList&) { }
-void           Module::OnRunTestSuite() { }
-void           Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { }
-ModResult      Module::OnNumeric(User*, unsigned int, const std::string&) { return MOD_RES_PASSTHRU; }
-void           Module::OnHookIO(StreamSocket*, ListenSocket*) { }
-ModResult   Module::OnAcceptConnection(int, ListenSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { return MOD_RES_PASSTHRU; }
-void           Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, std::string&) { }
-void           Module::OnSetUserIP(LocalUser*) { }
-
-ModuleManager::ModuleManager() : ModCount(0)
+void Module::DetachEvent(Implementation i)
+{
+       ServerInstance->Modules->Detach(i, this);
+}
+
+void           Module::ReadConfig(ConfigStatus& status) { }
+ModResult      Module::OnSendSnotice(char &snomask, std::string &type, const std::string &message) { DetachEvent(I_OnSendSnotice); return MOD_RES_PASSTHRU; }
+void           Module::OnUserConnect(LocalUser*) { DetachEvent(I_OnUserConnect); }
+void           Module::OnUserQuit(User*, const std::string&, const std::string&) { DetachEvent(I_OnUserQuit); }
+void           Module::OnUserDisconnect(LocalUser*) { DetachEvent(I_OnUserDisconnect); }
+void           Module::OnUserJoin(Membership*, bool, bool, CUList&) { DetachEvent(I_OnUserJoin); }
+void           Module::OnPostJoin(Membership*) { DetachEvent(I_OnPostJoin); }
+void           Module::OnUserPart(Membership*, std::string&, CUList&) { DetachEvent(I_OnUserPart); }
+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 Modes::ChangeList&, ModeParser::ModeProcessFlag) { 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::OnPostDeoper(User*) { DetachEvent(I_OnPostDeoper); }
+ModResult      Module::OnUserPreInvite(User*, User*, Channel*, time_t) { DetachEvent(I_OnUserPreInvite); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnUserPreMessage(User*, const MessageTarget&, MessageDetails&) { DetachEvent(I_OnUserPreMessage); 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*, 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); }
+void           Module::OnUnloadModule(Module*) { DetachEvent(I_OnUnloadModule); }
+void           Module::OnBackgroundTimer(time_t) { DetachEvent(I_OnBackgroundTimer); }
+ModResult      Module::OnPreCommand(std::string&, CommandBase::Params&, LocalUser*, bool) { DetachEvent(I_OnPreCommand); return MOD_RES_PASSTHRU; }
+void           Module::OnPostCommand(Command*, const CommandBase::Params&, LocalUser*, CmdResult, bool) { DetachEvent(I_OnPostCommand); }
+void           Module::OnUserInit(LocalUser*) { DetachEvent(I_OnUserInit); }
+void           Module::OnUserPostInit(LocalUser*) { DetachEvent(I_OnUserPostInit); }
+ModResult      Module::OnCheckReady(LocalUser*) { DetachEvent(I_OnCheckReady); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnUserRegister(LocalUser*) { DetachEvent(I_OnUserRegister); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnUserPreKick(User*, Membership*, const std::string&) { DetachEvent(I_OnUserPreKick); return MOD_RES_PASSTHRU; }
+void           Module::OnUserKick(User*, Membership*, const std::string&, CUList&) { DetachEvent(I_OnUserKick); }
+ModResult      Module::OnRawMode(User*, Channel*, ModeHandler*, const std::string&, bool) { DetachEvent(I_OnRawMode); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnCheckInvite(User*, Channel*) { DetachEvent(I_OnCheckInvite); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnCheckKey(User*, Channel*, const std::string&) { DetachEvent(I_OnCheckKey); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnCheckLimit(User*, Channel*) { DetachEvent(I_OnCheckLimit); return MOD_RES_PASSTHRU; }
+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::OnPreChangeHost(LocalUser*, const std::string&) { DetachEvent(I_OnPreChangeHost); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnPreChangeRealName(LocalUser*, const std::string&) { DetachEvent(I_OnPreChangeRealName); return MOD_RES_PASSTHRU; }
+ModResult      Module::OnPreTopicChange(User*, Channel*, const std::string&) { DetachEvent(I_OnPreTopicChange); return MOD_RES_PASSTHRU; }
+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::OnPostConnect(User*) { DetachEvent(I_OnPostConnect); }
+void           Module::OnUserPostMessage(User*, const MessageTarget&, const MessageDetails&) { DetachEvent(I_OnUserPostMessage); }
+void           Module::OnUserMessageBlocked(User*, const MessageTarget&, const MessageDetails&) { DetachEvent(I_OnUserMessageBlocked); }
+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::OnDecodeMetaData(Extensible*, const std::string&, const std::string&) { DetachEvent(I_OnDecodeMetaData); }
+void           Module::OnChangeHost(User*, const std::string&) { DetachEvent(I_OnChangeHost); }
+void           Module::OnChangeRealName(User*, const std::string&) { DetachEvent(I_OnChangeRealName); }
+void           Module::OnChangeIdent(User*, const std::string&) { DetachEvent(I_OnChangeIdent); }
+void           Module::OnAddLine(User*, XLine*) { DetachEvent(I_OnAddLine); }
+void           Module::OnDelLine(User*, XLine*) { DetachEvent(I_OnDelLine); }
+void           Module::OnExpireLine(XLine*) { DetachEvent(I_OnExpireLine); }
+void           Module::OnCleanup(ExtensionItem::ExtensibleType, Extensible*) { }
+ModResult      Module::OnChannelPreDelete(Channel*) { DetachEvent(I_OnChannelPreDelete); return MOD_RES_PASSTHRU; }
+void           Module::OnChannelDelete(Channel*) { DetachEvent(I_OnChannelDelete); }
+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::OnUserMessage(User*, const MessageTarget&, const MessageDetails&) { DetachEvent(I_OnUserMessage); }
+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::OnSetUserIP(LocalUser*) { DetachEvent(I_OnSetUserIP); }
+void           Module::OnServiceAdd(ServiceProvider&) { DetachEvent(I_OnServiceAdd); }
+void           Module::OnServiceDel(ServiceProvider&) { DetachEvent(I_OnServiceDel); }
+ModResult      Module::OnUserWrite(LocalUser*, ClientProtocol::Message&) { DetachEvent(I_OnUserWrite); return MOD_RES_PASSTHRU; }
+
+ServiceProvider::ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type)
+       : creator(Creator), name(Name), service(Type)
+{
+       if ((ServerInstance) && (ServerInstance->Modules->NewServices))
+               ServerInstance->Modules->NewServices->push_back(this);
+}
+
+void ServiceProvider::DisableAutoRegister()
+{
+       if ((ServerInstance) && (ServerInstance->Modules->NewServices))
+               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)
@@ -213,18 +185,22 @@ void ModuleManager::Attach(Implementation* i, Module* mod, size_t sz)
                Attach(i[n], mod);
 }
 
+void ModuleManager::AttachAll(Module* mod)
+{
+       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)
@@ -255,22 +231,25 @@ bool ModuleManager::SetPriority(Module* mod, Implementation i, Priority s, Modul
        return false;
 
 found_src:
+       // The modules registered for a hook are called in reverse order (to allow for easier removal
+       // of list entries while looping), meaning that the Priority given to us has the exact opposite effect
+       // on the list, e.g.: PRIORITY_BEFORE will actually put 'mod' after 'which', etc.
        size_t swap_pos = my_pos;
        switch (s)
        {
-               case PRIORITY_FIRST:
+               case PRIORITY_LAST:
                        if (prioritizationState != PRIO_STATE_FIRST)
                                return true;
                        else
                                swap_pos = 0;
                        break;
-               case PRIORITY_LAST:
+               case PRIORITY_FIRST:
                        if (prioritizationState != PRIO_STATE_FIRST)
                                return true;
                        else
                                swap_pos = EventHandlers[i].size() - 1;
                        break;
-               case PRIORITY_AFTER:
+               case PRIORITY_BEFORE:
                {
                        /* Find the latest possible position, only searching AFTER our position */
                        for (size_t x = EventHandlers[i].size() - 1; x > my_pos; --x)
@@ -285,7 +264,7 @@ found_src:
                        return true;
                }
                /* Place this module before a set of other modules */
-               case PRIORITY_BEFORE:
+               case PRIORITY_AFTER:
                {
                        for (size_t x = 0; x < my_pos; ++x)
                        {
@@ -325,6 +304,29 @@ swap_now:
        return true;
 }
 
+bool ModuleManager::PrioritizeHooks()
+{
+       /* We give every module a chance to re-prioritize when we introduce a new one,
+        * not just the one thats loading, as the new module could affect the preference
+        * of others
+        */
+       for (int tries = 0; tries < 20; tries++)
+       {
+               prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
+               for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
+                       n->second->Prioritize();
+
+               if (prioritizationState == PRIO_STATE_LAST)
+                       break;
+               if (tries == 19)
+               {
+                       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Hook priority dependency loop detected");
+                       return false;
+               }
+       }
+       return true;
+}
+
 bool ModuleManager::CanUnload(Module* mod)
 {
        std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
@@ -332,13 +334,7 @@ bool ModuleManager::CanUnload(Module* mod)
        if ((modfind == Modules.end()) || (modfind->second != mod) || (mod->dying))
        {
                LastModuleError = "Module " + mod->ModuleSourceFile + " is not loaded, cannot unload it!";
-               ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
-               return false;
-       }
-       if (mod->GetVersion().Flags & VF_STATIC)
-       {
-               LastModuleError = "Module " + mod->ModuleSourceFile + " not unloadable (marked static)";
-               ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+               ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
                return false;
        }
 
@@ -346,41 +342,59 @@ bool ModuleManager::CanUnload(Module* mod)
        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
+       // they pass execution to the soon to be unloaded module, it will happen now,
+       // i.e. before we unregister the services of the module being unloaded
+       FOREACH_MOD(OnUnloadModule, (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 */
-       for (chan_hash::iterator c = ServerInstance->chanlist->begin(); c != ServerInstance->chanlist->end(); )
+       const chan_hash& chans = ServerInstance->GetChans();
+       for (chan_hash::const_iterator c = chans.begin(); c != chans.end(); )
        {
                Channel* chan = c->second;
                ++c;
-               mod->OnCleanup(TYPE_CHANNEL, chan);
+               mod->OnCleanup(ExtensionItem::EXT_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)
+               {
+                       mod->OnCleanup(ExtensionItem::EXT_MEMBERSHIP, mi->second);
                        mi->second->doUnhookExtensions(items);
+               }
        }
-       for (user_hash::iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); )
+
+       const user_hash& users = ServerInstance->Users->GetUsers();
+       for (user_hash::const_iterator u = users.begin(); u != users.end(); )
        {
                User* user = u->second;
                // The module may quit the user (e.g. SSL mod unloading) and that will remove it from the container
                ++u;
-               mod->OnCleanup(TYPE_USER, user);
+               mod->OnCleanup(ExtensionItem::EXT_USER, user);
                user->doUnhookExtensions(items);
        }
-       for(char m='A'; m <= 'z'; m++)
-       {
-               ModeHandler* mh;
-               mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
-               if (mh && mh->creator == mod)
-                       ServerInstance->Modes->DelMode(mh);
-               mh = ServerInstance->Modes->FindMode(m, MODETYPE_CHANNEL);
-               if (mh && mh->creator == mod)
-                       ServerInstance->Modes->DelMode(mh);
-       }
+
        for(std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
        {
                std::multimap<std::string, ServiceProvider*>::iterator curr = i++;
@@ -390,19 +404,13 @@ void ModuleManager::DoSafeUnload(Module* mod)
 
        dynamic_reference_base::reset_all();
 
-       /* Tidy up any dangling resolvers */
-       ServerInstance->Res->CleanResolvers(mod);
-
-       FOREACH_MOD(I_OnUnloadModule,OnUnloadModule(mod));
-
        DetachAll(mod);
 
        Modules.erase(modfind);
        ServerInstance->GlobalCulls.AddItem(mod);
 
-       ServerInstance->Logs->Log("MODULE", DEFAULT,"Module %s unloaded",mod->ModuleSourceFile.c_str());
-       this->ModCount--;
-       ServerInstance->BuildISupport();
+       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Module %s unloaded",mod->ModuleSourceFile.c_str());
+       ServerInstance->ISupport.Build();
 }
 
 void ModuleManager::UnloadAll()
@@ -428,40 +436,129 @@ void ModuleManager::UnloadAll()
        }
 }
 
-std::string& ModuleManager::LastError()
+namespace
 {
-       return LastModuleError;
+       struct UnloadAction : public ActionBase
+       {
+               Module* const mod;
+               UnloadAction(Module* m) : mod(m) {}
+               void Call() CXX11_OVERRIDE
+               {
+                       DLLManager* dll = mod->ModuleDLLManager;
+                       ServerInstance->Modules->DoSafeUnload(mod);
+                       ServerInstance->GlobalCulls.Apply();
+                       // In pure static mode this is always NULL
+                       delete dll;
+                       ServerInstance->GlobalCulls.AddItem(this);
+               }
+       };
+}
+
+bool ModuleManager::Unload(Module* mod)
+{
+       if (!CanUnload(mod))
+               return false;
+       ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
+       return true;
+}
+
+void ModuleManager::LoadAll()
+{
+       std::map<std::string, ServiceList> servicemap;
+       LoadCoreModules(servicemap);
+
+       // Step 1: load all of the modules.
+       ConfigTagList tags = ServerInstance->Config->ConfTags("module");
+       for (ConfigIter i = tags.first; i != tags.second; ++i)
+       {
+               ConfigTag* tag = i->second;
+               std::string name = ExpandModName(tag->getString("name"));
+               this->NewServices = &servicemap[name];
+
+               // Skip modules which are already loaded.
+               if (Modules.find(name) != Modules.end())
+                       continue;
+
+               std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
+               if (!this->Load(name, true))
+               {
+                       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, this->LastError());
+                       std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
+                       ServerInstance->Exit(EXIT_STATUS_MODULE);
+               }
+       }
+
+       // Step 2: initialize the modules and register their services.
+       for (ModuleMap::const_iterator i = Modules.begin(); i != Modules.end(); ++i)
+       {
+               Module* mod = i->second;
+               try
+               {
+                       ServerInstance->Logs->Log("MODULE", LOG_DEBUG, "Initializing %s", i->first.c_str());
+                       AttachAll(mod);
+                       AddServices(servicemap[i->first]);
+                       mod->init();
+               }
+               catch (CoreException& modexcept)
+               {
+                       LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
+                       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+                       std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
+                       ServerInstance->Exit(EXIT_STATUS_MODULE);
+               }
+       }
+
+       this->NewServices = NULL;
+       ConfigStatus confstatus;
+
+       // Step 3: Read the configuration for the modules. This must be done as part of
+       // its own step so that services provided by modules can be registered before
+       // the configuration is read.
+       for (ModuleMap::const_iterator i = Modules.begin(); i != Modules.end(); ++i)
+       {
+               Module* mod = i->second;
+               try
+               {
+                       ServerInstance->Logs->Log("MODULE", LOG_DEBUG, "Reading configuration for %s", i->first.c_str());
+                       mod->ReadConfig(confstatus);
+               }
+               catch (CoreException& modexcept)
+               {
+                       LastModuleError = "Unable to read the configuration for " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
+                       ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+                       std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
+                       ServerInstance->Exit(EXIT_STATUS_CONFIG);
+               }
+       }
+
+       if (!PrioritizeHooks())
+               ServerInstance->Exit(EXIT_STATUS_MODULE);
 }
 
-CmdResult InspIRCd::CallCommandHandler(const std::string &commandname, const std::vector<std::string>& parameters, User* user)
+std::string& ModuleManager::LastError()
 {
-       return this->Parser->CallHandler(commandname, parameters, user);
+       return LastModuleError;
 }
 
-bool InspIRCd::IsValidModuleCommand(const std::string &commandname, int pcnt, User* user)
+void ModuleManager::AddServices(const ServiceList& list)
 {
-       return this->Parser->IsValidCommand(commandname, pcnt, user);
+       for (ServiceList::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               ServiceProvider& s = **i;
+               AddService(s);
+       }
 }
 
 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:
-                       if (!ServerInstance->Modes->AddMode(static_cast<ModeHandler*>(&item)))
-                               throw ModuleException("Mode "+std::string(item.name)+" already exists.");
-                       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:
                {
+                       if ((!item.name.compare(0, 5, "mode/", 5)) || (!item.name.compare(0, 6, "umode/", 6)))
+                               throw ModuleException("The \"mode/\" and the \"umode\" service name prefixes are reserved.");
+
                        DataProviders.insert(std::make_pair(item.name, &item));
                        std::string::size_type slash = item.name.find('/');
                        if (slash != std::string::npos)
@@ -469,11 +566,14 @@ void ModuleManager::AddService(ServiceProvider& item)
                                DataProviders.insert(std::make_pair(item.name.substr(0, slash), &item));
                                DataProviders.insert(std::make_pair(item.name.substr(slash + 1), &item));
                        }
-                       return;
+                       dynamic_reference_base::reset_all();
+                       break;
                }
                default:
-                       throw ModuleException("Cannot add unknown service type");
+                       item.RegisterService();
        }
+
+       FOREACH_MOD(OnServiceAdd, (item));
 }
 
 void ModuleManager::DelService(ServiceProvider& item)
@@ -483,22 +583,18 @@ void ModuleManager::DelService(ServiceProvider& item)
                case SERVICE_MODE:
                        if (!ServerInstance->Modes->DelMode(static_cast<ModeHandler*>(&item)))
                                throw ModuleException("Mode "+std::string(item.name)+" does not exist.");
-                       return;
+                       // Fall through
                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:
                        throw ModuleException("Cannot delete unknown service type");
        }
+
+       FOREACH_MOD(OnServiceDel, (item));
 }
 
 ServiceProvider* ModuleManager::FindService(ServiceType type, const std::string& name)
@@ -519,85 +615,66 @@ 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 std::vector<dynamic_reference_base*>;
-       dynrefs->push_back(this);
+               dynrefs = new insp::intrusive_list<dynamic_reference_base>;
+       dynrefs->push_front(this);
+
+       // Resolve unless there is no ModuleManager (part of class InspIRCd)
+       if (ServerInstance)
+               resolve();
 }
 
 dynamic_reference_base::~dynamic_reference_base()
 {
-       for(unsigned int i = 0; i < dynrefs->size(); i++)
+       dynrefs->erase(this);
+       if (dynrefs->empty())
        {
-               if (dynrefs->at(i) == this)
-               {
-                       unsigned int last = dynrefs->size() - 1;
-                       if (i != last)
-                               dynrefs->at(i) = dynrefs->at(last);
-                       dynrefs->erase(dynrefs->begin() + last);
-                       if (dynrefs->empty())
-                       {
-                               delete dynrefs;
-                               dynrefs = NULL;
-                       }
-                       return;
-               }
+               delete dynrefs;
+               dynrefs = NULL;
        }
 }
 
 void dynamic_reference_base::SetProvider(const std::string& newname)
 {
        name = newname;
-       ClearCache();
-}
-
-void dynamic_reference_base::lookup()
-{
-       if (!*this)
-               throw ModuleException("Dynamic reference to '" + name + "' failed to resolve");
+       resolve();
 }
 
-dynamic_reference_base::operator bool()
+void dynamic_reference_base::resolve()
 {
-       if (!value)
+       // 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))
        {
-               std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.find(name);
-               if (i != ServerInstance->Modules->DataProviders.end())
-                       value = static_cast<DataProvider*>(i->second);
+               ServiceProvider* newvalue = i->second;
+               if (value != newvalue)
+               {
+                       value = newvalue;
+                       if (hook)
+                               hook->OnCapture();
+               }
        }
-       return (value != NULL);
-}
-
-void InspIRCd::SendMode(const std::vector<std::string>& parameters, User *user)
-{
-       this->Modes->Process(parameters, user);
-}
-
-
-void InspIRCd::SendGlobalMode(const std::vector<std::string>& parameters, User *user)
-{
-       Modes->Process(parameters, user);
-       if (!Modes->GetLastParse().empty())
-               this->PI->SendMode(parameters[0], Modes->GetLastParseParams(), Modes->GetLastParseTranslate());
-}
-
-bool InspIRCd::AddResolver(Resolver* r, bool cached)
-{
-       if (!cached)
-               return this->Res->AddResolverClass(r);
        else
-       {
-               r->TriggerCachedResult();
-               delete r;
-               return true;
-       }
+               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;
@@ -605,181 +682,21 @@ Module* ModuleManager::Find(const std::string &name)
                return modfind->second;
 }
 
-const std::vector<std::string> ModuleManager::GetAllModuleNames(int filter)
+void ModuleManager::AddReferent(const std::string& name, ServiceProvider* service)
 {
-       std::vector<std::string> retval;
-       for (std::map<std::string, Module*>::iterator x = Modules.begin(); x != Modules.end(); ++x)
-               if (!filter || (x->second->GetVersion().Flags & filter))
-                       retval.push_back(x->first);
-       return retval;
-}
-
-ConfigReader::ConfigReader()
-{
-       this->error = 0;
-       ServerInstance->Logs->Log("MODULE", DEBUG, "ConfigReader is deprecated in 2.0; "
-               "use ServerInstance->Config->ConfValue(\"key\") or ->ConfTags(\"key\") instead");
-}
-
-
-ConfigReader::~ConfigReader()
-{
-}
-
-static ConfigTag* SlowGetTag(const std::string &tag, int index)
-{
-       ConfigTagList tags = ServerInstance->Config->ConfTags(tag);
-       while (tags.first != tags.second)
-       {
-               if (!index)
-                       return tags.first->second;
-               tags.first++;
-               index--;
-       }
-       return NULL;
-}
-
-std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds)
-{
-       std::string result = default_value;
-       ConfigTag* conftag = SlowGetTag(tag, index);
-       if (!conftag || !conftag->readString(name, result, allow_linefeeds))
-       {
-               this->error = CONF_VALUE_NOT_FOUND;
-       }
-       return result;
-}
-
-std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, int index, bool allow_linefeeds)
-{
-       return ReadValue(tag, name, "", index, allow_linefeeds);
-}
-
-bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index)
-{
-       bool def = (default_value == "yes");
-       ConfigTag* conftag = SlowGetTag(tag, index);
-       return conftag ? conftag->getBool(name, def) : def;
-}
-
-bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, int index)
-{
-       return ReadFlag(tag, name, "", index);
-}
-
-
-int ConfigReader::ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool need_positive)
-{
-       int v = atoi(default_value.c_str());
-       ConfigTag* conftag = SlowGetTag(tag, index);
-       int result = conftag ? conftag->getInt(name, v) : v;
-
-       if ((need_positive) && (result < 0))
-       {
-               this->error = CONF_INT_NEGATIVE;
-               return 0;
-       }
-
-       return result;
-}
-
-int ConfigReader::ReadInteger(const std::string &tag, const std::string &name, int index, bool need_positive)
-{
-       return ReadInteger(tag, name, "", index, need_positive);
-}
-
-long ConfigReader::GetError()
-{
-       long olderr = this->error;
-       this->error = 0;
-       return olderr;
-}
-
-int ConfigReader::Enumerate(const std::string &tag)
-{
-       ServerInstance->Logs->Log("MODULE", DEBUG, "Module is using ConfigReader::Enumerate on %s; this is slow!",
-               tag.c_str());
-       int i=0;
-       while (SlowGetTag(tag, i)) i++;
-       return i;
-}
-
-FileReader::FileReader(const std::string &filename)
-{
-       LoadFile(filename);
-}
-
-FileReader::FileReader()
-{
-}
-
-std::string FileReader::Contents()
-{
-       std::string x;
-       for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
-       {
-               x.append(*a);
-               x.append("\r\n");
-       }
-       return x;
-}
-
-unsigned long FileReader::ContentSize()
-{
-       return this->contentsize;
-}
-
-void FileReader::CalcSize()
-{
-       unsigned long n = 0;
-       for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
-               n += (a->length() + 2);
-       this->contentsize = n;
+       DataProviders.insert(std::make_pair(name, service));
+       dynamic_reference_base::reset_all();
 }
 
-void FileReader::LoadFile(const std::string &filename)
+void ModuleManager::DelReferent(ServiceProvider* service)
 {
-       std::map<std::string, file_cache>::iterator file = ServerInstance->Config->Files.find(filename);
-       if (file != ServerInstance->Config->Files.end())
+       for (std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
        {
-               this->fc = file->second;
+               ServiceProvider* curr = i->second;
+               if (curr == service)
+                       DataProviders.erase(i++);
+               else
+                       ++i;
        }
-       else
-       {
-               fc.clear();
-               FILE* f = fopen(filename.c_str(), "r");
-               if (!f)
-                       return;
-               char linebuf[MAXBUF*10];
-               while (fgets(linebuf, sizeof(linebuf), f))
-               {
-                       int len = strlen(linebuf);
-                       if (len)
-                               fc.push_back(std::string(linebuf, len - 1));
-               }
-               fclose(f);
-       }
-       CalcSize();
-}
-
-
-FileReader::~FileReader()
-{
-}
-
-bool FileReader::Exists()
-{
-       return (!(fc.size() == 0));
-}
-
-std::string FileReader::GetLine(int x)
-{
-       if ((x<0) || ((unsigned)x>=fc.size()))
-               return "";
-       return fc[x];
-}
-
-int FileReader::FileSize()
-{
-       return fc.size();
+       dynamic_reference_base::reset_all();
 }
diff --git a/src/modules/account.h b/src/modules/account.h
deleted file mode 100644 (file)
index ba671ba..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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/>.
- */
-
-
-#ifndef ACCOUNT_H
-#define ACCOUNT_H
-
-#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)
-       {
-       }
-};
-
-typedef StringExtItem AccountExtItem;
-
-inline AccountExtItem* GetAccountExtItem()
-{
-       return static_cast<AccountExtItem*>(ServerInstance->Extensions.GetItem("accountname"));
-}
-
-#endif
index f4a9316a133670eb6ab413b962642db266dae1c5..4f97075eaca5c41393164bada71d3d4a7c245e9a 100644 (file)
@@ -2,7 +2,7 @@ This directory stores modules which require external libraries to compile.
 For example, m_regex_pcre requires the PCRE libraries.
 
 To compile any of these modules first ensure you have the required dependencies
-(read the online documentation at https://docs.inspircd.org/) and then symlink
+(read the online documentation at https://docs.inspircd.org) and then symlink
 the .cpp file from this directory into the parent directory (src/modules/).
 
 Alternatively, use the command: ./configure --enable-extras=m_extra.cpp, which will
diff --git a/src/modules/extra/m_geo_maxmind.cpp b/src/modules/extra/m_geo_maxmind.cpp
new file mode 100644 (file)
index 0000000..f249ecf
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2019 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/>.
+ */
+
+/// $CompilerFlags: find_compiler_flags("libmaxminddb" "")
+/// $LinkerFlags: find_linker_flags("libmaxminddb" "-lmaxminddb")
+
+/// $PackageInfo: require_system("darwin") libmaxminddb pkg-config
+/// $PackageInfo: require_system("debian" "9.0") libmaxminddb-dev pkg-config
+/// $PackageInfo: require_system("ubuntu" "16.04") libmaxminddb-dev pkg-config
+
+#ifdef _WIN32
+# pragma comment(lib, "libmaxminddb.lib")
+#endif
+
+#include <maxminddb.h>
+#include "inspircd.h"
+#include "modules/geolocation.h"
+
+class GeolocationExtItem : public LocalExtItem
+{
+ public:
+       GeolocationExtItem(Module* parent)
+               : LocalExtItem("geolocation", ExtensionItem::EXT_USER, parent)
+       {
+       }
+
+       void free(Extensible* container, void* item) CXX11_OVERRIDE
+       {
+               Geolocation::Location* old = static_cast<Geolocation::Location*>(item);
+               if (old)
+                       old->refcount_dec();
+       }
+
+       Geolocation::Location* get(const Extensible* item) const
+       {
+               return static_cast<Geolocation::Location*>(get_raw(item));
+       }
+
+       void set(Extensible* item, Geolocation::Location* value)
+       {
+               value->refcount_inc();
+               free(item, set_raw(item, value));
+       }
+
+       void unset(Extensible* container)
+       {
+               free(container, unset_raw(container));
+       }
+};
+
+typedef insp::flat_map<std::string, Geolocation::Location*> LocationMap;
+
+class GeolocationAPIImpl : public Geolocation::APIBase
+{
+ public:
+       GeolocationExtItem ext;
+       LocationMap locations;
+       MMDB_s mmdb;
+
+       GeolocationAPIImpl(Module* parent)
+               : Geolocation::APIBase(parent)
+               , ext(parent)
+       {
+       }
+
+       Geolocation::Location* GetLocation(User* user) CXX11_OVERRIDE
+       {
+               // If we have the location cached then use that instead.
+               Geolocation::Location* location = ext.get(user);
+               if (location)
+                       return location;
+
+               // Attempt to locate this user.
+               location = GetLocation(user->client_sa);
+               if (!location)
+                       return NULL;
+
+               // We found the user. Cache their location for future use.
+               ext.set(user, location);
+               return location;
+       }
+
+       Geolocation::Location* GetLocation(irc::sockets::sockaddrs& sa) CXX11_OVERRIDE
+       {
+               // Attempt to look up the socket address.
+               int result;
+               MMDB_lookup_result_s lookup = MMDB_lookup_sockaddr(&mmdb, &sa.sa, &result);
+               if (result != MMDB_SUCCESS || !lookup.found_entry)
+                       return NULL;
+
+               // Attempt to retrieve the country code.
+               MMDB_entry_data_s country_code;
+               result = MMDB_get_value(&lookup.entry, &country_code, "country", "iso_code", NULL);
+               if (result != MMDB_SUCCESS || !country_code.has_data || country_code.type != MMDB_DATA_TYPE_UTF8_STRING || country_code.data_size != 2)
+                       return NULL;
+
+               // If the country has been seen before then use our cached Location object.
+               const std::string code(country_code.utf8_string, country_code.data_size);
+               LocationMap::iterator liter = locations.find(code);
+               if (liter != locations.end())
+                       return liter->second;
+
+               // Attempt to retrieve the country name.
+               MMDB_entry_data_s country_name;
+               result = MMDB_get_value(&lookup.entry, &country_name, "country", "names", "en", NULL);
+               if (result != MMDB_SUCCESS || !country_name.has_data || country_name.type != MMDB_DATA_TYPE_UTF8_STRING)
+                       return NULL;
+
+               // Create a Location object and cache it.
+               const std::string cname(country_name.utf8_string, country_name.data_size);
+               Geolocation::Location* location = new Geolocation::Location(code, cname);
+               locations[code] = location;
+               return location;
+       }
+};
+
+class ModuleGeoMaxMind : public Module
+{
+ private:
+       GeolocationAPIImpl geoapi;
+
+ public:
+       ModuleGeoMaxMind()
+               : geoapi(this)
+       {
+               memset(&geoapi.mmdb, 0, sizeof(geoapi.mmdb));
+       }
+
+       ~ModuleGeoMaxMind()
+       {
+               MMDB_close(&geoapi.mmdb);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides Geolocation lookups using the libMaxMindDB library", VF_VENDOR);
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("maxmind");
+               const std::string file = ServerInstance->Config->Paths.PrependConfig(tag->getString("file", "GeoLite2-Country.mmdb"));
+
+               // Try to read the new database.
+               MMDB_s mmdb;
+               int result = MMDB_open(file.c_str(), MMDB_MODE_MMAP, &mmdb);
+               if (result != MMDB_SUCCESS)
+                       throw ModuleException(InspIRCd::Format("Unable to load the MaxMind database (%s): %s",
+                               file.c_str(), MMDB_strerror(result)));
+
+               // Swap the new database with the old database.
+               std::swap(mmdb, geoapi.mmdb);
+
+               // Free the old database.
+               MMDB_close(&mmdb);
+       }
+
+       void OnGarbageCollect() CXX11_OVERRIDE
+       {
+               for (LocationMap::iterator iter = geoapi.locations.begin(); iter != geoapi.locations.end(); )
+               {       
+                       Geolocation::Location* location = iter->second;
+                       if (location->GetUseCount())
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Preserving geolocation data for %s (%s) with use count %u... ",
+                                       location->GetName().c_str(), location->GetCode().c_str(), location->GetUseCount());
+                               iter++;
+                       }
+                       else
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Deleting unused geolocation data for %s (%s)",
+                                       location->GetName().c_str(), location->GetCode().c_str());
+                               delete location;
+                               iter = geoapi.locations.erase(iter);
+                       }
+               }
+       }
+
+       void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
+       {
+               // Unset the extension so that the location of this user is looked
+               // up again next time it is requested.
+               geoapi.ext.unset(user);
+       }
+};
+
+MODULE_INIT(ModuleGeoMaxMind)
diff --git a/src/modules/extra/m_geoip.cpp b/src/modules/extra/m_geoip.cpp
deleted file mode 100644 (file)
index 03b7a55..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@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/>.
- */
-
-
-#include "inspircd.h"
-#include "xline.h"
-
-#include <GeoIP.h>
-
-#ifdef _WIN32
-# pragma comment(lib, "GeoIP.lib")
-#endif
-
-/* $ModDesc: Provides a way to restrict users by country using GeoIP lookup */
-/* $LinkerFlags: -lGeoIP */
-
-class ModuleGeoIP : public Module
-{
-       LocalStringExt ext;
-       GeoIP* gi;
-
-       std::string* SetExt(LocalUser* user)
-       {
-               const char* c = GeoIP_country_code_by_addr(gi, user->GetIPString());
-               if (!c)
-                       c = "UNK";
-
-               std::string* cc = new std::string(c);
-               ext.set(user, cc);
-               return cc;
-       }
-
- public:
-       ModuleGeoIP() : ext("geoip_cc", this), gi(NULL)
-       {
-       }
-
-       void init()
-       {
-               gi = GeoIP_new(GEOIP_STANDARD);
-               if (gi == NULL)
-                               throw ModuleException("Unable to initialize geoip, are you missing GeoIP.dat?");
-
-               ServerInstance->Modules->AddService(ext);
-               Implementation eventlist[] = { I_OnSetConnectClass, I_OnSetUserIP, I_OnStats };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
-               for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
-               {
-                       LocalUser* user = *i;
-                       if ((user->registered == REG_ALL) && (!ext.get(user)))
-                       {
-                               SetExt(user);
-                       }
-               }
-       }
-
-       ~ModuleGeoIP()
-       {
-               if (gi)
-                       GeoIP_delete(gi);
-       }
-
-       Version GetVersion()
-       {
-               return Version("Provides a way to assign users to connect classes by country using GeoIP lookup", VF_VENDOR);
-       }
-
-       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
-       {
-               std::string* cc = ext.get(user);
-               if (!cc)
-                       cc = SetExt(user);
-
-               std::string geoip = myclass->config->getString("geoip");
-               if (geoip.empty())
-                       return MOD_RES_PASSTHRU;
-               irc::commasepstream list(geoip);
-               std::string country;
-               while (list.GetToken(country))
-                       if (country == *cc)
-                               return MOD_RES_PASSTHRU;
-               return MOD_RES_DENY;
-       }
-
-       void OnSetUserIP(LocalUser* user)
-       {
-               // If user has sent NICK/USER, re-set the ExtItem as this is likely CGI:IRC changing the IP
-               if (user->registered == REG_NICKUSER)
-                       SetExt(user);
-       }
-
-       ModResult OnStats(char symbol, User* user, string_list &out)
-       {
-               if (symbol != '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)
-               {
-                       std::string* cc = ext.get(*i);
-                       if (cc)
-                               results[*cc]++;
-                       else
-                               unknown++;
-               }
-
-               std::string p = ServerInstance->Config->ServerName + " 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));
-               }
-
-               if (unknown)
-                       out.push_back(p + "Unknown " + ConvToStr(unknown));
-
-               return MOD_RES_DENY;
-       }
-};
-
-MODULE_INIT(ModuleGeoIP)
-
diff --git a/src/modules/extra/m_ldap.cpp b/src/modules/extra/m_ldap.cpp
new file mode 100644 (file)
index 0000000..65b6b2b
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   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
+ * 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: -llber -lldap_r
+
+/// $PackageInfo: require_system("centos") openldap-devel
+/// $PackageInfo: require_system("debian") libldap2-dev
+/// $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, "libldap_r.lib")
+# pragma comment(lib, "liblber.lib")
+#endif
+
+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
+{
+       LDAP* con;
+       reference<ConfigTag> config;
+       time_t last_connect;
+       int searchscope;
+       time_t timeout;
+
+ public:
+       static LDAPMod** BuildMods(const LDAPMods& attributes)
+       {
+               LDAPMod** mods = new LDAPMod*[attributes.size() + 1];
+               memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1));
+               for (unsigned int x = 0; x < attributes.size(); ++x)
+               {
+                       const LDAPModification& l = attributes[x];
+                       LDAPMod* mod = new LDAPMod;
+                       mods[x] = mod;
+
+                       if (l.op == LDAPModification::LDAP_ADD)
+                               mod->mod_op = LDAP_MOD_ADD;
+                       else if (l.op == LDAPModification::LDAP_DEL)
+                               mod->mod_op = LDAP_MOD_DELETE;
+                       else if (l.op == LDAPModification::LDAP_REPLACE)
+                               mod->mod_op = LDAP_MOD_REPLACE;
+                       else if (l.op != 0)
+                       {
+                               FreeMods(mods);
+                               throw LDAPException("Unknown LDAP operation");
+                       }
+                       mod->mod_type = strdup(l.name.c_str());
+                       mod->mod_values = new char*[l.values.size() + 1];
+                       memset(mod->mod_values, 0, sizeof(char*) * (l.values.size() + 1));
+                       for (unsigned int j = 0, c = 0; j < l.values.size(); ++j)
+                               if (!l.values[j].empty())
+                                       mod->mod_values[c++] = strdup(l.values[j].c_str());
+               }
+               return mods;
+       }
+
+       static void FreeMods(LDAPMod** mods)
+       {
+               for (unsigned int i = 0; mods[i] != NULL; ++i)
+               {
+                       LDAPMod* mod = mods[i];
+                       if (mod->mod_type != NULL)
+                               free(mod->mod_type);
+                       if (mod->mod_values != NULL)
+                       {
+                               for (unsigned int j = 0; mod->mod_values[j] != NULL; ++j)
+                                       free(mod->mod_values[j]);
+                               delete[] mod->mod_values;
+                       }
+               }
+               delete[] mods;
+       }
+
+ private:
+       void Reconnect()
+       {
+               // Only try one connect a minute. It is an expensive blocking operation
+               if (last_connect > ServerInstance->Time() - 60)
+                       throw LDAPException("Unable to connect to LDAP service " + this->name + ": reconnecting too fast");
+               last_connect = ServerInstance->Time();
+
+               ldap_unbind_ext(this->con, NULL, NULL);
+               Connect();
+       }
+
+       void QueueRequest(LDAPRequest* r)
+       {
+               this->LockQueue();
+               this->queries.push_back(r);
+               this->UnlockQueueWakeup();
+       }
+
+ public:
+       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)
+       {
+               std::string scope = config->getString("searchscope");
+               if (stdalgo::string::equalsci(scope, "base"))
+                       searchscope = LDAP_SCOPE_BASE;
+               else if (stdalgo::string::equalsci(scope, "onelevel"))
+                       searchscope = LDAP_SCOPE_ONELEVEL;
+               else
+                       searchscope = LDAP_SCOPE_SUBTREE;
+               timeout = config->getDuration("timeout", 5);
+
+               Connect();
+       }
+
+       ~LDAPService()
+       {
+               this->LockQueue();
+
+               for (unsigned int i = 0; i < this->queries.size(); ++i)
+               {
+                       LDAPRequest* req = this->queries[i];
+
+                       /* 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);
+
+                       delete req;
+               }
+               this->queries.clear();
+
+               for (unsigned int i = 0; i < this->results.size(); ++i)
+               {
+                       LDAPRequest* req = this->results[i];
+
+                       /* 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();
+
+               this->UnlockQueue();
+
+               ldap_unbind_ext(this->con, NULL, NULL);
+       }
+
+       void Connect()
+       {
+               std::string server = config->getString("server");
+               int i = ldap_initialize(&this->con, server.c_str());
+               if (i != LDAP_SUCCESS)
+                       throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
+
+               const int version = LDAP_VERSION3;
+               i = ldap_set_option(this->con, LDAP_OPT_PROTOCOL_VERSION, &version);
+               if (i != LDAP_OPT_SUCCESS)
+               {
+                       ldap_unbind_ext(this->con, NULL, NULL);
+                       this->con = NULL;
+                       throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i));
+               }
+
+               const struct timeval tv = { 0, 0 };
+               i = ldap_set_option(this->con, LDAP_OPT_NETWORK_TIMEOUT, &tv);
+               if (i != LDAP_OPT_SUCCESS)
+               {
+                       ldap_unbind_ext(this->con, NULL, NULL);
+                       this->con = NULL;
+                       throw LDAPException("Unable to set timeout for " + this->name + ": " + ldap_err2string(i));
+               }
+       }
+
+       void BindAsManager(LDAPInterface* i) CXX11_OVERRIDE
+       {
+               std::string binddn = config->getString("binddn");
+               std::string bindauth = config->getString("bindauth");
+               this->Bind(i, binddn, bindauth);
+       }
+
+       void Bind(LDAPInterface* i, const std::string& who, const std::string& pass) CXX11_OVERRIDE
+       {
+               LDAPBind* b = new LDAPBind(this, i, who, pass);
+               QueueRequest(b);
+       }
+
+       void Search(LDAPInterface* i, const std::string& base, const std::string& filter) CXX11_OVERRIDE
+       {
+               if (i == NULL)
+                       throw LDAPException("No interface");
+
+               LDAPSearch* s = new LDAPSearch(this, i, base, searchscope, filter);
+               QueueRequest(s);
+       }
+
+       void Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) CXX11_OVERRIDE
+       {
+               LDAPAdd* add = new LDAPAdd(this, i, dn, attributes);
+               QueueRequest(add);
+       }
+
+       void Del(LDAPInterface* i, const std::string& dn) CXX11_OVERRIDE
+       {
+               LDAPDel* del = new LDAPDel(this, i, dn);
+               QueueRequest(del);
+       }
+
+       void Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) CXX11_OVERRIDE
+       {
+               LDAPModify* mod = new LDAPModify(this, i, base, attributes);
+               QueueRequest(mod);
+       }
+
+       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);
+       }
+
+ private:
+       void BuildReply(int res, LDAPRequest* req)
+       {
+               LDAPResult* ldap_result = req->result = new LDAPResult();
+               req->result->type = req->type;
+
+               if (res != LDAP_SUCCESS)
+               {
+                       ldap_result->error = ldap_err2string(res);
+                       return;
+               }
+
+               if (req->message == NULL)
+               {
+                       return;
+               }
+
+               /* a search result */
+
+               for (LDAPMessage* cur = ldap_first_message(this->con, req->message); cur; cur = ldap_next_message(this->con, cur))
+               {
+                       LDAPAttributes attributes;
+
+                       char* dn = ldap_get_dn(this->con, cur);
+                       if (dn != NULL)
+                       {
+                               attributes["dn"].push_back(dn);
+                               ldap_memfree(dn);
+                               dn = NULL;
+                       }
+
+                       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);
+
+                       ldap_result->messages.push_back(attributes);
+               }
+       }
+
+       void SendRequests()
+       {
+               process_mutex.Lock();
+
+               query_queue q;
+               this->LockQueue();
+               queries.swap(q);
+               this->UnlockQueue();
+
+               if (q.empty())
+               {
+                       process_mutex.Unlock();
+                       return;
+               }
+
+               for (unsigned int i = 0; i < q.size(); ++i)
+               {
+                       LDAPRequest* req = q[i];
+                       int ret = req->run();
+
+                       if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+                       {
+                               /* try again */
+                               try
+                               {
+                                       Reconnect();
+                               }
+                               catch (const LDAPException &)
+                               {
+                               }
+
+                               ret = req->run();
+                       }
+
+                       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();
+                       if (this->queries.empty())
+                               this->WaitForQueue();
+                       this->UnlockQueue();
+
+                       SendRequests();
+               }
+       }
+
+       void OnNotify() CXX11_OVERRIDE
+       {
+               query_queue r;
+
+               this->LockQueue();
+               this->results.swap(r);
+               this->UnlockQueue();
+
+               for (unsigned int i = 0; i < r.size(); ++i)
+               {
+                       LDAPRequest* req = r[i];
+                       LDAPInterface* li = req->inter;
+                       LDAPResult* res = req->result;
+
+                       if (!res->error.empty())
+                               li->OnError(*res);
+                       else
+                               li->OnResult(*res);
+
+                       delete req;
+               }
+       }
+
+       LDAP* GetConnection()
+       {
+               return con;
+       }
+};
+
+class ModuleLDAP : public Module
+{
+       typedef insp::flat_map<std::string, LDAPService*> ServiceMap;
+       ServiceMap LDAPServices;
+
+ public:
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ServiceMap conns;
+
+               ConfigTagList tags = ServerInstance->Config->ConfTags("database");
+               for (ConfigIter i = tags.first; i != tags.second; i++)
+               {
+                       const reference<ConfigTag>& tag = i->second;
+
+                       if (!stdalgo::string::equalsci(tag->getString("module"), "ldap"))
+                               continue;
+
+                       std::string id = tag->getString("id");
+
+                       ServiceMap::iterator curr = LDAPServices.find(id);
+                       if (curr == LDAPServices.end())
+                       {
+                               LDAPService* conn = new LDAPService(this, tag);
+                               conns[id] = conn;
+
+                               ServerInstance->Modules->AddService(*conn);
+                               ServerInstance->Threads.Start(conn);
+                       }
+                       else
+                       {
+                               conns.insert(*curr);
+                               LDAPServices.erase(curr);
+                       }
+               }
+
+               for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
+               {
+                       LDAPService* conn = i->second;
+                       ServerInstance->Modules->DelService(*conn);
+                       conn->join();
+                       conn->OnNotify();
+                       delete conn;
+               }
+
+               LDAPServices.swap(conns);
+       }
+
+       void OnUnloadModule(Module* m) CXX11_OVERRIDE
+       {
+               for (ServiceMap::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
+               {
+                       LDAPService* s = it->second;
+
+                       s->process_mutex.Lock();
+                       s->LockQueue();
+
+                       for (unsigned int i = s->queries.size(); i > 0; --i)
+                       {
+                               LDAPRequest* req = s->queries[i - 1];
+                               LDAPInterface* li = req->inter;
+
+                               if (li->creator == m)
+                               {
+                                       s->queries.erase(s->queries.begin() + i - 1);
+                                       delete req;
+                               }
+                       }
+
+                       for (unsigned int i = s->results.size(); i > 0; --i)
+                       {
+                               LDAPRequest* req = s->results[i - 1];
+                               LDAPInterface* li = req->inter;
+
+                               if (li->creator == m)
+                               {
+                                       s->results.erase(s->results.begin() + i - 1);
+                                       delete req;
+                               }
+                       }
+
+                       s->UnlockQueue();
+                       s->process_mutex.Unlock();
+               }
+       }
+
+       ~ModuleLDAP()
+       {
+               for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
+               {
+                       LDAPService* conn = i->second;
+                       conn->join();
+                       conn->OnNotify();
+                       delete conn;
+               }
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides LDAP support", VF_VENDOR);
+       }
+};
+
+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_ldapauth.cpp b/src/modules/extra/m_ldapauth.cpp
deleted file mode 100644 (file)
index 405bab0..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2011 Pierre Carrier <pierre@spotify.com>
- *   Copyright (C) 2009-2010 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2008 Dennis Friis <peavey@inspircd.org>
- *   Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@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 "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include <ldap.h>
-
-#ifdef _WIN32
-# pragma comment(lib, "libldap.lib")
-# pragma comment(lib, "liblber.lib")
-#endif
-
-/* $ModDesc: Allow/Deny connections based upon answer from LDAP server */
-/* $LinkerFlags: -lldap */
-
-struct RAIILDAPString
-{
-       char *str;
-
-       RAIILDAPString(char *Str)
-               : str(Str)
-       {
-       }
-
-       ~RAIILDAPString()
-       {
-               ldap_memfree(str);
-       }
-
-       operator char*()
-       {
-               return str;
-       }
-
-       operator std::string()
-       {
-               return str;
-       }
-};
-
-struct RAIILDAPMessage
-{
-       RAIILDAPMessage()
-       {
-       }
-
-       ~RAIILDAPMessage()
-       {
-               dealloc();
-       }
-
-       void dealloc()
-       {
-               ldap_msgfree(msg);
-       }
-
-       operator LDAPMessage*()
-       {
-               return msg;
-       }
-
-       LDAPMessage **operator &()
-       {
-               return &msg;
-       }
-
-       LDAPMessage *msg;
-};
-
-class ModuleLDAPAuth : public Module
-{
-       LocalIntExt ldapAuthed;
-       LocalStringExt ldapVhost;
-       std::string base;
-       std::string attribute;
-       std::string ldapserver;
-       std::string allowpattern;
-       std::string killreason;
-       std::string username;
-       std::string password;
-       std::string vhost;
-       std::vector<std::string> whitelistedcidrs;
-       std::vector<std::pair<std::string, std::string> > requiredattributes;
-       int searchscope;
-       bool verbose;
-       bool useusername;
-       LDAP *conn;
-
-public:
-       ModuleLDAPAuth()
-               : ldapAuthed("ldapauth", this)
-               , ldapVhost("ldapauth_vhost", this)
-       {
-               conn = NULL;
-       }
-
-       void init()
-       {
-               ServerInstance->Modules->AddService(ldapAuthed);
-               ServerInstance->Modules->AddService(ldapVhost);
-               Implementation eventlist[] = { I_OnCheckReady, I_OnRehash,I_OnUserRegister, I_OnUserConnect };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
-       }
-
-       ~ModuleLDAPAuth()
-       {
-               if (conn)
-                       ldap_unbind_ext(conn, NULL, NULL);
-       }
-
-       void OnRehash(User* user)
-       {
-               ConfigTag* tag = ServerInstance->Config->ConfValue("ldapauth");
-               whitelistedcidrs.clear();
-               requiredattributes.clear();
-
-               base                    = tag->getString("baserdn");
-               attribute               = tag->getString("attribute");
-               ldapserver              = tag->getString("server");
-               allowpattern    = tag->getString("allowpattern");
-               killreason              = tag->getString("killreason");
-               std::string scope       = tag->getString("searchscope");
-               username                = tag->getString("binddn");
-               password                = tag->getString("bindauth");
-               vhost                   = tag->getString("host");
-               verbose                 = tag->getBool("verbose");              /* Set to true if failed connects should be reported to operators */
-               useusername             = tag->getBool("userfield");
-
-               ConfigTagList whitelisttags = ServerInstance->Config->ConfTags("ldapwhitelist");
-
-               for (ConfigIter i = whitelisttags.first; i != whitelisttags.second; ++i)
-               {
-                       std::string cidr = i->second->getString("cidr");
-                       if (!cidr.empty()) {
-                               whitelistedcidrs.push_back(cidr);
-                       }
-               }
-
-               ConfigTagList attributetags = ServerInstance->Config->ConfTags("ldaprequire");
-
-               for (ConfigIter i = attributetags.first; i != attributetags.second; ++i)
-               {
-                       const std::string attr = i->second->getString("attribute");
-                       const std::string val = i->second->getString("value");
-
-                       if (!attr.empty() && !val.empty())
-                               requiredattributes.push_back(make_pair(attr, val));
-               }
-
-               if (scope == "base")
-                       searchscope = LDAP_SCOPE_BASE;
-               else if (scope == "onelevel")
-                       searchscope = LDAP_SCOPE_ONELEVEL;
-               else searchscope = LDAP_SCOPE_SUBTREE;
-
-               Connect();
-       }
-
-       bool Connect()
-       {
-               if (conn != NULL)
-                       ldap_unbind_ext(conn, NULL, NULL);
-               int res, v = LDAP_VERSION3;
-               res = ldap_initialize(&conn, ldapserver.c_str());
-               if (res != LDAP_SUCCESS)
-               {
-                       if (verbose)
-                               ServerInstance->SNO->WriteToSnoMask('c', "LDAP connection failed: %s", ldap_err2string(res));
-                       conn = NULL;
-                       return false;
-               }
-
-               res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
-               if (res != LDAP_SUCCESS)
-               {
-                       if (verbose)
-                               ServerInstance->SNO->WriteToSnoMask('c', "LDAP set protocol to v3 failed: %s", ldap_err2string(res));
-                       ldap_unbind_ext(conn, NULL, NULL);
-                       conn = NULL;
-                       return false;
-               }
-               return true;
-       }
-
-       std::string SafeReplace(const std::string &text, std::map<std::string,
-                       std::string> &replacements)
-       {
-               std::string result;
-               result.reserve(MAXBUF);
-
-               for (unsigned int i = 0; i < text.length(); ++i) {
-                       char c = text[i];
-                       if (c == '$') {
-                               // find the first nonalpha
-                               i++;
-                               unsigned int start = i;
-
-                               while (i < text.length() - 1 && isalpha(text[i + 1]))
-                                       ++i;
-
-                               std::string key = text.substr(start, (i - start) + 1);
-                               result.append(replacements[key]);
-                       } else {
-                               result.push_back(c);
-                       }
-               }
-
-          return result;
-       }
-
-       virtual void OnUserConnect(LocalUser *user)
-       {
-               std::string* cc = ldapVhost.get(user);
-               if (cc)
-               {
-                       user->ChangeDisplayedHost(cc->c_str());
-                       ldapVhost.unset(user);
-               }
-       }
-
-       ModResult OnUserRegister(LocalUser* user)
-       {
-               if ((!allowpattern.empty()) && (InspIRCd::Match(user->nick,allowpattern)))
-               {
-                       ldapAuthed.set(user,1);
-                       return MOD_RES_PASSTHRU;
-               }
-
-               for (std::vector<std::string>::iterator i = whitelistedcidrs.begin(); i != whitelistedcidrs.end(); i++)
-               {
-                       if (InspIRCd::MatchCIDR(user->GetIPString(), *i, ascii_case_insensitive_map))
-                       {
-                               ldapAuthed.set(user,1);
-                               return MOD_RES_PASSTHRU;
-                       }
-               }
-
-               if (!CheckCredentials(user))
-               {
-                       ServerInstance->Users->QuitUser(user, killreason);
-                       return MOD_RES_DENY;
-               }
-               return MOD_RES_PASSTHRU;
-       }
-
-       bool CheckCredentials(LocalUser* user)
-       {
-               if (conn == NULL)
-                       if (!Connect())
-                               return false;
-
-               if (user->password.empty())
-               {
-                       if (verbose)
-                               ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (No password provided)", user->GetFullRealHost().c_str());
-                       return false;
-               }
-
-               int res;
-               // bind anonymously if no bind DN and authentication are given in the config
-               struct berval cred;
-               cred.bv_val = const_cast<char*>(password.c_str());
-               cred.bv_len = password.length();
-
-               if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
-               {
-                       if (res == LDAP_SERVER_DOWN)
-                       {
-                               // Attempt to reconnect if the connection dropped
-                               if (verbose)
-                                       ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
-                               Connect();
-                               res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
-                       }
-
-                       if (res != LDAP_SUCCESS)
-                       {
-                               if (verbose)
-                                       ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP bind failed: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
-                               ldap_unbind_ext(conn, NULL, NULL);
-                               conn = NULL;
-                               return false;
-                       }
-               }
-
-               RAIILDAPMessage msg;
-               std::string what;
-               std::string::size_type pos = user->password.find(':');
-               // If a username is provided in PASS, use it, othewrise user their nick or ident
-               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));
-               }
-               if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
-               {
-                       if (verbose)
-                               ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search failed: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
-                       return false;
-               }
-               if (ldap_count_entries(conn, msg) > 1)
-               {
-                       if (verbose)
-                               ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search returned more than one result: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
-                       return false;
-               }
-
-               LDAPMessage *entry;
-               if ((entry = ldap_first_entry(conn, msg)) == NULL)
-               {
-                       if (verbose)
-                               ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search returned no results: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
-                       return false;
-               }
-               cred.bv_val = (char*)user->password.data();
-               cred.bv_len = user->password.length();
-               RAIILDAPString DN(ldap_get_dn(conn, entry));
-               if ((res = ldap_sasl_bind_s(conn, DN, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
-               {
-                       if (verbose)
-                               ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (%s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
-                       return false;
-               }
-
-               if (!requiredattributes.empty())
-               {
-                       bool authed = false;
-
-                       for (std::vector<std::pair<std::string, std::string> >::const_iterator it = requiredattributes.begin(); it != requiredattributes.end(); ++it)
-                       {
-                               const std::string &attr = it->first;
-                               const std::string &val = it->second;
-
-                               struct berval attr_value;
-                               attr_value.bv_val = const_cast<char*>(val.c_str());
-                               attr_value.bv_len = val.length();
-
-                               ServerInstance->Logs->Log("m_ldapauth", DEBUG, "LDAP compare: %s=%s", attr.c_str(), val.c_str());
-
-                               authed = (ldap_compare_ext_s(conn, DN, attr.c_str(), &attr_value, NULL, NULL) == LDAP_COMPARE_TRUE);
-
-                               if (authed)
-                                       break;
-                       }
-
-                       if (!authed)
-                       {
-                               if (verbose)
-                                       ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (Lacks required LDAP attributes)", user->GetFullRealHost().c_str());
-                               return false;
-                       }
-               }
-
-               if (!vhost.empty())
-               {
-                       irc::commasepstream stream(DN);
-
-                       // mashed map of key:value parts of the DN
-                       std::map<std::string, std::string> dnParts;
-
-                       std::string dnPart;
-                       while (stream.GetToken(dnPart))
-                       {
-                               pos = dnPart.find('=');
-                               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
-                               dnParts[key] = value;
-                       }
-
-                       // change host according to config key
-                       ldapVhost.set(user, SafeReplace(vhost, dnParts));
-               }
-
-               ldapAuthed.set(user,1);
-               return true;
-       }
-
-       ModResult OnCheckReady(LocalUser* user)
-       {
-               return ldapAuthed.get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
-       }
-
-       Version GetVersion()
-       {
-               return Version("Allow/Deny connections based upon answer from LDAP server", VF_VENDOR);
-       }
-
-};
-
-MODULE_INIT(ModuleLDAPAuth)
diff --git a/src/modules/extra/m_ldapoper.cpp b/src/modules/extra/m_ldapoper.cpp
deleted file mode 100644 (file)
index 1f46361..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@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 "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include <ldap.h>
-
-#ifdef _WIN32
-# pragma comment(lib, "libldap.lib")
-# pragma comment(lib, "liblber.lib")
-#endif
-
-/* $ModDesc: Adds the ability to authenticate opers via LDAP */
-/* $LinkerFlags: -lldap */
-
-// Duplicated code, also found in cmd_oper and m_sqloper
-static bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
-{
-       std::stringstream hl(hostlist);
-       std::string xhost;
-       while (hl >> xhost)
-       {
-               if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
-               {
-                       return true;
-               }
-       }
-       return false;
-}
-
-struct RAIILDAPString
-{
-       char *str;
-
-       RAIILDAPString(char *Str)
-               : str(Str)
-       {
-       }
-
-       ~RAIILDAPString()
-       {
-               ldap_memfree(str);
-       }
-
-       operator char*()
-       {
-               return str;
-       }
-
-       operator std::string()
-       {
-               return str;
-       }
-};
-
-class ModuleLDAPAuth : public Module
-{
-       std::string base;
-       std::string ldapserver;
-       std::string username;
-       std::string password;
-       std::string attribute;
-       int searchscope;
-       LDAP *conn;
-
-       bool HandleOper(LocalUser* user, const std::string& opername, const std::string& inputpass)
-       {
-               OperIndex::iterator it = ServerInstance->Config->oper_blocks.find(opername);
-               if (it == ServerInstance->Config->oper_blocks.end())
-                       return false;
-
-               ConfigTag* tag = it->second->oper_block;
-               if (!tag)
-                       return false;
-
-               std::string acceptedhosts = tag->getString("host");
-               std::string hostname = user->ident + "@" + user->host;
-               if (!OneOfMatches(hostname.c_str(), user->GetIPString(), acceptedhosts))
-                       return false;
-
-               if (!LookupOper(opername, inputpass))
-                       return false;
-
-               user->Oper(it->second);
-               return true;
-       }
-
-public:
-       ModuleLDAPAuth()
-               : conn(NULL)
-       {
-       }
-
-       void init()
-       {
-               Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
-       }
-
-       virtual ~ModuleLDAPAuth()
-       {
-               if (conn)
-                       ldap_unbind_ext(conn, NULL, NULL);
-       }
-
-       virtual void OnRehash(User* user)
-       {
-               ConfigTag* tag = ServerInstance->Config->ConfValue("ldapoper");
-
-               base                    = tag->getString("baserdn");
-               ldapserver              = tag->getString("server");
-               std::string scope       = tag->getString("searchscope");
-               username                = tag->getString("binddn");
-               password                = tag->getString("bindauth");
-               attribute               = tag->getString("attribute");
-
-               if (scope == "base")
-                       searchscope = LDAP_SCOPE_BASE;
-               else if (scope == "onelevel")
-                       searchscope = LDAP_SCOPE_ONELEVEL;
-               else searchscope = LDAP_SCOPE_SUBTREE;
-
-               Connect();
-       }
-
-       bool Connect()
-       {
-               if (conn != NULL)
-                       ldap_unbind_ext(conn, NULL, NULL);
-               int res, v = LDAP_VERSION3;
-               res = ldap_initialize(&conn, ldapserver.c_str());
-               if (res != LDAP_SUCCESS)
-               {
-                       conn = NULL;
-                       return false;
-               }
-
-               res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
-               if (res != LDAP_SUCCESS)
-               {
-                       ldap_unbind_ext(conn, NULL, NULL);
-                       conn = NULL;
-                       return false;
-               }
-               return true;
-       }
-
-       ModResult OnPreCommand(std::string& command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string& original_line)
-       {
-               if (validated && command == "OPER" && parameters.size() >= 2)
-               {
-                       if (HandleOper(user, parameters[0], parameters[1]))
-                               return MOD_RES_DENY;
-               }
-               return MOD_RES_PASSTHRU;
-       }
-
-       bool LookupOper(const std::string& opername, const std::string& opassword)
-       {
-               if (conn == NULL)
-                       if (!Connect())
-                               return false;
-
-               int res;
-               char* authpass = strdup(password.c_str());
-               // bind anonymously if no bind DN and authentication are given in the config
-               struct berval cred;
-               cred.bv_val = authpass;
-               cred.bv_len = password.length();
-
-               if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
-               {
-                       if (res == LDAP_SERVER_DOWN)
-                       {
-                               // Attempt to reconnect if the connection dropped
-                               ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
-                               Connect();
-                               res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
-                       }
-
-                       if (res != LDAP_SUCCESS)
-                       {
-                               free(authpass);
-                               ldap_unbind_ext(conn, NULL, NULL);
-                               conn = NULL;
-                               return false;
-                       }
-               }
-               free(authpass);
-
-               LDAPMessage *msg, *entry;
-               std::string what = attribute + "=" + opername;
-               if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
-               {
-                       return false;
-               }
-               if (ldap_count_entries(conn, msg) > 1)
-               {
-                       ldap_msgfree(msg);
-                       return false;
-               }
-               if ((entry = ldap_first_entry(conn, msg)) == NULL)
-               {
-                       ldap_msgfree(msg);
-                       return false;
-               }
-               authpass = strdup(opassword.c_str());
-               cred.bv_val = authpass;
-               cred.bv_len = opassword.length();
-               RAIILDAPString DN(ldap_get_dn(conn, entry));
-               if ((res = ldap_sasl_bind_s(conn, DN, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) == LDAP_SUCCESS)
-               {
-                       free(authpass);
-                       ldap_msgfree(msg);
-                       return true;
-               }
-               else
-               {
-                       free(authpass);
-                       ldap_msgfree(msg);
-                       return false;
-               }
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Adds the ability to authenticate opers via LDAP", VF_VENDOR);
-       }
-
-};
-
-MODULE_INIT(ModuleLDAPAuth)
diff --git a/src/modules/extra/m_mssql.cpp b/src/modules/extra/m_mssql.cpp
deleted file mode 100644 (file)
index 598f9aa..0000000
+++ /dev/null
@@ -1,870 +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"
-
-/* $ModDesc: MsSQL provider */
-/* $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 */
-/* $ModDep: m_sqlv2.h */
-
-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() { }
-       virtual void Run();
-       virtual 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)
-       {
-       }
-
-       ~MsSQLResult()
-       {
-       }
-
-       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++;
-       }
-
-       virtual int Rows()
-       {
-               return rows;
-       }
-
-       virtual int Cols()
-       {
-               return cols;
-       }
-
-       virtual std::string ColName(int column)
-       {
-               if (column < (int)colnames.size())
-               {
-                       return colnames[column];
-               }
-               else
-               {
-                       throw SQLbadColName();
-               }
-               return "";
-       }
-
-       virtual 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;
-       }
-
-       virtual 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);
-       }
-
-       virtual SQLfieldList& GetRow()
-       {
-               if (currentrow < rows)
-                       return fieldlists[currentrow];
-               else
-                       return emptyfieldlist;
-       }
-
-       virtual 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;
-       }
-
-       virtual SQLfieldList* GetRowPtr()
-       {
-               fieldlist = new SQLfieldList();
-
-               if (currentrow < rows)
-               {
-                       for (int i = 0; i < Rows(); i++)
-                       {
-                               fieldlist->push_back(fieldlists[currentrow][i]);
-                       }
-                       currentrow++;
-               }
-               return fieldlist;
-       }
-
-       virtual 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;
-       }
-
-       virtual void Free(SQLfieldMap* fm)
-       {
-               delete fm;
-       }
-
-       virtual 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("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
-                                       LoggingMutex->Unlock();
-                                       CloseDB();
-                               }
-                       }
-                       else
-                       {
-                               LoggingMutex->Lock();
-                               ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
-                               LoggingMutex->Unlock();
-                               CloseDB();
-                       }
-               }
-               else
-               {
-                       LoggingMutex->Lock();
-                       ServerInstance->Logs->Log("m_mssql",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("m_mssql",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("m_mssql",DEBUG,"<******> result type: %d", tds_res);
-                       //ServerInstance->Logs->Log("m_mssql",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*[MAXBUF];
-                                                       char** data = new char*[MAXBUF];
-                                                       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("m_mssql", 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("m_mssql", 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()
-       {
-               ReadConf();
-
-               ServerInstance->Threads->Start(queryDispatcher);
-
-               Implementation eventlist[] = { I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               ServerInstance->Modules->AddService(sqlserv);
-       }
-
-       virtual ~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("m_mssql",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();
-       }
-
-       virtual void OnRehash(User* user)
-       {
-               queryDispatcher->LockQueue();
-               ReadConf();
-               queryDispatcher->UnlockQueueWakeup();
-       }
-
-       void OnRequest(Request& request)
-       {
-               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;
-       }
-
-       virtual Version GetVersion()
-       {
-               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)
index 159a0b8b2318b1b336300a243da7e00c2c848cc8..4f727519fd7c535221f665221104ca424c32844d 100644 (file)
  * 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("debian") libmysqlclient-dev
+/// $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 "sql.h"
+#include "modules/sql.h"
 
 #ifdef _WIN32
 # pragma comment(lib, "libmysql.lib")
 
 /* VERSION 3 API: With nonblocking (threaded) requests */
 
-/* $ModDesc: SQL Service Provider module for all other m_sql* modules */
-/* $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
@@ -75,20 +84,20 @@ class DispatcherThread;
 
 struct QQueueItem
 {
-       SQLQuery* q;
+       SQL::Query* q;
        std::string query;
        SQLConnection* c;
-       QQueueItem(SQLQuery* Q, const std::string& S, SQLConnection* C) : q(Q), query(S), c(C) {}
+       QQueueItem(SQL::Query* Q, const std::string& S, SQLConnection* C) : q(Q), query(S), c(C) {}
 };
 
 struct RQueueItem
 {
-       SQLQuery* q;
+       SQL::Query* q;
        MySQLresult* r;
-       RQueueItem(SQLQuery* Q, MySQLresult* R) : q(Q), r(R) {}
+       RQueueItem(SQL::Query* 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;
 
@@ -103,11 +112,11 @@ class ModuleSQL : public Module
        ConnMap connections; // main thread only
 
        ModuleSQL();
-       void init();
+       void init() CXX11_OVERRIDE;
        ~ModuleSQL();
-       void OnRehash(User* user);
-       void OnUnloadModule(Module* mod);
-       Version GetVersion();
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
+       Version GetVersion() CXX11_OVERRIDE;
 };
 
 class DispatcherThread : public SocketThread
@@ -117,8 +126,8 @@ class DispatcherThread : public SocketThread
  public:
        DispatcherThread(ModuleSQL* CreatorModule) : Parent(CreatorModule) { }
        ~DispatcherThread() { }
-       virtual void Run();
-       virtual void OnNotify();
+       void Run() CXX11_OVERRIDE;
+       void OnNotify() CXX11_OVERRIDE;
 };
 
 #if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224
@@ -127,16 +136,16 @@ class DispatcherThread : public SocketThread
 
 /** Represents a mysql result set
  */
-class MySQLresult : public SQLResult
+class MySQLresult : public SQL::Result
 {
  public:
-       SQLerror err;
+       SQL::Error err;
        int currentrow;
        int rows;
        std::vector<std::string> colnames;
-       std::vector<SQLEntries> fieldlists;
+       std::vector<SQL::Row> fieldlists;
 
-       MySQLresult(MYSQL_RES* res, int affected_rows) : err(SQL_NO_ERROR), currentrow(0), rows(0)
+       MySQLresult(MYSQL_RES* res, int affected_rows) : err(SQL::SUCCESS), currentrow(0), rows(0)
        {
                if (affected_rows >= 1)
                {
@@ -165,9 +174,9 @@ class MySQLresult : public SQLResult
                                        {
                                                std::string a = (fields[field_count].name ? fields[field_count].name : "");
                                                if (row[field_count])
-                                                       fieldlists[n].push_back(SQLEntry(row[field_count]));
+                                                       fieldlists[n].push_back(SQL::Field(row[field_count]));
                                                else
-                                                       fieldlists[n].push_back(SQLEntry());
+                                                       fieldlists[n].push_back(SQL::Field());
                                                colnames.push_back(a);
                                                field_count++;
                                        }
@@ -179,35 +188,44 @@ class MySQLresult : public SQLResult
                }
        }
 
-       MySQLresult(SQLerror& e) : err(e)
+       MySQLresult(SQL::Error& e) : err(e)
        {
 
        }
 
-       ~MySQLresult()
+       int Rows() CXX11_OVERRIDE
        {
+               return rows;
        }
 
-       virtual int Rows()
+       void GetCols(std::vector<std::string>& result) CXX11_OVERRIDE
        {
-               return rows;
+               result.assign(colnames.begin(), colnames.end());
        }
 
-       virtual void GetCols(std::vector<std::string>& result)
+       bool HasColumn(const std::string& column, size_t& index) CXX11_OVERRIDE
        {
-               result.assign(colnames.begin(), colnames.end());
+               for (size_t i = 0; i < colnames.size(); ++i)
+               {
+                       if (colnames[i] == column)
+                       {
+                               index = i;
+                               return true;
+                       }
+               }
+               return false;
        }
 
-       virtual SQLEntry GetValue(int row, int column)
+       SQL::Field GetValue(int row, int column)
        {
                if ((row >= 0) && (row < rows) && (column >= 0) && (column < (int)fieldlists[row].size()))
                {
                        return fieldlists[row][column];
                }
-               return SQLEntry();
+               return SQL::Field();
        }
 
-       virtual bool GetRow(SQLEntries& result)
+       bool GetRow(SQL::Row& result) CXX11_OVERRIDE
        {
                if (currentrow < rows)
                {
@@ -225,7 +243,7 @@ class MySQLresult : public SQLResult
 
 /** Represents a connection to a mysql database
  */
-class SQLConnection : public SQLProvider
+class SQLConnection : public SQL::Provider
 {
  public:
        reference<ConfigTag> config;
@@ -233,7 +251,7 @@ class SQLConnection : public SQLProvider
        Mutex lock;
 
        // This constructor creates an SQLConnection object with the given credentials, but does not connect yet.
-       SQLConnection(Module* p, ConfigTag* tag) : SQLProvider(p, "SQL/" + tag->getString("id")),
+       SQLConnection(Module* p, ConfigTag* tag) : SQL::Provider(p, "SQL/" + tag->getString("id")),
                config(tag), connection(NULL)
        {
        }
@@ -254,10 +272,16 @@ class SQLConnection : public SQLProvider
                std::string user = config->getString("user");
                std::string pass = config->getString("pass");
                std::string dbname = config->getString("name");
-               int port = config->getInt("port");
+               unsigned int port = config->getUInt("port", 3306);
                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))
                {
@@ -286,7 +310,7 @@ class SQLConnection : public SQLProvider
                {
                        /* XXX: See /usr/include/mysql/mysqld_error.h for a list of
                         * possible error numbers and error messages */
-                       SQLerror e(SQL_QREPLY_FAIL, ConvToStr(mysql_errno(connection)) + ": " + mysql_error(connection));
+                       SQL::Error e(SQL::QREPLY_FAIL, InspIRCd::Format("%u: %s", mysql_errno(connection), mysql_error(connection)));
                        return new MySQLresult(e);
                }
        }
@@ -308,14 +332,14 @@ class SQLConnection : public SQLProvider
                mysql_close(connection);
        }
 
-       void submit(SQLQuery* q, const std::string& qs)
+       void Submit(SQL::Query* q, const std::string& qs) CXX11_OVERRIDE
        {
                Parent()->Dispatcher->LockQueue();
                Parent()->qq.push_back(QQueueItem(q, qs, this));
                Parent()->Dispatcher->UnlockQueueWakeup();
        }
 
-       void submit(SQLQuery* call, const std::string& q, const ParamL& p)
+       void Submit(SQL::Query* call, const std::string& q, const SQL::ParamList& p) CXX11_OVERRIDE
        {
                std::string res;
                unsigned int param = 0;
@@ -332,18 +356,17 @@ class SQLConnection : public SQLProvider
                                        // and one byte is the terminating null
                                        std::vector<char> buffer(parm.length() * 2 + 1);
 
-                                       // The return value of mysql_escape_string() is the length of the encoded string,
+                                       // The return value of mysql_real_escape_string() is the length of the encoded string,
                                        // not including the terminating null
-                                       unsigned long escapedsize = mysql_escape_string(&buffer[0], parm.c_str(), parm.length());
-//                                     mysql_real_escape_string(connection, queryend, paramscopy[paramnum].c_str(), paramscopy[paramnum].length());
+                                       unsigned long escapedsize = mysql_real_escape_string(connection, &buffer[0], parm.c_str(), parm.length());
                                        res.append(&buffer[0], escapedsize);
                                }
                        }
                }
-               submit(call, res);
+               Submit(call, res);
        }
 
-       void submit(SQLQuery* call, const std::string& q, const ParamM& p)
+       void Submit(SQL::Query* call, const std::string& q, const SQL::ParamMap& p) CXX11_OVERRIDE
        {
                std::string res;
                for(std::string::size_type i = 0; i < q.length(); i++)
@@ -358,7 +381,7 @@ class SQLConnection : public SQLProvider
                                        field.push_back(q[i++]);
                                i--;
 
-                               ParamM::const_iterator it = p.find(field);
+                               SQL::ParamMap::const_iterator it = p.find(field);
                                if (it != p.end())
                                {
                                        std::string parm = it->second;
@@ -369,7 +392,7 @@ class SQLConnection : public SQLProvider
                                }
                        }
                }
-               submit(call, res);
+               Submit(call, res);
        }
 };
 
@@ -381,12 +404,7 @@ ModuleSQL::ModuleSQL()
 void ModuleSQL::init()
 {
        Dispatcher = new DispatcherThread(this);
-       ServerInstance->Threads->Start(Dispatcher);
-
-       Implementation eventlist[] = { I_OnRehash, I_OnUnloadModule };
-       ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
-       OnRehash(NULL);
+       ServerInstance->Threads.Start(Dispatcher);
 }
 
 ModuleSQL::~ModuleSQL()
@@ -403,13 +421,13 @@ ModuleSQL::~ModuleSQL()
        }
 }
 
-void ModuleSQL::OnRehash(User* user)
+void ModuleSQL::ReadConfig(ConfigStatus& status)
 {
        ConnMap conns;
        ConfigTagList tags = ServerInstance->Config->ConfTags("database");
        for(ConfigIter i = tags.first; i != tags.second; i++)
        {
-               if (i->second->getString("module", "mysql") != "mysql")
+               if (!stdalgo::string::equalsci(i->second->getString("module"), "mysql"))
                        continue;
                std::string id = i->second->getString("id");
                ConnMap::iterator curr = connections.find(id);
@@ -428,7 +446,7 @@ void ModuleSQL::OnRehash(User* user)
 
        // now clean up the deleted databases
        Dispatcher->LockQueue();
-       SQLerror err(SQL_BAD_DBID);
+       SQL::Error err(SQL::BAD_DBID);
        for(ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
        {
                ServerInstance->Modules->DelService(*i->second);
@@ -455,7 +473,7 @@ void ModuleSQL::OnRehash(User* user)
 
 void ModuleSQL::OnUnloadModule(Module* mod)
 {
-       SQLerror err(SQL_BAD_DBID);
+       SQL::Error err(SQL::BAD_DBID);
        Dispatcher->LockQueue();
        unsigned int i = qq.size();
        while (i > 0)
@@ -482,7 +500,7 @@ void ModuleSQL::OnUnloadModule(Module* mod)
 
 Version ModuleSQL::GetVersion()
 {
-       return Version("MySQL support", VF_VENDOR);
+       return Version("Provides MySQL support", VF_VENDOR);
 }
 
 void DispatcherThread::Run()
@@ -535,7 +553,7 @@ void DispatcherThread::OnNotify()
        for(ResultQueue::iterator i = Parent->rq.begin(); i != Parent->rq.end(); i++)
        {
                MySQLresult* res = i->r;
-               if (res->err.id == SQL_NO_ERROR)
+               if (res->err.code == SQL::SUCCESS)
                        i->q->OnResult(*res);
                else
                        i->q->OnError(res->err);
index ac247548ac35c303a805734c483f112bb0c3f3f5..bb727b623ea504dd4d3dbd4a874fd59e61531702 100644 (file)
  * 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("debian") libpq-dev
+/// $PackageInfo: require_system("ubuntu") libpq-dev
+
 
 #include "inspircd.h"
 #include <cstdlib>
-#include <sstream>
 #include <libpq-fe.h>
-#include "sql.h"
-
-/* $ModDesc: PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API */
-/* $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 */
+#include "modules/sql.h"
 
 /* SQLConn rewritten by peavey to
  * use EventHandler instead of
@@ -43,7 +46,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
@@ -59,17 +62,17 @@ 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)
        {
        }
-       virtual void Tick(time_t TIME);
+       bool Tick(time_t TIME) CXX11_OVERRIDE;
 };
 
 struct QueueItem
 {
-       SQLQuery* c;
+       SQL::Query* c;
        std::string q;
-       QueueItem(SQLQuery* C, const std::string& Q) : c(C), q(Q) {}
+       QueueItem(SQL::Query* C, const std::string& Q) : c(C), q(Q) {}
 };
 
 /** PgSQLresult is a subclass of the mostly-pure-virtual class SQLresult.
@@ -79,11 +82,21 @@ struct QueueItem
  * data is passes to the module nearly as directly as if it was using the API directly itself.
  */
 
-class PgSQLresult : public SQLResult
+class PgSQLresult : public SQL::Result
 {
        PGresult* res;
        int currentrow;
        int rows;
+       std::vector<std::string> colnames;
+
+       void getColNames()
+       {
+               colnames.resize(PQnfields(res));
+               for(unsigned int i=0; i < colnames.size(); i++)
+               {
+                       colnames[i] = PQfname(res, i);
+               }
+       }
  public:
        PgSQLresult(PGresult* result) : res(result), currentrow(0)
        {
@@ -97,30 +110,44 @@ class PgSQLresult : public SQLResult
                PQclear(res);
        }
 
-       virtual int Rows()
+       int Rows() CXX11_OVERRIDE
        {
                return rows;
        }
 
-       virtual void GetCols(std::vector<std::string>& result)
+       void GetCols(std::vector<std::string>& result) CXX11_OVERRIDE
+       {
+               if (colnames.empty())
+                       getColNames();
+               result = colnames;
+       }
+
+       bool HasColumn(const std::string& column, size_t& index) CXX11_OVERRIDE
        {
-               result.resize(PQnfields(res));
-               for(unsigned int i=0; i < result.size(); i++)
+               if (colnames.empty())
+                       getColNames();
+
+               for (size_t i = 0; i < colnames.size(); ++i)
                {
-                       result[i] = PQfname(res, i);
+                       if (colnames[i] == column)
+                       {
+                               index = i;
+                               return true;
+                       }
                }
+               return false;
        }
 
-       virtual SQLEntry GetValue(int row, int column)
+       SQL::Field GetValue(int row, int column)
        {
                char* v = PQgetvalue(res, row, column);
                if (!v || PQgetisnull(res, row, column))
-                       return SQLEntry();
+                       return SQL::Field();
 
-               return SQLEntry(std::string(v, PQgetlength(res, row, column)));
+               return SQL::Field(std::string(v, PQgetlength(res, row, column)));
        }
 
-       virtual bool GetRow(SQLEntries& result)
+       bool GetRow(SQL::Row& result) CXX11_OVERRIDE
        {
                if (currentrow >= PQntuples(res))
                        return false;
@@ -138,7 +165,7 @@ class PgSQLresult : public SQLResult
 
 /** SQLConn represents one SQL session.
  */
-class SQLConn : public SQLProvider, public EventHandler
+class SQLConn : public SQL::Provider, public EventHandler
 {
  public:
        reference<ConfigTag> conf;      /* The <database> entry */
@@ -148,25 +175,25 @@ class SQLConn : public SQLProvider, public EventHandler
        QueueItem               qinprog;        /* If there is currently a query in progress */
 
        SQLConn(Module* Creator, ConfigTag* tag)
-       : SQLProvider(Creator, "SQL/" + tag->getString("id")), conf(tag), sql(NULL), status(CWRITE), qinprog(NULL, "")
+       : SQL::Provider(Creator, "SQL/" + tag->getString("id")), conf(tag), sql(NULL), status(CWRITE), qinprog(NULL, "")
        {
                if (!DoConnect())
                {
-                       ServerInstance->Logs->Log("m_pgsql",DEFAULT, "WARNING: Could not connect to database " + tag->getString("id"));
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not connect to database " + tag->getString("id"));
                        DelayReconnect();
                }
        }
 
-       CullResult cull()
+       CullResult cull() CXX11_OVERRIDE
        {
-               this->SQLProvider::cull();
+               this->SQL::Provider::cull();
                ServerInstance->Modules->DelService(*this);
                return this->EventHandler::cull();
        }
 
        ~SQLConn()
        {
-               SQLerror err(SQL_BAD_DBID);
+               SQL::Error err(SQL::BAD_DBID);
                if (qinprog.c)
                {
                        qinprog.c->OnError(err);
@@ -174,24 +201,25 @@ class SQLConn : public SQLProvider, public EventHandler
                }
                for(std::deque<QueueItem>::iterator i = queue.begin(); i != queue.end(); i++)
                {
-                       SQLQuery* q = i->c;
+                       SQL::Query* q = i->c;
                        q->OnError(err);
                        delete q;
                }
        }
 
-       virtual 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()
@@ -242,9 +270,9 @@ class SQLConn : public SQLProvider, public EventHandler
                if(this->fd <= -1)
                        return false;
 
-               if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_WRITE | FD_WANT_NO_READ))
+               if (!SocketEngine::AddFd(this, FD_WANT_NO_WRITE | FD_WANT_NO_READ))
                {
-                       ServerInstance->Logs->Log("m_pgsql",DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
                        return false;
                }
 
@@ -257,17 +285,17 @@ class SQLConn : public SQLProvider, public EventHandler
                switch(PQconnectPoll(sql))
                {
                        case PGRES_POLLING_WRITING:
-                               ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
+                               SocketEngine::ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
                                status = CWRITE;
                                return true;
                        case PGRES_POLLING_READING:
-                               ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+                               SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
                                status = CREAD;
                                return true;
                        case PGRES_POLLING_FAILED:
                                return false;
                        case PGRES_POLLING_OK:
-                               ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+                               SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
                                status = WWRITE;
                                DoConnectedPoll();
                        default:
@@ -316,7 +344,7 @@ restart:
                                        case PGRES_BAD_RESPONSE:
                                        case PGRES_FATAL_ERROR:
                                        {
-                                               SQLerror err(SQL_QREPLY_FAIL, PQresultErrorMessage(result));
+                                               SQL::Error err(SQL::QREPLY_FAIL, PQresultErrorMessage(result));
                                                qinprog.c->OnError(err);
                                                break;
                                        }
@@ -350,17 +378,17 @@ restart:
                switch(PQresetPoll(sql))
                {
                        case PGRES_POLLING_WRITING:
-                               ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
+                               SocketEngine::ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
                                status = CWRITE;
                                return DoPoll();
                        case PGRES_POLLING_READING:
-                               ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+                               SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
                                status = CREAD;
                                return true;
                        case PGRES_POLLING_FAILED:
                                return false;
                        case PGRES_POLLING_OK:
-                               ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+                               SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
                                status = WWRITE;
                                DoConnectedPoll();
                        default:
@@ -386,7 +414,7 @@ restart:
                }
        }
 
-       void submit(SQLQuery *req, const std::string& q)
+       void Submit(SQL::Query *req, const std::string& q) CXX11_OVERRIDE
        {
                if (qinprog.q.empty())
                {
@@ -399,7 +427,7 @@ restart:
                }
        }
 
-       void submit(SQLQuery *req, const std::string& q, const ParamL& p)
+       void Submit(SQL::Query *req, const std::string& q, const SQL::ParamList& p) CXX11_OVERRIDE
        {
                std::string res;
                unsigned int param = 0;
@@ -413,22 +441,18 @@ 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("m_pgsql", DEBUG, "BUG: Apparently PQescapeStringConn() failed");
-#else
-                                       size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
-#endif
+                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
                                        res.append(&buffer[0], escapedsize);
                                }
                        }
                }
-               submit(req, res);
+               Submit(req, res);
        }
 
-       void submit(SQLQuery *req, const std::string& q, const ParamM& p)
+       void Submit(SQL::Query *req, const std::string& q, const SQL::ParamMap& p) CXX11_OVERRIDE
        {
                std::string res;
                for(std::string::size_type i = 0; i < q.length(); i++)
@@ -443,24 +467,20 @@ restart:
                                        field.push_back(q[i++]);
                                i--;
 
-                               ParamM::const_iterator it = p.find(field);
+                               SQL::ParamMap::const_iterator it = p.find(field);
                                if (it != p.end())
                                {
                                        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("m_pgsql", DEBUG, "BUG: Apparently PQescapeStringConn() failed");
-#else
-                                       size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
-#endif
+                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
                                        res.append(&buffer[0], escapedsize);
                                }
                        }
                }
-               submit(req, res);
+               Submit(req, res);
        }
 
        void DoQuery(const QueueItem& req)
@@ -468,7 +488,7 @@ restart:
                if (status != WREAD && status != WWRITE)
                {
                        // whoops, not connected...
-                       SQLerror err(SQL_BAD_CONN);
+                       SQL::Error err(SQL::BAD_CONN);
                        req.c->OnError(err);
                        delete req.c;
                        return;
@@ -480,7 +500,7 @@ restart:
                }
                else
                {
-                       SQLerror err(SQL_QSEND_FAIL, PQerrorMessage(sql));
+                       SQL::Error err(SQL::QSEND_FAIL, PQerrorMessage(sql));
                        req.c->OnError(err);
                        delete req.c;
                }
@@ -488,7 +508,7 @@ restart:
 
        void Close()
        {
-               ServerInstance->SE->DelFd(this);
+               SocketEngine::DelFd(this);
 
                if(sql)
                {
@@ -505,25 +525,17 @@ class ModulePgSQL : public Module
        ReconnectTimer* retimer;
 
        ModulePgSQL()
+               : retimer(NULL)
        {
        }
 
-       void init()
-       {
-               ReadConf();
-
-               Implementation eventlist[] = { I_OnUnloadModule, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModulePgSQL()
+       ~ModulePgSQL()
        {
-               if (retimer)
-                       ServerInstance->Timers->DelTimer(retimer);
+               delete retimer;
                ClearAllConnections();
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ReadConf();
        }
@@ -534,7 +546,7 @@ class ModulePgSQL : public Module
                ConfigTagList tags = ServerInstance->Config->ConfTags("database");
                for(ConfigIter i = tags.first; i != tags.second; i++)
                {
-                       if (i->second->getString("module", "pgsql") != "pgsql")
+                       if (!stdalgo::string::equalsci(i->second->getString("module"), "pgsql"))
                                continue;
                        std::string id = i->second->getString("id");
                        ConnMap::iterator curr = connections.find(id);
@@ -564,9 +576,9 @@ class ModulePgSQL : public Module
                connections.clear();
        }
 
-       void OnUnloadModule(Module* mod)
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE
        {
-               SQLerror err(SQL_BAD_DBID);
+               SQL::Error err(SQL::BAD_DBID);
                for(ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
                {
                        SQLConn* conn = i->second;
@@ -579,7 +591,7 @@ class ModulePgSQL : public Module
                        std::deque<QueueItem>::iterator j = conn->queue.begin();
                        while (j != conn->queue.end())
                        {
-                               SQLQuery* q = j->c;
+                               SQL::Query* q = j->c;
                                if (q->creator == mod)
                                {
                                        q->OnError(err);
@@ -592,16 +604,18 @@ class ModulePgSQL : public Module
                }
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API", VF_VENDOR);
        }
 };
 
-void ReconnectTimer::Tick(time_t time)
+bool ReconnectTimer::Tick(time_t time)
 {
        mod->retimer = NULL;
        mod->ReadConf();
+       delete this;
+       return false;
 }
 
 void SQLConn::DelayReconnect()
@@ -615,7 +629,7 @@ void SQLConn::DelayReconnect()
                if (!mod->retimer)
                {
                        mod->retimer = new ReconnectTimer(mod);
-                       ServerInstance->Timers->AddTimer(mod->retimer);
+                       ServerInstance->Timers.AddTimer(mod->retimer);
                }
        }
 }
index cba234c8cae74977c9930bbfde1a462ea63649f0..e8ef96c2266bba7f4fd770dd36bc2a239662dc16 100644 (file)
  * 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("debian") libpcre3-dev pkg-config
+/// $PackageInfo: require_system("ubuntu") libpcre3-dev pkg-config
+
 
 #include "inspircd.h"
 #include <pcre.h>
-#include "m_regex.h"
-
-/* $ModDesc: Regex Provider Module for PCRE */
-/* $ModDep: m_regex.h */
-/* $CompileFlags: exec("pcre-config --cflags") */
-/* $LinkerFlags: exec("pcre-config --libs") rpath("pcre-config --libs") -lpcre */
+#include "modules/regex.h"
 
 #ifdef _WIN32
 # pragma comment(lib, "libpcre.lib")
 #endif
 
-class PCREException : public ModuleException
-{
-public:
-       PCREException(const std::string& rx, const std::string& error, int erroffset)
-               : ModuleException("Error in regex " + rx + " at offset " + ConvToStr(erroffset) + ": " + error)
-       {
-       }
-};
-
 class PCRERegex : public Regex
 {
-private:
        pcre* regex;
 
-public:
+ public:
        PCRERegex(const std::string& rx) : Regex(rx)
        {
                const char* error;
@@ -53,24 +46,19 @@ public:
                regex = pcre_compile(rx.c_str(), 0, &error, &erroffset, NULL);
                if (!regex)
                {
-                       ServerInstance->Logs->Log("REGEX", DEBUG, "pcre_compile failed: /%s/ [%d] %s", rx.c_str(), erroffset, error);
-                       throw PCREException(rx, error, erroffset);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "pcre_compile failed: /%s/ [%d] %s", rx.c_str(), erroffset, error);
+                       throw RegexException(rx, error, erroffset);
                }
        }
 
-       virtual ~PCRERegex()
+       ~PCRERegex()
        {
                pcre_free(regex);
        }
 
-       virtual bool Matches(const std::string& text)
+       bool Matches(const std::string& text) CXX11_OVERRIDE
        {
-               if (pcre_exec(regex, NULL, text.c_str(), text.length(), 0, 0, NULL, 0) > -1)
-               {
-                       // Bang. :D
-                       return true;
-               }
-               return false;
+               return (pcre_exec(regex, NULL, text.c_str(), text.length(), 0, 0, NULL, 0) >= 0);
        }
 };
 
@@ -78,7 +66,7 @@ class PCREFactory : public RegexFactory
 {
  public:
        PCREFactory(Module* m) : RegexFactory(m, "regex/pcre") {}
-       Regex* Create(const std::string& expr)
+       Regex* Create(const std::string& expr) CXX11_OVERRIDE
        {
                return new PCRERegex(expr);
        }
@@ -86,13 +74,13 @@ class PCREFactory : public RegexFactory
 
 class ModuleRegexPCRE : public Module
 {
-public:
+ public:
        PCREFactory ref;
-       ModuleRegexPCRE() : ref(this) {
-               ServerInstance->Modules->AddService(ref);
+       ModuleRegexPCRE() : ref(this)
+       {
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Regex Provider Module for PCRE", VF_VENDOR);
        }
index b3afd60c80b8c002ebdaa21eae3069690a775ab2..b5fddfab8f2c0e748aa35cb22fcc861bc2eeb90f 100644 (file)
 
 
 #include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
 #include <sys/types.h>
 #include <regex.h>
 
-/* $ModDesc: Regex Provider Module for POSIX Regular Expressions */
-/* $ModDep: m_regex.h */
-
-class POSIXRegexException : public ModuleException
-{
-public:
-       POSIXRegexException(const std::string& rx, const std::string& error)
-               : ModuleException("Error in regex " + rx + ": " + error)
-       {
-       }
-};
-
 class POSIXRegex : public Regex
 {
-private:
        regex_t regbuf;
 
-public:
+ public:
        POSIXRegex(const std::string& rx, bool extended) : Regex(rx)
        {
                int flags = (extended ? REG_EXTENDED : 0) | REG_NOSUB;
@@ -58,23 +45,18 @@ public:
                        error = errbuf;
                        delete[] errbuf;
                        regfree(&regbuf);
-                       throw POSIXRegexException(rx, error);
+                       throw RegexException(rx, error);
                }
        }
 
-       virtual ~POSIXRegex()
+       ~POSIXRegex()
        {
                regfree(&regbuf);
        }
 
-       virtual bool Matches(const std::string& text)
+       bool Matches(const std::string& text) CXX11_OVERRIDE
        {
-               if (regexec(&regbuf, text.c_str(), 0, NULL, 0) == 0)
-               {
-                       // Bang. :D
-                       return true;
-               }
-               return false;
+               return (regexec(&regbuf, text.c_str(), 0, NULL, 0) == 0);
        }
 };
 
@@ -83,7 +65,7 @@ class PosixFactory : public RegexFactory
  public:
        bool extended;
        PosixFactory(Module* m) : RegexFactory(m, "regex/posix") {}
-       Regex* Create(const std::string& expr)
+       Regex* Create(const std::string& expr) CXX11_OVERRIDE
        {
                return new POSIXRegex(expr, extended);
        }
@@ -92,20 +74,18 @@ class PosixFactory : public RegexFactory
 class ModuleRegexPOSIX : public Module
 {
        PosixFactory ref;
-public:
-       ModuleRegexPOSIX() : ref(this) {
-               ServerInstance->Modules->AddService(ref);
-               Implementation eventlist[] = { I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
+
+ public:
+       ModuleRegexPOSIX() : ref(this)
+       {
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Regex Provider Module for POSIX Regular Expressions", VF_VENDOR);
        }
 
-       void OnRehash(User* u)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ref.extended = ServerInstance->Config->ConfValue("posix")->getBool("extended");
        }
diff --git a/src/modules/extra/m_regex_re2.cpp b/src/modules/extra/m_regex_re2.cpp
new file mode 100644 (file)
index 0000000..4bcf287
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+ *   Copyright (C) 2012 ChrisTX <chris@rev-crew.info>
+ *
+ * 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/>.
+ */
+
+/// $CompilerFlags: find_compiler_flags("re2" "")
+/// $LinkerFlags: find_linker_flags("re2" "-lre2")
+
+/// $PackageInfo: require_system("darwin") pkg-config re2
+/// $PackageInfo: require_system("debian" "8.0") libre2-dev pkg-config
+/// $PackageInfo: require_system("ubuntu" "15.10") libre2-dev pkg-config
+
+
+#include "inspircd.h"
+#include "modules/regex.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
+
+#include <re2/re2.h>
+
+class RE2Regex : public Regex
+{
+       RE2 regexcl;
+
+ public:
+       RE2Regex(const std::string& rx) : Regex(rx), regexcl(rx, RE2::Quiet)
+       {
+               if (!regexcl.ok())
+               {
+                       throw RegexException(rx, regexcl.error());
+               }
+       }
+
+       bool Matches(const std::string& text) CXX11_OVERRIDE
+       {
+               return RE2::FullMatch(text, regexcl);
+       }
+};
+
+class RE2Factory : public RegexFactory
+{
+ public:
+       RE2Factory(Module* m) : RegexFactory(m, "regex/re2") { }
+       Regex* Create(const std::string& expr) CXX11_OVERRIDE
+       {
+               return new RE2Regex(expr);
+       }
+};
+
+class ModuleRegexRE2 : public Module
+{
+       RE2Factory ref;
+
+ public:
+       ModuleRegexRE2() : ref(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Regex Provider Module for RE2", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleRegexRE2)
index 204728b652601290c3b64e25e913dd8da721aea6..14796c22f861469f86eab438a75c409292904a9e 100644 (file)
  * 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 "m_regex.h"
-#include <regex>
 
-/* $ModDesc: Regex Provider Module for std::regex Regular Expressions */
-/* $ModConfig: <stdregex type="ecmascript">
- *  Specify the Regular Expression engine to use here. Valid settings are
- *  bre, ere, awk, grep, egrep, ecmascript (default if not specified)*/
-/* $CompileFlags: -std=c++11 */
-/* $ModDep: m_regex.h */
+/// $CompilerFlags: -std=c++11
 
-class StdRegexException : public ModuleException
-{
-public:
-       StdRegexException(const std::string& rx, const std::string& error)
-               : ModuleException(std::string("Error in regex ") + rx + ": " + error)
-       {
-       }
-};
+
+#include "inspircd.h"
+#include "modules/regex.h"
+#include <regex>
 
 class StdRegex : public Regex
 {
-private:
        std::regex regexcl;
-public:
+
+ public:
        StdRegex(const std::string& rx, std::regex::flag_type fltype) : Regex(rx)
        {
                try{
@@ -48,11 +35,11 @@ public:
                }
                catch(std::regex_error rxerr)
                {
-                       throw StdRegexException(rx, rxerr.what());
+                       throw RegexException(rx, rxerr.what());
                }
        }
-       
-       virtual bool Matches(const std::string& text)
+
+       bool Matches(const std::string& text) CXX11_OVERRIDE
        {
                return std::regex_search(text, regexcl);
        }
@@ -63,7 +50,7 @@ class StdRegexFactory : public RegexFactory
  public:
        std::regex::flag_type regextype;
        StdRegexFactory(Module* m) : RegexFactory(m, "regex/stdregex") {}
-       Regex* Create(const std::string& expr)
+       Regex* Create(const std::string& expr) CXX11_OVERRIDE
        {
                return new StdRegex(expr, regextype);
        }
@@ -73,37 +60,34 @@ class ModuleRegexStd : public Module
 {
 public:
        StdRegexFactory ref;
-       ModuleRegexStd() : ref(this) {
-               ServerInstance->Modules->AddService(ref);
-               Implementation eventlist[] = { I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
+       ModuleRegexStd() : ref(this)
+       {
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Regex Provider Module for std::regex", VF_VENDOR);
        }
-       
-       void OnRehash(User* u)
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* Conf = ServerInstance->Config->ConfValue("stdregex");
                std::string regextype = Conf->getString("type", "ecmascript");
-               
-               if(regextype == "bre")
+
+               if (stdalgo::string::equalsci(regextype, "bre"))
                        ref.regextype = std::regex::basic;
-               else if(regextype == "ere")
+               else if (stdalgo::string::equalsci(regextype, "ere"))
                        ref.regextype = std::regex::extended;
-               else if(regextype == "awk")
+               else if (stdalgo::string::equalsci(regextype, "awk"))
                        ref.regextype = std::regex::awk;
-               else if(regextype == "grep")
+               else if (stdalgo::string::equalsci(regextype, "grep"))
                        ref.regextype = std::regex::grep;
-               else if(regextype == "egrep")
+               else if (stdalgo::string::equalsci(regextype, "egrep"))
                        ref.regextype = std::regex::egrep;
                else
                {
-                       if(regextype != "ecmascript")
-                               ServerInstance->SNO->WriteToSnoMask('a', "WARNING: Non-existent regex engine '%s' specified. Falling back to ECMAScript.", regextype.c_str());
+                       if (!stdalgo::string::equalsci(regextype, "ecmascript"))
+                               ServerInstance->SNO->WriteToSnoMask('a', "WARNING: Nonexistent regex engine '%s' specified. Falling back to ECMAScript.", regextype.c_str());
                        ref.regextype = std::regex::ECMAScript;
                }
        }
index 4b9eab472266c0dece1e10e683979981bdd9865a..aa3f1d41e4cdc432cb96f3a808f050644f079e7d 100644 (file)
  * 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("debian") libtre-dev pkg-config
+/// $PackageInfo: require_system("ubuntu") libtre-dev pkg-config
 
 #include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
 #include <sys/types.h>
 #include <tre/regex.h>
 
-/* $ModDesc: Regex Provider Module for TRE Regular Expressions */
-/* $CompileFlags: pkgconfincludes("tre","tre/regex.h","") */
-/* $LinkerFlags: pkgconflibs("tre","/libtre.so","-ltre") rpath("pkg-config --libs tre") */
-/* $ModDep: m_regex.h */
-
-class TRERegexException : public ModuleException
-{
-public:
-       TRERegexException(const std::string& rx, const std::string& error)
-               : ModuleException("Error in regex " + rx + ": " + error)
-       {
-       }
-};
-
 class TRERegex : public Regex
 {
-private:
        regex_t regbuf;
 
 public:
@@ -60,30 +51,26 @@ public:
                        error = errbuf;
                        delete[] errbuf;
                        regfree(&regbuf);
-                       throw TRERegexException(rx, error);
+                       throw RegexException(rx, error);
                }
        }
 
-       virtual ~TRERegex()
+       ~TRERegex()
        {
                regfree(&regbuf);
        }
 
-       virtual bool Matches(const std::string& text)
+       bool Matches(const std::string& text)  CXX11_OVERRIDE
        {
-               if (regexec(&regbuf, text.c_str(), 0, NULL, 0) == 0)
-               {
-                       // Bang. :D
-                       return true;
-               }
-               return false;
+               return (regexec(&regbuf, text.c_str(), 0, NULL, 0) == 0);
        }
 };
 
-class TREFactory : public RegexFactory {
+class TREFactory : public RegexFactory
+{
  public:
        TREFactory(Module* m) : RegexFactory(m, "regex/tre") {}
-       Regex* Create(const std::string& expr)
+       Regex* Create(const std::string& expr) CXX11_OVERRIDE
        {
                return new TRERegex(expr);
        }
@@ -92,18 +79,15 @@ class TREFactory : public RegexFactory {
 class ModuleRegexTRE : public Module
 {
        TREFactory trf;
-public:
-       ModuleRegexTRE() : trf(this) {
-               ServerInstance->Modules->AddService(trf);
-       }
 
-       Version GetVersion()
+ public:
+       ModuleRegexTRE() : trf(this)
        {
-               return Version("Regex Provider Module for TRE Regular Expressions", VF_VENDOR);
        }
 
-       ~ModuleRegexTRE()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Regex Provider Module for TRE Regular Expressions", VF_VENDOR);
        }
 };
 
index 47880c02c51ef83a5585e886eabe11e94bda1b39..e81e990252c7e9b115f1865c6b3fe038306e1bec 100644 (file)
  * 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("debian") libsqlite3-dev pkg-config
+/// $PackageInfo: require_system("ubuntu") libsqlite3-dev pkg-config
 
 #include "inspircd.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>
-#include "sql.h"
 
 #ifdef _WIN32
 # pragma comment(lib, "sqlite3.lib")
 #endif
 
-/* $ModDesc: sqlite3 provider */
-/* $CompileFlags: pkgconfversion("sqlite3","3.3") pkgconfincludes("sqlite3","/sqlite3.h","") */
-/* $LinkerFlags: pkgconflibs("sqlite3","/libsqlite3.so","-lsqlite3") */
-/* $NoPedantic */
-
 class SQLConn;
-typedef std::map<std::string, SQLConn*> ConnMap;
+typedef insp::flat_map<std::string, SQLConn*> ConnMap;
 
-class SQLite3Result : public SQLResult
+class SQLite3Result : public SQL::Result
 {
  public:
        int currentrow;
        int rows;
        std::vector<std::string> columns;
-       std::vector<SQLEntries> fieldlists;
+       std::vector<SQL::Row> fieldlists;
 
        SQLite3Result() : currentrow(0), rows(0)
        {
        }
 
-       ~SQLite3Result()
-       {
-       }
-
-       virtual int Rows()
+       int Rows() CXX11_OVERRIDE
        {
                return rows;
        }
 
-       virtual bool GetRow(SQLEntries& result)
+       bool GetRow(SQL::Row& result) CXX11_OVERRIDE
        {
                if (currentrow < rows)
                {
@@ -72,20 +78,32 @@ class SQLite3Result : public SQLResult
                }
        }
 
-       virtual void GetCols(std::vector<std::string>& result)
+       void GetCols(std::vector<std::string>& result) CXX11_OVERRIDE
        {
                result.assign(columns.begin(), columns.end());
        }
+
+       bool HasColumn(const std::string& column, size_t& index) CXX11_OVERRIDE
+       {
+               for (size_t i = 0; i < columns.size(); ++i)
+               {
+                       if (columns[i] == column)
+                       {
+                               index = i;
+                               return true;
+                       }
+               }
+               return false;
+       }
 };
 
-class SQLConn : public SQLProvider
+class SQLConn : public SQL::Provider
 {
- private:
        sqlite3* conn;
        reference<ConfigTag> config;
 
  public:
-       SQLConn(Module* Parent, ConfigTag* tag) : SQLProvider(Parent, "SQL/" + tag->getString("id")), config(tag)
+       SQLConn(Module* Parent, ConfigTag* tag) : SQL::Provider(Parent, "SQL/" + tag->getString("id")), config(tag)
        {
                std::string host = tag->getString("hostname");
                if (sqlite3_open_v2(host.c_str(), &conn, SQLITE_OPEN_READWRITE, 0) != SQLITE_OK)
@@ -93,7 +111,7 @@ class SQLConn : public SQLProvider
                        // Even in case of an error conn must be closed
                        sqlite3_close(conn);
                        conn = NULL;
-                       ServerInstance->Logs->Log("m_sqlite3",DEFAULT, "WARNING: Could not open DB with id: " + tag->getString("id"));
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not open DB with id: " + tag->getString("id"));
                }
        }
 
@@ -106,14 +124,14 @@ class SQLConn : public SQLProvider
                }
        }
 
-       void Query(SQLQuery* query, const std::string& q)
+       void Query(SQL::Query* query, const std::string& q)
        {
                SQLite3Result res;
                sqlite3_stmt *stmt;
                int err = sqlite3_prepare_v2(conn, q.c_str(), q.length(), &stmt, NULL);
                if (err != SQLITE_OK)
                {
-                       SQLerror error(SQL_QSEND_FAIL, sqlite3_errmsg(conn));
+                       SQL::Error error(SQL::QSEND_FAIL, sqlite3_errmsg(conn));
                        query->OnError(error);
                        return;
                }
@@ -135,7 +153,7 @@ class SQLConn : public SQLProvider
                                {
                                        const char* txt = (const char*)sqlite3_column_text(stmt, i);
                                        if (txt)
-                                               res.fieldlists[res.rows][i] = SQLEntry(txt);
+                                               res.fieldlists[res.rows][i] = SQL::Field(txt);
                                }
                                res.rows++;
                        }
@@ -146,7 +164,7 @@ class SQLConn : public SQLProvider
                        }
                        else
                        {
-                               SQLerror error(SQL_QREPLY_FAIL, sqlite3_errmsg(conn));
+                               SQL::Error error(SQL::QREPLY_FAIL, sqlite3_errmsg(conn));
                                query->OnError(error);
                                break;
                        }
@@ -154,13 +172,13 @@ class SQLConn : public SQLProvider
                sqlite3_finalize(stmt);
        }
 
-       virtual void submit(SQLQuery* query, const std::string& q)
+       void Submit(SQL::Query* query, const std::string& q) CXX11_OVERRIDE
        {
                Query(query, q);
                delete query;
        }
 
-       virtual void submit(SQLQuery* query, const std::string& q, const ParamL& p)
+       void Submit(SQL::Query* query, const std::string& q, const SQL::ParamList& p) CXX11_OVERRIDE
        {
                std::string res;
                unsigned int param = 0;
@@ -178,10 +196,10 @@ class SQLConn : public SQLProvider
                                }
                        }
                }
-               submit(query, res);
+               Submit(query, res);
        }
 
-       virtual void submit(SQLQuery* query, const std::string& q, const ParamM& p)
+       void Submit(SQL::Query* query, const std::string& q, const SQL::ParamMap& p) CXX11_OVERRIDE
        {
                std::string res;
                for(std::string::size_type i = 0; i < q.length(); i++)
@@ -196,7 +214,7 @@ class SQLConn : public SQLProvider
                                        field.push_back(q[i++]);
                                i--;
 
-                               ParamM::const_iterator it = p.find(field);
+                               SQL::ParamMap::const_iterator it = p.find(field);
                                if (it != p.end())
                                {
                                        char* escaped = sqlite3_mprintf("%q", it->second.c_str());
@@ -205,29 +223,16 @@ class SQLConn : public SQLProvider
                                }
                        }
                }
-               submit(query, res);
+               Submit(query, res);
        }
 };
 
 class ModuleSQLite3 : public Module
 {
- private:
        ConnMap conns;
 
  public:
-       ModuleSQLite3()
-       {
-       }
-
-       void init()
-       {
-               ReadConf();
-
-               Implementation eventlist[] = { I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModuleSQLite3()
+       ~ModuleSQLite3()
        {
                ClearConns();
        }
@@ -243,13 +248,13 @@ class ModuleSQLite3 : public Module
                conns.clear();
        }
 
-       void ReadConf()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ClearConns();
                ConfigTagList tags = ServerInstance->Config->ConfTags("database");
                for(ConfigIter i = tags.first; i != tags.second; i++)
                {
-                       if (i->second->getString("module", "sqlite") != "sqlite")
+                       if (!stdalgo::string::equalsci(i->second->getString("module"), "sqlite"))
                                continue;
                        SQLConn* conn = new SQLConn(this, i->second);
                        conns.insert(std::make_pair(i->second->getString("id"), conn));
@@ -257,14 +262,9 @@ class ModuleSQLite3 : public Module
                }
        }
 
-       void OnRehash(User* user)
-       {
-               ReadConf();
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("sqlite3 provider", VF_VENDOR);
+               return Version("Provides SQLite3 support", VF_VENDOR);
        }
 };
 
index 2f4acf3f05edbb14454642940bf70fa37ae2759a..ce1dbaeaf71feefe43d53365ae1c1c923246c0a5 100644 (file)
  * 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")
 
-#include "inspircd.h"
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-#include "ssl.h"
-#include "m_cap.h"
+/// $LinkerFlags: find_linker_flags("gnutls" "-lgnutls")
+/// $LinkerFlags: require_version("gnutls" "1.0" "2.12") execute("libgcrypt-config --libs" "LIBGCRYPT_LDFLAGS")
 
-#ifdef _WIN32
-# pragma comment(lib, "libgnutls-30.lib")
+/// $PackageInfo: require_system("centos") gnutls-devel pkgconfig
+/// $PackageInfo: require_system("darwin") gnutls pkg-config
+/// $PackageInfo: require_system("debian" "1.0" "7.99") libgcrypt11-dev
+/// $PackageInfo: require_system("debian") gnutls-bin libgnutls28-dev pkg-config
+/// $PackageInfo: require_system("ubuntu" "1.0" "13.10") libgcrypt11-dev
+/// $PackageInfo: require_system("ubuntu") gnutls-bin libgnutls-dev pkg-config
+
+#include "inspircd.h"
+#include "modules/ssl.h"
+#include <memory>
+
+// 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__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))
+#  pragma GCC diagnostic ignored "-Wpedantic"
+# else
+#  pragma GCC diagnostic ignored "-pedantic"
+# endif
 #endif
 
-/* $ModDesc: Provides SSL support for clients */
-/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") iflt("pkg-config --modversion gnutls","2.12") exec("libgcrypt-config --cflags") */
-/* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") iflt("pkg-config --modversion gnutls","2.12") exec("libgcrypt-config --libs") */
-/* $NoPedantic */
+// Fix warnings about using std::auto_ptr on C++11 or newer.
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 
-#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
-#endif
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
 
-// 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))
-#define GNUTLS_NEW_PRIO_API
+#ifndef GNUTLS_VERSION_NUMBER
+#define GNUTLS_VERSION_NUMBER LIBGNUTLS_VERSION_NUMBER
+#define GNUTLS_VERSION LIBGNUTLS_VERSION
 #endif
 
-#if(GNUTLS_VERSION_MAJOR < 2)
-typedef gnutls_certificate_credentials_t gnutls_certificate_credentials;
-typedef gnutls_dh_params_t gnutls_dh_params;
+// 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
-# include <gnutls/crypto.h>
 #else
 # include <gcrypt.h>
 #endif
 
-enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED };
-
-struct SSLConfig : public refcountbase
-{
-       gnutls_certificate_credentials_t x509_cred;
-       std::vector<gnutls_x509_crt_t> x509_certs;
-       gnutls_x509_privkey_t x509_key;
-       gnutls_dh_params_t dh_params;
-#ifdef GNUTLS_NEW_PRIO_API
-       gnutls_priority_t priority;
+#ifdef _WIN32
+# pragma comment(lib, "libgnutls-30.lib")
 #endif
 
-       SSLConfig()
-               : x509_cred(NULL)
-               , x509_key(NULL)
-               , dh_params(NULL)
-#ifdef GNUTLS_NEW_PRIO_API
-               , priority(NULL)
+// These don't exist in older GnuTLS versions
+#if INSPIRCD_GNUTLS_HAS_VERSION(2, 1, 7)
+#define GNUTLS_NEW_PRIO_API
 #endif
-       {
-       }
-
-       ~SSLConfig()
-       {
-               ServerInstance->Logs->Log("m_ssl_gnutls", DEBUG, "Destroying SSLConfig %p", (void*)this);
-
-               if (x509_cred)
-                       gnutls_certificate_free_credentials(x509_cred);
-
-               for (unsigned int i = 0; i < x509_certs.size(); i++)
-                       gnutls_x509_crt_deinit(x509_certs[i]);
-
-               if (x509_key)
-                       gnutls_x509_privkey_deinit(x509_key);
 
-               if (dh_params)
-                       gnutls_dh_params_deinit(dh_params);
+enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_HANDSHAKEN };
 
-#ifdef GNUTLS_NEW_PRIO_API
-               if (priority)
-                       gnutls_priority_deinit(priority);
+#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
-       }
-};
-
-static reference<SSLConfig> currconf;
-
-static SSLConfig* GetSessionConfig(gnutls_session_t session);
 
-#if(GNUTLS_VERSION_MAJOR < 2 || ( GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12 ) )
-static int cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs,
-       const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) {
+#if INSPIRCD_GNUTLS_HAS_VERSION(3, 3, 5)
+#define INSPIRCD_GNUTLS_HAS_RECV_PACKET
+#endif
 
-       st->type = GNUTLS_CRT_X509;
+#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
-static int cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs,
-       const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st * st) {
-       st->cert_type = GNUTLS_CRT_X509;
-       st->key_type = GNUTLS_PRIVKEY_X509;
+typedef gnutls_connection_end_t inspircd_gnutls_session_init_flags_t;
 #endif
-       SSLConfig* conf = GetSessionConfig(session);
-       std::vector<gnutls_x509_crt_t>& x509_certs = conf->x509_certs;
-       st->ncerts = x509_certs.size();
-       st->cert.x509 = &x509_certs[0];
-       st->key.x509 = conf->x509_key;
-       st->deinit_all = 0;
 
-       return 0;
-}
+#if INSPIRCD_GNUTLS_HAS_VERSION(3, 1, 9)
+#define INSPIRCD_GNUTLS_HAS_CORK
+#endif
 
-class RandGen : public HandlerBase2<void, char*, size_t>
+static Module* thismod;
+
+class RandGen
 {
  public:
-       RandGen() {}
-       void Call(char* buffer, size_t len)
+       static void Call(char* buffer, size_t len)
        {
 #ifdef GNUTLS_HAS_RND
                gnutls_rnd(GNUTLS_RND_RANDOM, buffer, len);
@@ -143,749 +123,675 @@ class RandGen : public HandlerBase2<void, char*, size_t>
        }
 };
 
-/** Represents an SSL user's extra data
- */
-class issl_session
-{
-public:
-       StreamSocket* socket;
-       gnutls_session_t sess;
-       issl_status status;
-       reference<ssl_cert> cert;
-       reference<SSLConfig> config;
-
-       issl_session() : socket(NULL), sess(NULL), status(ISSL_NONE) {}
-};
-
-static SSLConfig* GetSessionConfig(gnutls_session_t sess)
+namespace GnuTLS
 {
-       issl_session* session = reinterpret_cast<issl_session*>(gnutls_transport_get_ptr(sess));
-       return session->config;
-}
-
-class CommandStartTLS : public SplitCommand
-{
- public:
-       bool enabled;
-       CommandStartTLS (Module* mod) : SplitCommand(mod, "STARTTLS")
+       class Init
        {
-               enabled = true;
-               works_before_reg = true;
-       }
+        public:
+               Init() { gnutls_global_init(); }
+               ~Init() { gnutls_global_deinit(); }
+       };
 
-       CmdResult HandleLocal(const std::vector<std::string> &parameters, LocalUser *user)
+       class Exception : public ModuleException
        {
-               if (!enabled)
-               {
-                       user->WriteNumeric(691, "%s :STARTTLS is not enabled", user->nick.c_str());
-                       return CMD_FAILURE;
-               }
+        public:
+               Exception(const std::string& reason)
+                       : ModuleException(reason) { }
+       };
 
-               if (user->registered == REG_ALL)
-               {
-                       user->WriteNumeric(691, "%s :STARTTLS is not permitted after client registration is complete", user->nick.c_str());
-               }
-               else
+       void ThrowOnError(int errcode, const char* msg)
+       {
+               if (errcode < 0)
                {
-                       if (!user->eh.GetIOHook())
-                       {
-                               user->WriteNumeric(670, "%s :STARTTLS successful, go ahead with TLS handshake", user->nick.c_str());
-                               /* 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,
-                                * we assume the write will not block here; this is usually safe, as
-                                * STARTTLS is sent very early on in the registration phase, where the
-                                * user hasn't built up much sendq. Handling a blocked write here would
-                                * be very annoying.
-                                */
-                               user->eh.DoWrite();
-                               user->eh.AddIOHook(creator);
-                               creator->OnStreamSocketAccept(&user->eh, NULL, NULL);
-                       }
-                       else
-                               user->WriteNumeric(691, "%s :STARTTLS failure", user->nick.c_str());
+                       std::string reason = msg;
+                       reason.append(" :").append(gnutls_strerror(errcode));
+                       throw Exception(reason);
                }
-
-               return CMD_FAILURE;
        }
-};
-
-class ModuleSSLGnuTLS : public Module
-{
-       issl_session* sessions;
-
-       gnutls_digest_algorithm_t hash;
 
-       std::string sslports;
-       int dh_bits;
+       /** Used to create a gnutls_datum_t* from a std::string
+        */
+       class Datum
+       {
+               gnutls_datum_t datum;
 
-       RandGen randhandler;
-       CommandStartTLS starttls;
+        public:
+               Datum(const std::string& dat)
+               {
+                       datum.data = (unsigned char*)dat.data();
+                       datum.size = static_cast<unsigned int>(dat.length());
+               }
 
-       GenericCap capHandler;
-       ServiceProvider iohook;
+               const gnutls_datum_t* get() const { return &datum; }
+       };
 
-       inline static const char* UnknownIfNULL(const char* str)
+       class Hash
        {
-               return str ? str : "UNKNOWN";
-       }
+               gnutls_digest_algorithm_t hash;
 
-       static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size)
-       {
-               issl_session* session = reinterpret_cast<issl_session*>(session_wrap);
-               if (session->socket->GetEventMask() & FD_READ_WILL_BLOCK)
+        public:
+               // Nothing to deallocate, constructor may throw freely
+               Hash(const std::string& hashname)
                {
-#ifdef _WIN32
-                       gnutls_transport_set_errno(session->sess, EAGAIN);
+                       // As older versions of gnutls can't do this, let's disable it where needed.
+#ifdef GNUTLS_HAS_MAC_GET_ID
+                       // As gnutls_digest_algorithm_t and gnutls_mac_algorithm_t are mapped 1:1, we can do this
+                       // There is no gnutls_dig_get_id() at the moment, but it may come later
+                       hash = (gnutls_digest_algorithm_t)gnutls_mac_get_id(hashname.c_str());
+                       if (hash == GNUTLS_DIG_UNKNOWN)
+                               throw Exception("Unknown hash type " + hashname);
+
+                       // Check if the user is giving us something that is a valid MAC but not digest
+                       gnutls_hash_hd_t is_digest;
+                       if (gnutls_hash_init(&is_digest, hash) < 0)
+                               throw Exception("Unknown hash type " + hashname);
+                       gnutls_hash_deinit(is_digest, NULL);
 #else
-                       errno = EAGAIN;
+                       if (stdalgo::string::equalsci(hashname, "md5"))
+                               hash = GNUTLS_DIG_MD5;
+                       else if (stdalgo::string::equalsci(hashname, "sha1"))
+                               hash = GNUTLS_DIG_SHA1;
+                       else if (stdalgo::string::equalsci(hashname, "sha256"))
+                               hash = GNUTLS_DIG_SHA256;
+                       else
+                               throw Exception("Unknown hash type " + hashname);
 #endif
-                       return -1;
                }
 
-               int rv = ServerInstance->SE->Recv(session->socket, reinterpret_cast<char *>(buffer), size, 0);
+               gnutls_digest_algorithm_t get() const { return hash; }
+       };
 
-#ifdef _WIN32
-               if (rv < 0)
+       class DHParams
+       {
+               gnutls_dh_params_t dh_params;
+
+               DHParams()
                {
-                       /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
-                        * and then set errno appropriately.
-                        * The gnutls library may also have a different errno variable than us, see
-                        * gnutls_transport_set_errno(3).
-                        */
-                       gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+                       ThrowOnError(gnutls_dh_params_init(&dh_params), "gnutls_dh_params_init() failed");
                }
-#endif
-
-               if (rv < (int)size)
-                       ServerInstance->SE->ChangeEventMask(session->socket, FD_READ_WILL_BLOCK);
-               return rv;
-       }
 
-       static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size)
-       {
-               issl_session* session = reinterpret_cast<issl_session*>(session_wrap);
-               if (session->socket->GetEventMask() & FD_WRITE_WILL_BLOCK)
+        public:
+               /** Import */
+               static std::auto_ptr<DHParams> Import(const std::string& dhstr)
                {
-#ifdef _WIN32
-                       gnutls_transport_set_errno(session->sess, EAGAIN);
-#else
-                       errno = EAGAIN;
-#endif
-                       return -1;
+                       std::auto_ptr<DHParams> dh(new DHParams);
+                       int ret = gnutls_dh_params_import_pkcs3(dh->dh_params, Datum(dhstr).get(), GNUTLS_X509_FMT_PEM);
+                       ThrowOnError(ret, "Unable to import DH params");
+                       return dh;
                }
 
-               int rv = ServerInstance->SE->Send(session->socket, reinterpret_cast<const char *>(buffer), size, 0);
-
-#ifdef _WIN32
-               if (rv < 0)
+               ~DHParams()
                {
-                       /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
-                        * and then set errno appropriately.
-                        * The gnutls library may also have a different errno variable than us, see
-                        * gnutls_transport_set_errno(3).
-                        */
-                       gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+                       gnutls_dh_params_deinit(dh_params);
                }
-#endif
-
-               if (rv < (int)size)
-                       ServerInstance->SE->ChangeEventMask(session->socket, FD_WRITE_WILL_BLOCK);
-               return rv;
-       }
 
- public:
+               const gnutls_dh_params_t& get() const { return dh_params; }
+       };
 
-       ModuleSSLGnuTLS()
-               : starttls(this), capHandler(this, "tls"), iohook(this, "ssl/gnutls", SERVICE_IOHOOK)
+       class X509Key
        {
-#ifndef GNUTLS_HAS_RND
-               gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
-#endif
-
-               sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
-
-               gnutls_global_init(); // This must be called once in the program
-       }
+               /** Ensure that the key is deinited in case the constructor of X509Key throws
+                */
+               class RAIIKey
+               {
+                public:
+                       gnutls_x509_privkey_t key;
 
-       void init()
-       {
-               currconf = new SSLConfig;
-               InitSSLConfig(currconf);
+                       RAIIKey()
+                       {
+                               ThrowOnError(gnutls_x509_privkey_init(&key), "gnutls_x509_privkey_init() failed");
+                       }
 
-               ServerInstance->GenRandom = &randhandler;
+                       ~RAIIKey()
+                       {
+                               gnutls_x509_privkey_deinit(key);
+                       }
+               } key;
 
-               Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnUserConnect,
-                       I_OnEvent, I_OnHookIO, I_OnCheckReady };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+        public:
+               /** Import */
+               X509Key(const std::string& keystr)
+               {
+                       int ret = gnutls_x509_privkey_import(key.key, Datum(keystr).get(), GNUTLS_X509_FMT_PEM);
+                       ThrowOnError(ret, "Unable to import private key");
+               }
 
-               ServerInstance->Modules->AddService(iohook);
-               ServerInstance->Modules->AddService(starttls);
-       }
+               gnutls_x509_privkey_t& get() { return key.key; }
+       };
 
-       void OnRehash(User* user)
+       class X509CertList
        {
-               sslports.clear();
+               std::vector<gnutls_x509_crt_t> certs;
 
-               ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
-               starttls.enabled = Conf->getBool("starttls", true);
-
-               if (Conf->getBool("showports", true))
+        public:
+               /** Import */
+               X509CertList(const std::string& certstr)
                {
-                       sslports = Conf->getString("advertisedports");
-                       if (!sslports.empty())
-                               return;
+                       unsigned int certcount = 3;
+                       certs.resize(certcount);
+                       Datum datum(certstr);
 
-                       for (size_t i = 0; i < ServerInstance->ports.size(); i++)
+                       int ret = gnutls_x509_crt_list_import(raw(), &certcount, datum.get(), GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
+                       if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
                        {
-                               ListenSocket* port = ServerInstance->ports[i];
-                               if (port->bind_tag->getString("ssl") != "gnutls")
-                                       continue;
-
-                               const std::string& portid = port->bind_desc;
-                               ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %s", portid.c_str());
-
-                               if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
-                               {
-                                       /*
-                                        * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
-                                        * the IP:port in ISUPPORT.
-                                        *
-                                        * We used to advertise all ports seperated by a ';' char that matched the above criteria,
-                                        * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
-                                        * To solve this by default we now only display the first IP:port found and let the user
-                                        * configure the exact value for the 005 token, if necessary.
-                                        */
-                                       sslports = portid;
-                                       break;
-                               }
+                               // the buffer wasn't big enough to hold all certs but gnutls changed certcount to the number of available certs,
+                               // try again with a bigger buffer
+                               certs.resize(certcount);
+                               ret = gnutls_x509_crt_list_import(raw(), &certcount, datum.get(), GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
                        }
-               }
-       }
 
-       void OnModuleRehash(User* user, const std::string &param)
-       {
-               if(param != "ssl")
-                       return;
+                       ThrowOnError(ret, "Unable to load certificates");
 
-               reference<SSLConfig> newconf = new SSLConfig;
-               try
-               {
-                       InitSSLConfig(newconf);
+                       // Resize the vector to the actual number of certs because we rely on its size being correct
+                       // when deallocating the certs
+                       certs.resize(certcount);
                }
-               catch (ModuleException& ex)
+
+               ~X509CertList()
                {
-                       ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls: Not applying new config. %s", ex.GetReason());
-                       return;
+                       for (std::vector<gnutls_x509_crt_t>::iterator i = certs.begin(); i != certs.end(); ++i)
+                               gnutls_x509_crt_deinit(*i);
                }
 
-               ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls: Applying new config, old config is in use by %d connection(s)", currconf->GetReferenceCount()-1);
-               currconf = newconf;
-       }
+               gnutls_x509_crt_t* raw() { return &certs[0]; }
+               unsigned int size() const { return certs.size(); }
+       };
 
-       void InitSSLConfig(SSLConfig* config)
+       class X509CRL : public refcountbase
        {
-               ServerInstance->Logs->Log("m_ssl_gnutls", DEBUG, "Initializing new SSLConfig %p", (void*)config);
-
-               std::string keyfile;
-               std::string certfile;
-               std::string cafile;
-               std::string crlfile;
-               OnRehash(NULL);
-
-               ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
-
-               cafile = Conf->getString("cafile", CONFIG_PATH "/ca.pem");
-               crlfile = Conf->getString("crlfile", CONFIG_PATH "/crl.pem");
-               certfile = Conf->getString("certfile", CONFIG_PATH "/cert.pem");
-               keyfile = Conf->getString("keyfile", CONFIG_PATH "/key.pem");
-               dh_bits = Conf->getInt("dhbits");
-               std::string hashname = Conf->getString("hash", "md5");
-
-               // The GnuTLS manual states that the gnutls_set_default_priority()
-               // call we used previously when initializing the session is the same
-               // as setting the "NORMAL" priority string.
-               // Thus if the setting below is not in the config we will behave exactly
-               // the same as before, when the priority setting wasn't available.
-               std::string priorities = Conf->getString("priority", "NORMAL");
-
-               if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096))
-                       dh_bits = 1024;
-
-               if (hashname == "md5")
-                       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 ModuleException("Unknown hash type " + hashname);
-
+               class RAIICRL
+               {
+                public:
+                       gnutls_x509_crl_t crl;
 
-               int ret;
+                       RAIICRL()
+                       {
+                               ThrowOnError(gnutls_x509_crl_init(&crl), "gnutls_x509_crl_init() failed");
+                       }
 
-               gnutls_certificate_credentials_t& x509_cred = config->x509_cred;
+                       ~RAIICRL()
+                       {
+                               gnutls_x509_crl_deinit(crl);
+                       }
+               } crl;
 
-               ret = gnutls_certificate_allocate_credentials(&x509_cred);
-               if (ret < 0)
+        public:
+               /** Import */
+               X509CRL(const std::string& crlstr)
                {
-                       // Set to NULL because we can't be sure what value is in it and we must not try to
-                       // deallocate it in case of an error
-                       x509_cred = NULL;
-                       throw ModuleException("Failed to allocate certificate credentials: " + std::string(gnutls_strerror(ret)));
+                       int ret = gnutls_x509_crl_import(get(), Datum(crlstr).get(), GNUTLS_X509_FMT_PEM);
+                       ThrowOnError(ret, "Unable to load certificate revocation list");
                }
 
-               if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
-                       ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
+               gnutls_x509_crl_t& get() { return crl.crl; }
+       };
 
-               if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
-                       ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));
-
-               FileReader reader;
-
-               reader.LoadFile(certfile);
-               std::string cert_string = reader.Contents();
-               gnutls_datum_t cert_datum = { (unsigned char*)cert_string.data(), static_cast<unsigned int>(cert_string.length()) };
+#ifdef GNUTLS_NEW_PRIO_API
+       class Priority
+       {
+               gnutls_priority_t priority;
 
-               reader.LoadFile(keyfile);
-               std::string key_string = reader.Contents();
-               gnutls_datum_t key_datum = { (unsigned char*)key_string.data(), static_cast<unsigned int>(key_string.length()) };
+        public:
+               Priority(const std::string& priorities)
+               {
+                       // Try to set the priorities for ciphers, kex methods etc. to the user supplied string
+                       // If the user did not supply anything then the string is already set to "NORMAL"
+                       const char* priocstr = priorities.c_str();
+                       const char* prioerror;
 
-               std::vector<gnutls_x509_crt_t>& x509_certs = config->x509_certs;
+                       int ret = gnutls_priority_init(&priority, priocstr, &prioerror);
+                       if (ret < 0)
+                       {
+                               // gnutls did not understand the user supplied string
+                               throw Exception("Unable to initialize priorities to \"" + priorities + "\": " + gnutls_strerror(ret) + " Syntax error at position " + ConvToStr((unsigned int) (prioerror - priocstr)));
+                       }
+               }
 
-               // If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException
-               unsigned int certcount = 3;
-               x509_certs.resize(certcount);
-               ret = gnutls_x509_crt_list_import(&x509_certs[0], &certcount, &cert_datum, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
-               if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
+               ~Priority()
                {
-                       // the buffer wasn't big enough to hold all certs but gnutls updated certcount to the number of available certs, try again with a bigger buffer
-                       x509_certs.resize(certcount);
-                       ret = gnutls_x509_crt_list_import(&x509_certs[0], &certcount, &cert_datum, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
+                       gnutls_priority_deinit(priority);
                }
 
-               if (ret <= 0)
+               void SetupSession(gnutls_session_t sess)
                {
-                       // clear the vector so we won't call gnutls_x509_crt_deinit() on the (uninited) certs later
-                       x509_certs.clear();
-                       throw ModuleException("Unable to load GnuTLS server certificate (" + certfile + "): " + ((ret < 0) ? (std::string(gnutls_strerror(ret))) : "No certs could be read"));
+                       gnutls_priority_set(sess, priority);
                }
-               x509_certs.resize(ret);
 
-               gnutls_x509_privkey_t& x509_key = config->x509_key;
-               if (gnutls_x509_privkey_init(&x509_key) < 0)
+               static const char* GetDefault()
                {
-                       // Make sure the destructor does not try to deallocate this, see above
-                       x509_key = NULL;
-                       throw ModuleException("Unable to initialize private key");
+                       return "NORMAL:%SERVER_PRECEDENCE:-VERS-SSL3.0";
                }
 
-               if((ret = gnutls_x509_privkey_import(x509_key, &key_datum, GNUTLS_X509_FMT_PEM)) < 0)
-                       throw ModuleException("Unable to load GnuTLS server private key (" + keyfile + "): " + std::string(gnutls_strerror(ret)));
+               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
+        */
+       class Priority
+       {
+        public:
+               Priority(const std::string& priorities)
+               {
+                       if (priorities != GetDefault())
+                               throw Exception("You've set a non-default priority string, but GnuTLS lacks support for it");
+               }
 
-               if((ret = gnutls_certificate_set_x509_key(x509_cred, &x509_certs[0], certcount, x509_key)) < 0)
-                       throw ModuleException("Unable to set GnuTLS cert/key pair: " + std::string(gnutls_strerror(ret)));
+               static void SetupSession(gnutls_session_t sess)
+               {
+                       // Always set the default priorities
+                       gnutls_set_default_priority(sess);
+               }
 
-               #ifdef GNUTLS_NEW_PRIO_API
-               // Try to set the priorities for ciphers, kex methods etc. to the user supplied string
-               // If the user did not supply anything then the string is already set to "NORMAL"
-               const char* priocstr = priorities.c_str();
-               const char* prioerror;
+               static const char* GetDefault()
+               {
+                       return "NORMAL";
+               }
 
-               gnutls_priority_t& priority = config->priority;
-               if ((ret = gnutls_priority_init(&priority, priocstr, &prioerror)) < 0)
+               static std::string RemoveUnknownTokens(const std::string& prio)
                {
-                       // gnutls did not understand the user supplied string, log and fall back to the default priorities
-                       ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to set priorities to \"%s\": %s Syntax error at position %u, falling back to default (NORMAL)", priorities.c_str(), gnutls_strerror(ret), (unsigned int) (prioerror - priocstr));
-                       gnutls_priority_init(&priority, "NORMAL", NULL);
+                       // We don't do anything here because only NORMAL is accepted
+                       return prio;
                }
+       };
+#endif
 
-               #else
-               if (priorities != "NORMAL")
-                       ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: You've set <gnutls:priority> to a value other than the default, but this is only supported with GnuTLS v2.1.7 or newer. Your GnuTLS version is older than that so the option will have no effect.");
-               #endif
+       class CertCredentials
+       {
+               /** DH parameters associated with these credentials
+                */
+               std::auto_ptr<DHParams> dh;
 
-               #if(GNUTLS_VERSION_MAJOR < 2 || ( GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12 ) )
-               gnutls_certificate_client_set_retrieve_function (x509_cred, cert_callback);
-               #else
-               gnutls_certificate_set_retrieve_function (x509_cred, cert_callback);
-               #endif
+        protected:
+               gnutls_certificate_credentials_t cred;
 
-               gnutls_dh_params_t& dh_params = config->dh_params;
-               ret = gnutls_dh_params_init(&dh_params);
-               if (ret < 0)
+        public:
+               CertCredentials()
                {
-                       // Make sure the destructor does not try to deallocate this, see above
-                       dh_params = NULL;
-                       ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters: %s", gnutls_strerror(ret));
-                       return;
+                       ThrowOnError(gnutls_certificate_allocate_credentials(&cred), "Cannot allocate certificate credentials");
                }
 
-               std::string dhfile = Conf->getString("dhfile");
-               if (!dhfile.empty())
+               ~CertCredentials()
                {
-                       // Try to load DH params from file
-                       reader.LoadFile(dhfile);
-                       std::string dhstring = reader.Contents();
-                       gnutls_datum_t dh_datum = { (unsigned char*)dhstring.data(), static_cast<unsigned int>(dhstring.length()) };
-
-                       if ((ret = gnutls_dh_params_import_pkcs3(dh_params, &dh_datum, GNUTLS_X509_FMT_PEM)) < 0)
-                       {
-                               // File unreadable or GnuTLS was unhappy with the contents, generate the DH primes now
-                               ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Generating DH parameters because I failed to load them from file '%s': %s", dhfile.c_str(), gnutls_strerror(ret));
-                               GenerateDHParams(dh_params);
-                       }
+                       gnutls_certificate_free_credentials(cred);
                }
-               else
+
+               /** Associates these credentials with the session
+                */
+               void SetupSession(gnutls_session_t sess)
                {
-                       GenerateDHParams(dh_params);
+                       gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, cred);
                }
 
-               gnutls_certificate_set_dh_params(x509_cred, dh_params);
-       }
+               /** Set the given DH parameters to be used with these credentials
+                */
+               void SetDH(std::auto_ptr<DHParams>& DH)
+               {
+                       dh = DH;
+                       gnutls_certificate_set_dh_params(cred, dh->get());
+               }
+       };
 
-       void GenerateDHParams(gnutls_dh_params_t dh_params)
+       class X509Credentials : public CertCredentials
        {
-               // Generate Diffie Hellman parameters - for use with DHE
-               // kx algorithms. These should be discarded and regenerated
-               // once a day, once a week or once a month. Depending on the
-               // security requirements.
+               /** Private key
+                */
+               X509Key key;
 
-               int ret;
+               /** Certificate list, presented to the peer
+                */
+               X509CertList certs;
 
-               if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0)
-                       ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret));
-       }
+               /** Trusted CA, may be NULL
+                */
+               std::auto_ptr<X509CertList> trustedca;
 
-       ~ModuleSSLGnuTLS()
-       {
-               currconf = NULL;
+               /** Certificate revocation list, may be NULL
+                */
+               std::auto_ptr<X509CRL> crl;
 
-               gnutls_global_deinit();
-               delete[] sessions;
-               ServerInstance->GenRandom = &ServerInstance->HandleGenRandom;
-       }
+               static int cert_callback(gnutls_session_t session, 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);
 
-       void OnCleanup(int target_type, void* item)
-       {
-               if(target_type == TYPE_USER)
+        public:
+               X509Credentials(const std::string& certstr, const std::string& keystr)
+                       : key(keystr)
+                       , certs(certstr)
                {
-                       LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+                       // Throwing is ok here, the destructor of Credentials is called in that case
+                       int ret = gnutls_certificate_set_x509_key(cred, certs.raw(), certs.size(), key.get());
+                       ThrowOnError(ret, "Unable to set cert/key pair");
 
-                       if (user && user->eh.GetIOHook() == 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.
-                               ServerInstance->Users->QuitUser(user, "SSL module unloading");
-                       }
+#ifdef GNUTLS_NEW_CERT_CALLBACK_API
+                       gnutls_certificate_set_retrieve_function(cred, cert_callback);
+#else
+                       gnutls_certificate_client_set_retrieve_function(cred, cert_callback);
+#endif
                }
-       }
 
-       Version GetVersion()
-       {
-               return Version("Provides SSL support for clients", VF_VENDOR);
-       }
+               /** Sets the trusted CA and the certificate revocation list
+                * to use when verifying certificates
+                */
+               void SetCA(std::auto_ptr<X509CertList>& certlist, std::auto_ptr<X509CRL>& CRL)
+               {
+                       // Do nothing if certlist is NULL
+                       if (certlist.get())
+                       {
+                               int ret = gnutls_certificate_set_x509_trust(cred, certlist->raw(), certlist->size());
+                               ThrowOnError(ret, "gnutls_certificate_set_x509_trust() failed");
 
+                               if (CRL.get())
+                               {
+                                       ret = gnutls_certificate_set_x509_crl(cred, &CRL->get(), 1);
+                                       ThrowOnError(ret, "gnutls_certificate_set_x509_crl() failed");
+                               }
 
-       void On005Numeric(std::string &output)
-       {
-               if (!sslports.empty())
-                       output.append(" SSL=" + sslports);
-               if (starttls.enabled)
-                       output.append(" STARTTLS");
-       }
+                               trustedca = certlist;
+                               crl = CRL;
+                       }
+               }
+       };
 
-       void OnHookIO(StreamSocket* user, ListenSocket* lsb)
+       class DataReader
        {
-               if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "gnutls")
+               int retval;
+#ifdef INSPIRCD_GNUTLS_HAS_RECV_PACKET
+               gnutls_packet_t packet;
+
+        public:
+               DataReader(gnutls_session_t sess)
                {
-                       /* Hook the user with our module */
-                       user->AddIOHook(this);
+                       // 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 OnRequest(Request& request)
-       {
-               if (strcmp("GET_SSL_CERT", request.id) == 0)
+               void appendto(std::string& recvq)
                {
-                       SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
-                       int fd = req.sock->GetFd();
-                       issl_session* session = &sessions[fd];
+                       // 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);
 
-                       req.cert = session->cert;
+                       gnutls_packet_deinit(packet);
                }
-               else if (!strcmp("GET_RAW_SSL_SESSION", request.id))
+#else
+               char* const buffer;
+
+        public:
+               DataReader(gnutls_session_t sess)
+                       : buffer(ServerInstance->GetReadBuffer())
                {
-                       SSLRawSessionRequest& req = static_cast<SSLRawSessionRequest&>(request);
-                       if ((req.fd >= 0) && (req.fd < ServerInstance->SE->GetMaxFds()))
-                               req.data = reinterpret_cast<void*>(sessions[req.fd].sess);
+                       // Read data from GnuTLS buffers into ReadBuffer
+                       retval = gnutls_record_recv(sess, buffer, ServerInstance->Config->NetBufferSize);
                }
-       }
 
-       void InitSession(StreamSocket* user, bool me_server)
-       {
-               issl_session* session = &sessions[user->GetFd()];
+               void appendto(std::string& recvq)
+               {
+                       // Copy data from ReadBuffer to recvq
+                       recvq.append(buffer, retval);
+               }
+#endif
 
-               gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
-               session->socket = user;
-               session->config = currconf;
+               int ret() const { return retval; }
+       };
 
-               #ifdef GNUTLS_NEW_PRIO_API
-               gnutls_priority_set(session->sess, currconf->priority);
-               #else
-               gnutls_set_default_priority(session->sess);
-               #endif
-               gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, currconf->x509_cred);
-               gnutls_dh_set_prime_bits(session->sess, dh_bits);
-               gnutls_transport_set_ptr(session->sess, reinterpret_cast<gnutls_transport_ptr_t>(session));
-               gnutls_transport_set_push_function(session->sess, gnutls_push_wrapper);
-               gnutls_transport_set_pull_function(session->sess, gnutls_pull_wrapper);
+       class Profile
+       {
+               /** Name of this profile
+                */
+               const std::string name;
 
-               if (me_server)
-                       gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
+               /** X509 certificate(s) and key
+                */
+               X509Credentials x509cred;
 
-               Handshake(session, user);
-       }
-
-       void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
-       {
-               issl_session* session = &sessions[user->GetFd()];
-
-               /* For STARTTLS: Don't try and init a session on a socket that already has a session */
-               if (session->sess)
-                       return;
+               /** The minimum length in bits for the DH prime to be accepted as a client
+                */
+               unsigned int min_dh_bits;
 
-               InitSession(user, true);
-       }
+               /** Hashing algorithm to use when generating certificate fingerprints
+                */
+               Hash hash;
 
-       void OnStreamSocketConnect(StreamSocket* user)
-       {
-               InitSession(user, false);
-       }
+               /** Priorities for ciphers, compression methods, etc.
+                */
+               Priority priority;
 
-       void OnStreamSocketClose(StreamSocket* user)
-       {
-               CloseSession(&sessions[user->GetFd()]);
-       }
+               /** Rough max size of records to send
+                */
+               const unsigned int outrecsize;
 
-       int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
-       {
-               issl_session* session = &sessions[user->GetFd()];
+               /** True to request a client certificate as a server
+                */
+               const bool requestclientcert;
 
-               if (!session->sess)
+               static std::string ReadFile(const std::string& filename)
                {
-                       CloseSession(session);
-                       user->SetError("No SSL session");
-                       return -1;
+                       FileReader reader(filename);
+                       std::string ret = reader.GetString();
+                       if (ret.empty())
+                               throw Exception("Cannot read file " + filename);
+                       return ret;
                }
 
-               if (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE)
+               static std::string GetPrioStr(const std::string& profilename, ConfigTag* tag)
                {
-                       // The handshake isn't finished, try to finish it.
-
-                       if(!Handshake(session, user))
+                       // 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))
                        {
-                               if (session->status != ISSL_CLOSING)
-                                       return 0;
-                               return -1;
+                               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;
                }
 
-               // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
-
-               if (session->status == ISSL_HANDSHAKEN)
+        public:
+               struct Config
                {
-                       char* buffer = ServerInstance->GetReadBuffer();
-                       size_t bufsiz = ServerInstance->Config->NetBufferSize;
-                       int ret = gnutls_record_recv(session->sess, buffer, bufsiz);
-                       if (ret > 0)
+                       std::string name;
+
+                       std::auto_ptr<X509CertList> ca;
+                       std::auto_ptr<X509CRL> crl;
+
+                       std::string certstr;
+                       std::string keystr;
+                       std::auto_ptr<DHParams> dh;
+
+                       std::string priostr;
+                       unsigned int mindh;
+                       std::string hashstr;
+
+                       unsigned int outrecsize;
+                       bool requestclientcert;
+
+                       Config(const std::string& profilename, ConfigTag* tag)
+                               : name(profilename)
+                               , certstr(ReadFile(tag->getString("certfile", "cert.pem")))
+                               , keystr(ReadFile(tag->getString("keyfile", "key.pem")))
+                               , dh(DHParams::Import(ReadFile(tag->getString("dhfile", "dhparams.pem"))))
+                               , priostr(GetPrioStr(profilename, tag))
+                               , mindh(tag->getUInt("mindhbits", 1024))
+                               , hashstr(tag->getString("hash", "md5"))
+                               , requestclientcert(tag->getBool("requestclientcert", true))
                        {
-                               recvq.append(buffer, ret);
-                               // Schedule a read if there is still data in the GnuTLS buffer
-                               if (gnutls_record_check_pending(session->sess) > 0)
-                                       ServerInstance->SE->ChangeEventMask(user, FD_ADD_TRIAL_READ);
-                               return 1;
-                       }
-                       else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
-                       {
-                               return 0;
-                       }
-                       else if (ret == 0)
-                       {
-                               user->SetError("Connection closed");
-                               CloseSession(session);
-                               return -1;
-                       }
-                       else
-                       {
-                               user->SetError(gnutls_strerror(ret));
-                               CloseSession(session);
-                               return -1;
-                       }
-               }
-               else if (session->status == ISSL_CLOSING)
-                       return -1;
-
-               return 0;
-       }
+                               // Load trusted CA and revocation list, if set
+                               std::string filename = tag->getString("cafile");
+                               if (!filename.empty())
+                               {
+                                       ca.reset(new X509CertList(ReadFile(filename)));
 
-       int OnStreamSocketWrite(StreamSocket* user, std::string& sendq)
-       {
-               issl_session* session = &sessions[user->GetFd()];
+                                       filename = tag->getString("crlfile");
+                                       if (!filename.empty())
+                                               crl.reset(new X509CRL(ReadFile(filename)));
+                               }
 
-               if (!session->sess)
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+                               // If cork support is available outrecsize represents the (rough) max amount of data we give GnuTLS while corked
+                               outrecsize = tag->getUInt("outrecsize", 2048, 512);
+#else
+                               outrecsize = tag->getUInt("outrecsize", 2048, 512, 16384);
+#endif
+                       }
+               };
+
+               Profile(Config& config)
+                       : name(config.name)
+                       , x509cred(config.certstr, config.keystr)
+                       , min_dh_bits(config.mindh)
+                       , hash(config.hashstr)
+                       , priority(config.priostr)
+                       , outrecsize(config.outrecsize)
+                       , requestclientcert(config.requestclientcert)
                {
-                       CloseSession(session);
-                       user->SetError("No SSL session");
-                       return -1;
+                       x509cred.SetDH(config.dh);
+                       x509cred.SetCA(config.ca, config.crl);
                }
-
-               if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ)
+               /** Set up the given session with the settings in this profile
+                */
+               void SetupSession(gnutls_session_t sess)
                {
-                       // The handshake isn't finished, try to finish it.
-                       Handshake(session, user);
-                       if (session->status != ISSL_CLOSING)
-                               return 0;
-                       return -1;
+                       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);
                }
 
-               int ret = 0;
+               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; }
+       };
+}
 
-               if (session->status == ISSL_HANDSHAKEN)
-               {
-                       ret = gnutls_record_send(session->sess, sendq.data(), sendq.length());
+class GnuTLSIOHook : public SSLIOHook
+{
+ private:
+       gnutls_session_t sess;
+       issl_status status;
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+       size_t gbuffersize;
+#endif
 
-                       if (ret == (int)sendq.length())
-                       {
-                               ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_WRITE);
-                               return 1;
-                       }
-                       else if (ret > 0)
-                       {
-                               sendq = sendq.substr(ret);
-                               ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
-                               return 0;
-                       }
-                       else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
-                       {
-                               ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
-                               return 0;
-                       }
-                       else // (ret < 0)
-                       {
-                               user->SetError(gnutls_strerror(ret));
-                               CloseSession(session);
-                               return -1;
-                       }
+       void CloseSession()
+       {
+               if (this->sess)
+               {
+                       gnutls_bye(this->sess, GNUTLS_SHUT_WR);
+                       gnutls_deinit(this->sess);
                }
-
-               return 0;
+               sess = NULL;
+               certificate = NULL;
+               status = ISSL_NONE;
        }
 
-       bool Handshake(issl_session* session, 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(session->sess);
+               int ret = gnutls_handshake(this->sess);
 
                if (ret < 0)
                {
                        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(session->sess) == 0)
+                               if (gnutls_record_get_direction(this->sess) == 0)
                                {
                                        // gnutls_handshake() wants to read() again.
-                                       session->status = ISSL_HANDSHAKING_READ;
-                                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+                                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
                                }
                                else
                                {
                                        // gnutls_handshake() wants to write() again.
-                                       session->status = ISSL_HANDSHAKING_WRITE;
-                                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_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(session);
-                               session->status = ISSL_CLOSING;
+                               CloseSession();
+                               return -1;
                        }
-
-                       return false;
                }
                else
                {
                        // Change the seesion state
-                       session->status = ISSL_HANDSHAKEN;
+                       this->status = ISSL_HANDSHAKEN;
 
-                       VerifyCertificate(session,user);
+                       VerifyCertificate();
 
                        // Finish writing, if any left
-                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
-
-                       return true;
-               }
-       }
-
-       void OnUserConnect(LocalUser* user)
-       {
-               if (user->eh.GetIOHook() == this)
-               {
-                       if (sessions[user->eh.GetFd()].sess)
-                       {
-                               const gnutls_session_t& sess = sessions[user->eh.GetFd()].sess;
-                               std::string cipher = UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)));
-                               cipher.append("-").append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).append("-");
-                               cipher.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess))));
-
-                               ssl_cert* cert = sessions[user->eh.GetFd()].cert;
-                               if (cert->fingerprint.empty())
-                                       user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), cipher.c_str());
-                               else
-                                       user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
-                                               " and your SSL fingerprint is %s", user->nick.c_str(), cipher.c_str(), cert->fingerprint.c_str());
-                       }
-               }
-       }
+                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 
-       void CloseSession(issl_session* session)
-       {
-               if (session->sess)
-               {
-                       gnutls_bye(session->sess, GNUTLS_SHUT_WR);
-                       gnutls_deinit(session->sess);
+                       return 1;
                }
-               session->socket = NULL;
-               session->sess = NULL;
-               session->cert = NULL;
-               session->status = ISSL_NONE;
-               session->config = NULL;
        }
 
-       void VerifyCertificate(issl_session* session, StreamSocket* user)
+       void VerifyCertificate()
        {
-               if (!session->sess || !user)
-                       return;
-
-               unsigned int status;
+               unsigned int certstatus;
                const gnutls_datum_t* cert_list;
                int ret;
                unsigned int cert_list_size;
                gnutls_x509_crt_t cert;
-               char name[MAXBUF];
-               unsigned char digest[MAXBUF];
+               char str[512];
+               unsigned char digest[512];
                size_t digest_size = sizeof(digest);
-               size_t name_size = sizeof(name);
+               size_t name_size = sizeof(str);
                ssl_cert* certinfo = new ssl_cert;
-               session->cert = certinfo;
+               this->certificate = certinfo;
 
                /* This verification function uses the trusted CAs in the credentials
                 * structure. So you must have installed one or more CA certificates.
                 */
-               ret = gnutls_certificate_verify_peers2(session->sess, &status);
+               ret = gnutls_certificate_verify_peers2(this->sess, &certstatus);
 
                if (ret < 0)
                {
@@ -893,16 +799,16 @@ class ModuleSSLGnuTLS : public Module
                        return;
                }
 
-               certinfo->invalid = (status & GNUTLS_CERT_INVALID);
-               certinfo->unknownsigner = (status & GNUTLS_CERT_SIGNER_NOT_FOUND);
-               certinfo->revoked = (status & GNUTLS_CERT_REVOKED);
-               certinfo->trusted = !(status & GNUTLS_CERT_SIGNER_NOT_CA);
+               certinfo->invalid = (certstatus & GNUTLS_CERT_INVALID);
+               certinfo->unknownsigner = (certstatus & GNUTLS_CERT_SIGNER_NOT_FOUND);
+               certinfo->revoked = (certstatus & GNUTLS_CERT_REVOKED);
+               certinfo->trusted = !(certstatus & GNUTLS_CERT_SIGNER_NOT_CA);
 
                /* Up to here the process is the same for X.509 certificates and
                 * OpenPGP keys. From now on X.509 certificates are assumed. This can
                 * be easily extended to work with openpgp keys as well.
                 */
-               if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509)
+               if (gnutls_certificate_type_get(this->sess) != GNUTLS_CRT_X509)
                {
                        certinfo->error = "No X509 keys sent";
                        return;
@@ -916,7 +822,7 @@ class ModuleSSLGnuTLS : public Module
                }
 
                cert_list_size = 0;
-               cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
+               cert_list = gnutls_certificate_get_peers(this->sess, &cert_list_size);
                if (cert_list == NULL)
                {
                        certinfo->error = "No certificate was found";
@@ -934,31 +840,31 @@ class ModuleSSLGnuTLS : public Module
                        goto info_done_dealloc;
                }
 
-               if (gnutls_x509_crt_get_dn(cert, name, &name_size) == 0)
+               if (gnutls_x509_crt_get_dn(cert, str, &name_size) == 0)
                {
                        std::string& dn = certinfo->dn;
-                       dn = name;
+                       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();
                }
 
-               name_size = sizeof(name);
-               if (gnutls_x509_crt_get_issuer_dn(cert, name, &name_size) == 0)
+               name_size = sizeof(str);
+               if (gnutls_x509_crt_get_issuer_dn(cert, str, &name_size) == 0)
                {
                        std::string& issuer = certinfo->issuer;
-                       issuer = name;
+                       issuer = str;
                        if (issuer.find_first_of("\r\n") != std::string::npos)
                                issuer.clear();
                }
 
-               if ((ret = gnutls_x509_crt_get_fingerprint(cert, hash, digest, &digest_size)) < 0)
+               if ((ret = gnutls_x509_crt_get_fingerprint(cert, GetProfile().GetHash(), digest, &digest_size)) < 0)
                {
                        certinfo->error = gnutls_strerror(ret);
                }
                else
                {
-                       certinfo->fingerprint = irc::hex(digest, digest_size);
+                       certinfo->fingerprint = BinToHex(digest, digest_size);
                }
 
                /* Beware here we do not check for errors.
@@ -972,15 +878,522 @@ info_done_dealloc:
                gnutls_x509_crt_deinit(cert);
        }
 
-       void OnEvent(Event& ev)
+       // 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";
+       }
+
+       static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size)
+       {
+               StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
+#ifdef _WIN32
+               GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
+#endif
+
+               if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
+               {
+#ifdef _WIN32
+                       gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+                       errno = EAGAIN;
+#endif
+                       return -1;
+               }
+
+               int rv = SocketEngine::Recv(sock, reinterpret_cast<char *>(buffer), size, 0);
+
+#ifdef _WIN32
+               if (rv < 0)
+               {
+                       /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
+                        * and then set errno appropriately.
+                        * The gnutls library may also have a different errno variable than us, see
+                        * gnutls_transport_set_errno(3).
+                        */
+                       gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+               }
+#endif
+
+               if (rv < (int)size)
+                       SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+               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->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;
+               }
+
+               int rv = SocketEngine::Send(sock, reinterpret_cast<const char *>(buffer), size, 0);
+
+#ifdef _WIN32
+               if (rv < 0)
+               {
+                       /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
+                        * and then set errno appropriately.
+                        * The gnutls library may also have a different errno variable than us, see
+                        * gnutls_transport_set_errno(3).
+                        */
+                       gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+               }
+#endif
+
+               if (rv < (int)size)
+                       SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+               return rv;
+       }
+#endif // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
+
+ public:
+       GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, inspircd_gnutls_session_init_flags_t flags)
+               : SSLIOHook(hookprov)
+               , sess(NULL)
+               , status(ISSL_NONE)
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+               , gbuffersize(0)
+#endif
+       {
+               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);
+               GetProfile().SetupSession(sess);
+
+               sock->AddIOHook(this);
+               Handshake(sock);
+       }
+
+       void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
+       {
+               CloseSession();
+       }
+
+       int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
+       {
+               // 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.
+               {
+                       GnuTLS::DataReader reader(sess);
+                       int ret = reader.ret();
+                       if (ret > 0)
+                       {
+                               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)
+                       {
+                               return 0;
+                       }
+                       else if (ret == 0)
+                       {
+                               user->SetError("Connection closed");
+                               CloseSession();
+                               return -1;
+                       }
+                       else
+                       {
+                               user->SetError(gnutls_strerror(ret));
+                               CloseSession();
+                               return -1;
+                       }
+               }
+       }
+
+       int OnStreamSocketWrite(StreamSocket* user, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
+       {
+               // Finish handshake if needed
+               int prepret = PrepareIO(user);
+               if (prepret <= 0)
+                       return prepret;
+
+               // Session is ready for transferring application data
+
+#ifdef INSPIRCD_GNUTLS_HAS_CORK
+               while (true)
+               {
+                       // 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 < GetProfile().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;
+
+               while (!sendq.empty())
+               {
+                       FlattenSendQueue(sendq, GetProfile().GetOutgoingRecordSize());
+                       const StreamSocket::SendQueue::Element& buffer = sendq.front();
+                       ret = HandleWriteRet(user, gnutls_record_send(this->sess, buffer.data(), buffer.length()));
+
+                       if (ret <= 0)
+                               return ret;
+                       else if (ret < (int)buffer.length())
+                       {
+                               sendq.erase_front(ret);
+                               SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+                               return 0;
+                       }
+
+                       // Wrote entire record, continue sending
+                       sendq.pop_front();
+               }
+#endif
+
+               SocketEngine::ChangeEventMask(user, FD_WANT_NO_WRITE);
+               return 1;
+       }
+
+       void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
+       {
+               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))));
+       }
+
+       bool GetServerName(std::string& out) const CXX11_OVERRIDE
+       {
+               std::vector<char> nameBuffer;
+               size_t nameLength = 0;
+               unsigned int nameType = GNUTLS_NAME_DNS;
+
+               // First, determine the size of the hostname.
+               if (gnutls_server_name_get(sess, &nameBuffer[0], &nameLength, &nameType, 0) != GNUTLS_E_SHORT_MEMORY_BUFFER)
+                       return false;
+
+               // Then retrieve the hostname.
+               nameBuffer.resize(nameLength);
+               if (gnutls_server_name_get(sess, &nameBuffer[0], &nameLength, &nameType, 0) != GNUTLS_E_SUCCESS)
+                       return false;
+
+               out.append(&nameBuffer[0]);
+               return true;
+       }
+
+       GnuTLS::Profile& GetProfile();
+       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)
+{
+#ifndef GNUTLS_NEW_CERT_CALLBACK_API
+       st->type = GNUTLS_CRT_X509;
+#else
+       st->cert_type = GNUTLS_CRT_X509;
+       st->key_type = GNUTLS_PRIVKEY_X509;
+#endif
+       StreamSocket* sock = reinterpret_cast<StreamSocket*>(gnutls_transport_get_ptr(sess));
+       GnuTLS::X509Credentials& cred = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod))->GetProfile().GetX509Credentials();
+
+       st->ncerts = cred.certs.size();
+       st->cert.x509 = cred.certs.raw();
+       st->key.x509 = cred.key.get();
+       st->deinit_all = 0;
+
+       return 0;
+}
+
+class GnuTLSIOHookProvider : public IOHookProvider
+{
+       GnuTLS::Profile profile;
+
+ public:
+       GnuTLSIOHookProvider(Module* mod, GnuTLS::Profile::Config& config)
+               : IOHookProvider(mod, "ssl/" + config.name, IOHookProvider::IOH_SSL)
+               , profile(config)
+       {
+               ServerInstance->Modules->AddService(*this);
+       }
+
+       ~GnuTLSIOHookProvider()
+       {
+               ServerInstance->Modules->DelService(*this);
+       }
+
+       void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+       {
+               new GnuTLSIOHook(this, sock, GNUTLS_SERVER);
+       }
+
+       void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+       {
+               new GnuTLSIOHook(this, sock, GNUTLS_CLIENT);
+       }
+
+       GnuTLS::Profile& GetProfile() { return profile; }
+};
+
+GnuTLS::Profile& GnuTLSIOHook::GetProfile()
+{
+       IOHookProvider* hookprov = prov;
+       return static_cast<GnuTLSIOHookProvider*>(hookprov)->GetProfile();
+}
+
+class ModuleSSLGnuTLS : public Module
+{
+       typedef std::vector<reference<GnuTLSIOHookProvider> > ProfileList;
+
+       // First member of the class, gets constructed first and destructed last
+       GnuTLS::Init libinit;
+       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 "gnutls" from settings in the <gnutls> block
+                       const std::string defname = "gnutls";
+                       ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found; using settings from the <gnutls> tag");
+
+                       try
+                       {
+                               GnuTLS::Profile::Config profileconfig(defname, tag);
+                               newprofiles.push_back(new GnuTLSIOHookProvider(this, profileconfig));
+                       }
+                       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 (!stdalgo::string::equalsci(tag->getString("provider"), "gnutls"))
+                               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<GnuTLSIOHookProvider> prov;
+                       try
+                       {
+                               GnuTLS::Profile::Config profileconfig(name, tag);
+                               prov = new GnuTLSIOHookProvider(this, profileconfig);
+                       }
+                       catch (CoreException& ex)
+                       {
+                               throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+                       }
+
+                       newprofiles.push_back(prov);
+               }
+
+               // New profiles are ok, begin using them
+               // Old profiles are deleted when their refcount drops to zero
+               for (ProfileList::iterator i = profiles.begin(); i != profiles.end(); ++i)
+               {
+                       GnuTLSIOHookProvider& prov = **i;
+                       ServerInstance->Modules.DelService(prov);
+               }
+
+               profiles.swap(newprofiles);
+       }
+
+ public:
+       ModuleSSLGnuTLS()
+       {
+#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 = RandGen::Call;
+       }
+
+       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.");
+               }
+       }
+
+       ~ModuleSSLGnuTLS()
+       {
+               ServerInstance->GenRandom = &InspIRCd::DefaultGenRandom;
+       }
+
+       void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+       {
+               if (type == ExtensionItem::EXT_USER)
+               {
+                       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 one of *our* SSL ports.
+                               // Potentially there could be multiple SSL modules loaded at once on different ports.
+                               ServerInstance->Users->QuitUser(user, "SSL module unloading");
+                       }
+               }
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
        {
-               if (starttls.enabled)
-                       capHandler.HandleEvent(ev);
+               return Version("Provides SSL support via GnuTLS", VF_VENDOR);
        }
 
-       ModResult OnCheckReady(LocalUser* user)
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
-               if ((user->eh.GetIOHook() == this) && (sessions[user->eh.GetFd()].status != ISSL_HANDSHAKEN))
+               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 (file)
index 0000000..75b25fb
--- /dev/null
@@ -0,0 +1,969 @@
+/*
+ * 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("debian" "9.0") libmbedtls-dev
+/// $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
+       {
+               /** 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;
+
+        public:
+               struct Config
+               {
+                       const std::string name;
+
+                       CTRDRBG& ctrdrbg;
+
+                       const std::string certstr;
+                       const std::string keystr;
+                       const std::string dhstr;
+
+                       const std::string ciphersuitestr;
+                       const std::string curvestr;
+                       const unsigned int mindh;
+                       const std::string hashstr;
+
+                       std::string crlstr;
+                       std::string castr;
+
+                       const int minver;
+                       const int maxver;
+                       const unsigned int outrecsize;
+                       const bool requestclientcert;
+
+                       Config(const std::string& profilename, ConfigTag* tag, CTRDRBG& ctr_drbg)
+                               : name(profilename)
+                               , ctrdrbg(ctr_drbg)
+                               , certstr(ReadFile(tag->getString("certfile", "cert.pem")))
+                               , keystr(ReadFile(tag->getString("keyfile", "key.pem")))
+                               , dhstr(ReadFile(tag->getString("dhfile", "dhparams.pem")))
+                               , ciphersuitestr(tag->getString("ciphersuites"))
+                               , curvestr(tag->getString("curves"))
+                               , mindh(tag->getUInt("mindhbits", 2048))
+                               , hashstr(tag->getString("hash", "sha256"))
+                               , castr(tag->getString("cafile"))
+                               , minver(tag->getUInt("minver", 0))
+                               , maxver(tag->getUInt("maxver", 0))
+                               , outrecsize(tag->getUInt("outrecsize", 2048, 512, 16384))
+                               , requestclientcert(tag->getBool("requestclientcert", true))
+                       {
+                               if (!castr.empty())
+                               {
+                                       castr = ReadFile(castr);
+                                       crlstr = tag->getString("crlfile");
+                                       if (!crlstr.empty())
+                                               crlstr = ReadFile(crlstr);
+                               }
+                       }
+               };
+
+               Profile(Config& config)
+                       : name(config.name)
+                       , x509cred(config.certstr, config.keystr)
+                       , ciphersuites(config.ciphersuitestr)
+                       , curves(config.curvestr)
+                       , serverctx(config.ctrdrbg, MBEDTLS_SSL_IS_SERVER)
+                       , clientctx(config.ctrdrbg, MBEDTLS_SSL_IS_CLIENT)
+                       , cacerts(config.castr, true)
+                       , crl(config.crlstr)
+                       , hash(config.hashstr)
+                       , outrecsize(config.outrecsize)
+               {
+                       serverctx.SetX509CertAndKey(x509cred);
+                       clientctx.SetX509CertAndKey(x509cred);
+                       clientctx.SetMinDHBits(config.mindh);
+
+                       if (!ciphersuites.empty())
+                       {
+                               serverctx.SetCiphersuites(ciphersuites);
+                               clientctx.SetCiphersuites(ciphersuites);
+                       }
+
+                       if (!curves.empty())
+                       {
+                               serverctx.SetCurves(curves);
+                               clientctx.SetCurves(curves);
+                       }
+
+                       serverctx.SetVersion(config.minver, config.maxver);
+                       clientctx.SetVersion(config.minver, config.maxver);
+
+                       if (!config.dhstr.empty())
+                       {
+                               dhparams.set(config.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 (config.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;
+               }
+
+               /** 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;
+
+       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 = GetProfile().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)
+               : SSLIOHook(hookprov)
+               , status(ISSL_NONE)
+       {
+               mbedtls_ssl_init(&sess);
+               if (isserver)
+                       GetProfile().SetupServerSession(&sess);
+               else
+                       GetProfile().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, GetProfile().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 GetServerName(std::string& out) const CXX11_OVERRIDE
+       {
+               // TODO: Implement SNI support.
+               return false;
+       }
+
+       mbedTLS::Profile& GetProfile();
+       bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); }
+};
+
+class mbedTLSIOHookProvider : public IOHookProvider
+{
+       mbedTLS::Profile profile;
+
+ public:
+       mbedTLSIOHookProvider(Module* mod, mbedTLS::Profile::Config& config)
+               : IOHookProvider(mod, "ssl/" + config.name, IOHookProvider::IOH_SSL)
+               , profile(config)
+       {
+               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);
+       }
+
+       void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+       {
+               new mbedTLSIOHook(this, sock, false);
+       }
+
+       mbedTLS::Profile& GetProfile() { return profile; }
+};
+
+mbedTLS::Profile& mbedTLSIOHook::GetProfile()
+{
+       IOHookProvider* hookprov = prov;
+       return static_cast<mbedTLSIOHookProvider*>(hookprov)->GetProfile();
+}
+
+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
+                       {
+                               mbedTLS::Profile::Config profileconfig(defname, tag, ctr_drbg);
+                               newprofiles.push_back(new mbedTLSIOHookProvider(this, profileconfig));
+                       }
+                       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 (!stdalgo::string::equalsci(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<mbedTLSIOHookProvider> prov;
+                       try
+                       {
+                               mbedTLS::Profile::Config profileconfig(name, tag, ctr_drbg);
+                               prov = new mbedTLSIOHookProvider(this, profileconfig);
+                       }
+                       catch (CoreException& ex)
+                       {
+                               throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+                       }
+
+                       newprofiles.push_back(prov);
+               }
+
+               // New profiles are ok, begin using them
+               // Old profiles are deleted when their refcount drops to zero
+               for (ProfileList::iterator i = profiles.begin(); i != profiles.end(); ++i)
+               {
+                       mbedTLSIOHookProvider& prov = **i;
+                       ServerInstance->Modules.DelService(prov);
+               }
+
+               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(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+       {
+               if (type != ExtensionItem::EXT_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)
index f2189f257e237954693efdbe29d7d9d9e6a10d34..3ebc8e4d91685b258e9961220790ad3c2a0f7ef4 100644 (file)
  * 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("debian") libssl-dev openssl pkg-config
+/// $PackageInfo: require_system("ubuntu") 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 <openssl/dh.h>
-#include "ssl.h"
 
 #ifdef _WIN32
 # pragma comment(lib, "ssleay32.lib")
 # pragma comment(lib, "libeay32.lib")
-# undef MAX_DESCRIPTORS
-# define MAX_DESCRIPTORS 10000
 #endif
 
 // Compatibility layer to allow OpenSSL 1.0 to use the 1.1 API.
 #if ((defined LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L))
+
+// BIO is opaque in OpenSSL 1.1 but the access API does not exist in 1.0.
+# 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;
+
+// These functions have been renamed in OpenSSL 1.1.
+# define OpenSSL_version SSLeay_version
 # define X509_getm_notAfter X509_get_notAfter
 # define X509_getm_notBefore X509_get_notBefore
 # define OPENSSL_init_ssl(OPTIONS, SETTINGS) \
        SSL_library_init(); \
        SSL_load_error_strings();
-#endif
 
-/* $ModDesc: Provides SSL support for clients */
+// These macros have been renamed in OpenSSL 1.1.
+# define OPENSSL_VERSION SSLEAY_VERSION
 
-/* $LinkerFlags: if("USE_FREEBSD_BASE_SSL") -lssl -lcrypto */
-/* $CompileFlags: if(!"USE_FREEBSD_BASE_SSL") pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
-/* $LinkerFlags: if(!"USE_FREEBSD_BASE_SSL") rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */
-
-/* $NoPedantic */
-
-
-class ModuleSSLOpenSSL;
+#else
+# define INSPIRCD_OPENSSL_OPAQUE_BIO
+#endif
 
 enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
 
 static bool SelfSigned = false;
-
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
-static ModuleSSLOpenSSL* opensslmod = NULL;
-#endif
+static int exdataindex;
 
 char* get_error()
 {
        return ERR_error_string(ERR_get_error(), NULL);
 }
 
-static int error_callback(const char *str, size_t len, void *u);
+static int OnVerify(int preverify_ok, X509_STORE_CTX* ctx);
+static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
 
-/** Represents an SSL user's extra data
- */
-class issl_session
+namespace OpenSSL
 {
-public:
-       SSL* sess;
-       issl_status status;
-       reference<ssl_cert> cert;
-
-       bool outbound;
-       bool data_to_write;
-
-       issl_session()
-               : sess(NULL)
-               , status(ISSL_NONE)
+       class Exception : public ModuleException
        {
-               outbound = false;
-               data_to_write = false;
-       }
-};
-
-static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
-{
-       /* XXX: This will allow self signed certificates.
-        * In the future if we want an option to not allow this,
-        * we can just return preverify_ok here, and openssl
-        * will boot off self-signed and invalid peer certs.
-        */
-       int ve = X509_STORE_CTX_get_error(ctx);
-
-       SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
+        public:
+               Exception(const std::string& reason)
+                       : ModuleException(reason) { }
+       };
 
-       return 1;
-}
+       class DHParams
+       {
+               DH* dh;
 
-class ModuleSSLOpenSSL : public Module
-{
-       issl_session* sessions;
+        public:
+               DHParams(const std::string& filename)
+               {
+                       BIO* dhpfile = BIO_new_file(filename.c_str(), "r");
+                       if (dhpfile == NULL)
+                               throw Exception("Couldn't open DH file " + filename);
 
-       SSL_CTX* ctx;
-       SSL_CTX* clictx;
+                       dh = PEM_read_bio_DHparams(dhpfile, NULL, NULL, NULL);
+                       BIO_free(dhpfile);
 
-       long ctx_options;
-       long clictx_options;
+                       if (!dh)
+                               throw Exception("Couldn't read DH params from file " + filename);
+               }
 
-       std::string sslports;
-       bool use_sha;
+               ~DHParams()
+               {
+                       DH_free(dh);
+               }
 
-       ServiceProvider iohook;
+               DH* get()
+               {
+                       return dh;
+               }
+       };
 
-       static void SetContextOptions(SSL_CTX* ctx, long defoptions, const std::string& ctxname, ConfigTag* tag)
+       class Context
        {
-               long setoptions = tag->getInt(ctxname + "setoptions");
-               // User-friendly config options for setting context options
-#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
-               if (tag->getBool("cipherserverpref"))
-                       setoptions |= SSL_OP_CIPHER_SERVER_PREFERENCE;
+               SSL_CTX* const ctx;
+               long ctx_options;
+
+        public:
+               Context(SSL_CTX* context)
+                       : ctx(context)
+               {
+                       // 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_SSLv3 | 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_COMPRESSION
-               if (!tag->getBool("compression", true))
-                       setoptions |= SSL_OP_NO_COMPRESSION;
+#ifdef SSL_OP_NO_TICKET
+                       opts |= SSL_OP_NO_TICKET;
 #endif
-               if (!tag->getBool("sslv3", true))
-                       setoptions |= SSL_OP_NO_SSLv3;
-               if (!tag->getBool("tlsv1", true))
-                       setoptions |= SSL_OP_NO_TLSv1;
 
-               long clearoptions = tag->getInt(ctxname + "clearoptions");
-               ServerInstance->Logs->Log("m_ssl_openssl", DEBUG, "Setting OpenSSL %s context options, default: %ld set: %ld clear: %ld", ctxname.c_str(), defoptions, setoptions, clearoptions);
+                       ctx_options = SSL_CTX_set_options(ctx, opts);
 
-               // 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, defoptions | setoptions);
-               long final = SSL_CTX_clear_options(ctx, clearoptions);
-               ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "OpenSSL %s context options: %ld", ctxname.c_str(), final);
-       }
-
-#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
-       void SetupECDH(ConfigTag* tag)
-       {
-               std::string curvename = tag->getString("ecdhcurve", "prime256v1");
-               if (curvename.empty())
-                       return;
+                       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);
+               }
 
-               int nid = OBJ_sn2nid(curvename.c_str());
-               if (nid == 0)
+               ~Context()
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unknown curve: \"%s\"", curvename.c_str());
-                       return;
+                       SSL_CTX_free(ctx);
                }
 
-               EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
-               if (!eckey)
+               bool SetDH(DHParams& dh)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unable to create EC key object");
-                       return;
+                       ERR_clear_error();
+                       return (SSL_CTX_set_tmp_dh(ctx, dh.get()) >= 0);
                }
 
-               ERR_clear_error();
-               if (SSL_CTX_set_tmp_ecdh(ctx, eckey) < 0)
+#ifndef OPENSSL_NO_ECDH
+               void SetECDH(const std::string& curvename)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set ECDH parameters");
-                       ERR_print_errors_cb(error_callback, this);
-               }
+                       int nid = OBJ_sn2nid(curvename.c_str());
+                       if (nid == 0)
+                               throw Exception("Unknown curve: " + curvename);
 
-               EC_KEY_free(eckey);
-       }
+                       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
 
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
-       static void SSLInfoCallback(const SSL* ssl, int where, int rc)
-       {
-               int fd = SSL_get_fd(const_cast<SSL*>(ssl));
-               issl_session& session = opensslmod->sessions[fd];
+               bool SetCiphers(const std::string& ciphers)
+               {
+                       ERR_clear_error();
+                       return SSL_CTX_set_cipher_list(ctx, ciphers.c_str());
+               }
 
-               if ((where & SSL_CB_HANDSHAKE_START) && (session.status == ISSL_OPEN))
+               bool SetCerts(const std::string& filename)
                {
-                       // The other side is trying to renegotiate, kill the connection and change status
-                       // to ISSL_NONE so CheckRenego() closes the session
-                       session.status = ISSL_NONE;
-                       ServerInstance->SE->Shutdown(fd, 2);
+                       ERR_clear_error();
+                       return SSL_CTX_use_certificate_chain_file(ctx, filename.c_str());
                }
-       }
 
-       bool CheckRenego(StreamSocket* sock, issl_session* session)
-       {
-               if (session->status != ISSL_NONE)
-                       return true;
+               bool SetPrivateKey(const std::string& filename)
+               {
+                       ERR_clear_error();
+                       return SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
+               }
 
-               ServerInstance->Logs->Log("m_ssl_openssl", DEBUG, "Session %p killed, attempted to renegotiate", (void*)session->sess);
-               CloseSession(session);
-               sock->SetError("Renegotiation is not allowed");
-               return false;
-       }
-#endif
+               bool SetCA(const std::string& filename)
+               {
+                       ERR_clear_error();
+                       return SSL_CTX_load_verify_locations(ctx, filename.c_str(), 0);
+               }
 
- public:
+               void SetCRL(const std::string& crlfile, const std::string& crlpath, const std::string& crlmode)
+               {
+                       if (crlfile.empty() && crlpath.empty())
+                               return;
 
-       ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK)
-       {
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
-               opensslmod = this;
-#endif
-               sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
+                       /* Set CRL mode */
+                       unsigned long crlflags = X509_V_FLAG_CRL_CHECK;
+                       if (stdalgo::string::equalsci(crlmode, "chain"))
+                       {
+                               crlflags |= X509_V_FLAG_CRL_CHECK_ALL;
+                       }
+                       else if (!stdalgo::string::equalsci(crlmode, "leaf"))
+                       {
+                               throw ModuleException("Unknown mode '" + crlmode + "'; expected either 'chain' (default) or 'leaf'");
+                       }
 
-               /* Global SSL library initialization*/
-               OPENSSL_init_ssl(0, NULL);
+                       /* Load CRL files */
+                       X509_STORE* store = SSL_CTX_get_cert_store(ctx);
+                       if (!store)
+                       {
+                               throw ModuleException("Unable to get X509_STORE from SSL context; this should never happen");
+                       }
+                       ERR_clear_error();
+                       if (!X509_STORE_load_locations(store,
+                               crlfile.empty() ? NULL : crlfile.c_str(),
+                               crlpath.empty() ? NULL : crlpath.c_str()))
+                       {
+                               int err = ERR_get_error();
+                               throw ModuleException("Unable to load CRL file '" + crlfile + "' or CRL path '" + crlpath + "': '" + (err ? ERR_error_string(err, NULL) : "unknown") + "'");
+                       }
 
-               /* Build our SSL contexts:
-                * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
-                */
-               ctx = SSL_CTX_new( SSLv23_server_method() );
-               clictx = SSL_CTX_new( SSLv23_client_method() );
+                       /* Set CRL mode */
+                       if (X509_STORE_set_flags(store, crlflags) != 1)
+                       {
+                               throw ModuleException("Unable to set X509 CRL flags");
+                       }
+               }
 
-               SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-               SSL_CTX_set_mode(clictx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 
-               SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
-               SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+               long GetDefaultContextOptions() const
+               {
+                       return ctx_options;
+               }
 
-               SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
-               SSL_CTX_set_session_cache_mode(clictx, SSL_SESS_CACHE_OFF);
+               long SetRawContextOptions(long setoptions, long clearoptions)
+               {
+                       // Clear everything
+                       SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
 
-               long opts = SSL_OP_NO_SSLv2 | 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
+                       // 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);
+               }
 
-               ctx_options = SSL_CTX_set_options(ctx, opts);
-               clictx_options = SSL_CTX_set_options(clictx, opts);
-       }
+               void SetVerifyCert()
+               {
+                       SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+               }
 
-       void init()
-       {
-               // Needs the flag as it ignores a plain /rehash
-               OnModuleRehash(NULL,"ssl");
-               Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnHookIO, I_OnUserConnect };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               ServerInstance->Modules->AddService(iohook);
-       }
+               SSL* CreateServerSession()
+               {
+                       SSL* sess = SSL_new(ctx);
+                       SSL_set_accept_state(sess); // Act as server
+                       return sess;
+               }
 
-       void OnHookIO(StreamSocket* user, ListenSocket* lsb)
-       {
-               if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
+               SSL* CreateClientSession()
                {
-                       /* Hook the user with our module */
-                       user->AddIOHook(this);
+                       SSL* sess = SSL_new(ctx);
+                       SSL_set_connect_state(sess); // Act as client
+                       return sess;
                }
-       }
+       };
 
-       void OnRehash(User* user)
+       class Profile
        {
-               sslports.clear();
+               /** Name of this profile
+                */
+               const std::string name;
+
+               /** DH parameters in use
+                */
+               DHParams dh;
 
-               ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
+               /** OpenSSL makes us have two contexts, one for servers and one for clients
+                */
+               Context ctx;
+               Context clictx;
+
+               /** Digest to use when generating fingerprints
+                */
+               const EVP_MD* digest;
+
+               /** Last error, set by error_callback()
+                */
+               std::string lasterr;
+
+               /** True if renegotiations are allowed, false if not
+                */
+               const bool allowrenego;
 
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
-               // Set the callback if we are not allowing renegotiations, unset it if we do
-               if (Conf->getBool("renegotiation", true))
+               /** Rough max size of records to send
+                */
+               const unsigned int outrecsize;
+
+               static int error_callback(const char* str, size_t len, void* u)
                {
-                       SSL_CTX_set_info_callback(ctx, NULL);
-                       SSL_CTX_set_info_callback(clictx, NULL);
+                       Profile* profile = reinterpret_cast<Profile*>(u);
+                       profile->lasterr = std::string(str, len - 1);
+                       return 0;
                }
-               else
+
+               /** 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)
                {
-                       SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
-                       SSL_CTX_set_info_callback(clictx, SSLInfoCallback);
-               }
+                       long setoptions = tag->getInt(ctxname + "setoptions", 0);
+                       long clearoptions = tag->getInt(ctxname + "clearoptions", 0);
+#ifdef SSL_OP_NO_COMPRESSION
+                       if (!tag->getBool("compression", false)) // Disable compression by default
+                               setoptions |= SSL_OP_NO_COMPRESSION;
 #endif
+                       // Disable TLSv1.0 by default.
+                       if (!tag->getBool("tlsv1", false))
+                               setoptions |= SSL_OP_NO_TLSv1;
 
-               if (Conf->getBool("showports", true))
-               {
-                       sslports = Conf->getString("advertisedports");
-                       if (!sslports.empty())
-                               return;
+                       if (!setoptions && !clearoptions)
+                               return; // Nothing to do
 
-                       for (size_t i = 0; i < ServerInstance->ports.size(); i++)
-                       {
-                               ListenSocket* port = ServerInstance->ports[i];
-                               if (port->bind_tag->getString("ssl") != "openssl")
-                                       continue;
+                       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", "dhparams.pem")))
+                       , ctx(SSL_CTX_new(SSLv23_server_method()))
+                       , clictx(SSL_CTX_new(SSLv23_client_method()))
+                       , allowrenego(tag->getBool("renegotiation")) // Disallow by default
+                       , outrecsize(tag->getUInt("outrecsize", 2048, 512, 16384))
+               {
+                       if ((!ctx.SetDH(dh)) || (!clictx.SetDH(dh)))
+                               throw Exception("Couldn't set DH parameters");
 
-                               const std::string& portid = port->bind_desc;
-                               ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %s", portid.c_str());
+                       std::string hash = tag->getString("hash", "md5");
+                       digest = EVP_get_digestbyname(hash.c_str());
+                       if (digest == NULL)
+                               throw Exception("Unknown hash type " + hash);
 
-                               if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
+                       std::string ciphers = tag->getString("ciphers");
+                       if (!ciphers.empty())
+                       {
+                               if ((!ctx.SetCiphers(ciphers)) || (!clictx.SetCiphers(ciphers)))
                                {
-                                       /*
-                                        * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
-                                        * the IP:port in ISUPPORT.
-                                        *
-                                        * We used to advertise all ports seperated by a ';' char that matched the above criteria,
-                                        * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
-                                        * To solve this by default we now only display the first IP:port found and let the user
-                                        * configure the exact value for the 005 token, if necessary.
-                                        */
-                                       sslports = portid;
-                                       break;
+                                       ERR_print_errors_cb(error_callback, this);
+                                       throw Exception("Can't set cipher list to \"" + ciphers + "\" " + lasterr);
                                }
                        }
-               }
-       }
-
-       void OnModuleRehash(User* user, const std::string &param)
-       {
-               if (param != "ssl")
-                       return;
-
-               std::string keyfile;
-               std::string certfile;
-               std::string cafile;
-               std::string dhfile;
-               OnRehash(user);
 
-               ConfigTag* conf = ServerInstance->Config->ConfValue("openssl");
+#ifndef OPENSSL_NO_ECDH
+                       std::string curvename = tag->getString("ecdhcurve", "prime256v1");
+                       if (!curvename.empty())
+                               ctx.SetECDH(curvename);
+#endif
 
-               cafile   = conf->getString("cafile", CONFIG_PATH "/ca.pem");
-               certfile = conf->getString("certfile", CONFIG_PATH "/cert.pem");
-               keyfile  = conf->getString("keyfile", CONFIG_PATH "/key.pem");
-               dhfile   = conf->getString("dhfile", CONFIG_PATH "/dhparams.pem");
-               std::string hash = conf->getString("hash", "md5");
-               if (hash != "sha1" && hash != "md5")
-                       throw ModuleException("Unknown hash type " + hash);
-               use_sha = (hash == "sha1");
+                       SetContextOptions("server", tag, ctx);
+                       SetContextOptions("client", tag, clictx);
 
-               if (conf->getBool("customcontextoptions"))
-               {
-                       SetContextOptions(ctx, ctx_options, "server", conf);
-                       SetContextOptions(clictx, clictx_options, "client", conf);
-               }
+                       /* Load our keys and certificates
+                        * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
+                        */
+                       std::string filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("certfile", "cert.pem"));
+                       if ((!ctx.SetCerts(filename)) || (!clictx.SetCerts(filename)))
+                       {
+                               ERR_print_errors_cb(error_callback, this);
+                               throw Exception("Can't read certificate file: " + lasterr);
+                       }
 
-               std::string ciphers = conf->getString("ciphers", "");
+                       filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("keyfile", "key.pem"));
+                       if ((!ctx.SetPrivateKey(filename)) || (!clictx.SetPrivateKey(filename)))
+                       {
+                               ERR_print_errors_cb(error_callback, this);
+                               throw Exception("Can't read key file: " + lasterr);
+                       }
 
-               if (!ciphers.empty())
-               {
-                       ERR_clear_error();
-                       if ((!SSL_CTX_set_cipher_list(ctx, ciphers.c_str())) || (!SSL_CTX_set_cipher_list(clictx, ciphers.c_str())))
+                       // Load the CAs we trust
+                       filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("cafile", "ca.pem"));
+                       if ((!ctx.SetCA(filename)) || (!clictx.SetCA(filename)))
                        {
-                               ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't set cipher list to %s.", ciphers.c_str());
                                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());
                        }
+
+                       // Load the CRLs.
+                       std::string crlfile  = tag->getString("crlfile");
+                       std::string crlpath  = tag->getString("crlpath");
+                       std::string crlmode  = tag->getString("crlmode", "chain");
+                       ctx.SetCRL(crlfile, crlpath, crlmode);
+
+                       clictx.SetVerifyCert();
+                       if (tag->getBool("requestclientcert", true))
+                               ctx.SetVerifyCert();
                }
 
-               /* Load our keys and certificates
-                * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
-                */
-               ERR_clear_error();
-               if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
+               const std::string& GetName() const { return name; }
+               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)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
-                       ERR_print_errors_cb(error_callback, this);
+                       BIO_set_init(bio, 1);
+                       return 1;
                }
 
-               ERR_clear_error();
-               if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM)))
+               static int destroy(BIO* bio)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
-                       ERR_print_errors_cb(error_callback, this);
+                       // 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;
                }
 
-               /* Load the CAs we trust*/
-               ERR_clear_error();
-               if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
+               static long ctrl(BIO* bio, int cmd, long num, void* ptr)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: 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", cafile.c_str(), strerror(errno));
-                       ERR_print_errors_cb(error_callback, this);
+                       if (cmd == BIO_CTRL_FLUSH)
+                               return 1;
+                       return 0;
                }
 
-#ifdef _WIN32
-               BIO* dhpfile = BIO_new_file(dhfile.c_str(), "r");
-#else
-               FILE* dhpfile = fopen(dhfile.c_str(), "r");
-#endif
-               DH* ret;
+               static int read(BIO* bio, char* buf, int len);
+               static int write(BIO* bio, const char* buf, int len);
 
-               if (dhpfile == NULL)
+#ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
+               static BIO_METHOD* alloc()
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
-                       throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
+                       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;
                }
-               else
-               {
-#ifdef _WIN32
-                       ret = PEM_read_bio_DHparams(dhpfile, NULL, NULL, NULL);
-                       BIO_free(dhpfile);
+#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
-                       ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
+static BIO_METHOD* biomethods;
 #endif
 
-                       ERR_clear_error();
-                       if (ret)
+static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
+{
+       /* XXX: This will allow self signed certificates.
+        * In the future if we want an option to not allow this,
+        * we can just return preverify_ok here, and openssl
+        * will boot off self-signed and invalid peer certs.
+        */
+       int ve = X509_STORE_CTX_get_error(ctx);
+
+       SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
+
+       return 1;
+}
+
+class OpenSSLIOHook : public SSLIOHook
+{
+ private:
+       SSL* sess;
+       issl_status status;
+       bool data_to_write;
+
+       // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
+       int Handshake(StreamSocket* user)
+       {
+               ERR_clear_error();
+               int ret = SSL_do_handshake(sess);
+               if (ret < 0)
+               {
+                       int err = SSL_get_error(sess, ret);
+
+                       if (err == SSL_ERROR_WANT_READ)
                        {
-                               if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
-                               {
-                                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
-                                       ERR_print_errors_cb(error_callback, this);
-                               }
-                               DH_free(ret);
+                               SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+                               this->status = ISSL_HANDSHAKING;
+                               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 0;
                        }
                        else
                        {
-                               ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s.", dhfile.c_str());
+                               CloseSession();
+                               return -1;
                        }
                }
+               else if (ret > 0)
+               {
+                       // Handshake complete.
+                       VerifyCertificate();
 
-#ifndef _WIN32
-               fclose(dhpfile);
-#endif
-
-#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
-               SetupECDH(conf);
-#endif
-       }
-
-       void On005Numeric(std::string &output)
-       {
-               if (!sslports.empty())
-                       output.append(" SSL=" + sslports);
-       }
+                       status = ISSL_OPEN;
 
-       ~ModuleSSLOpenSSL()
-       {
-               SSL_CTX_free(ctx);
-               SSL_CTX_free(clictx);
-               delete[] sessions;
-       }
+                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 
-       void OnUserConnect(LocalUser* user)
-       {
-               if (user->eh.GetIOHook() == this)
+                       return 1;
+               }
+               else if (ret == 0)
                {
-                       if (sessions[user->eh.GetFd()].sess)
-                       {
-                               if (!sessions[user->eh.GetFd()].cert->fingerprint.empty())
-                                       user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
-                                               " and your SSL fingerprint is %s", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess), sessions[user->eh.GetFd()].cert->fingerprint.c_str());
-                               else
-                                       user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess));
-                       }
+                       CloseSession();
                }
+               return -1;
        }
 
-       void OnCleanup(int target_type, void* item)
+       void CloseSession()
        {
-               if (target_type == TYPE_USER)
+               if (sess)
                {
-                       LocalUser* user = IS_LOCAL((User*)item);
-
-                       if (user && user->eh.GetIOHook() == 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.
-                               ServerInstance->Users->QuitUser(user, "SSL module unloading");
-                       }
+                       SSL_shutdown(sess);
+                       SSL_free(sess);
                }
+               sess = NULL;
+               certificate = NULL;
+               status = ISSL_NONE;
        }
 
-       Version GetVersion()
+       void VerifyCertificate()
        {
-               return Version("Provides SSL support for clients", VF_VENDOR);
-       }
+               X509* cert;
+               ssl_cert* certinfo = new ssl_cert;
+               this->certificate = certinfo;
+               unsigned int n;
+               unsigned char md[EVP_MAX_MD_SIZE];
 
-       void OnRequest(Request& request)
-       {
-               if (strcmp("GET_SSL_CERT", request.id) == 0)
+               cert = SSL_get_peer_certificate(sess);
+
+               if (!cert)
                {
-                       SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
-                       int fd = req.sock->GetFd();
-                       issl_session* session = &sessions[fd];
+                       certinfo->error = "Could not get peer certificate: "+std::string(get_error());
+                       return;
+               }
+
+               certinfo->invalid = (SSL_get_verify_result(sess) != X509_V_OK);
 
-                       req.cert = session->cert;
+               if (!SelfSigned)
+               {
+                       certinfo->unknownsigner = false;
+                       certinfo->trusted = true;
                }
-               else if (!strcmp("GET_RAW_SSL_SESSION", request.id))
+               else
                {
-                       SSLRawSessionRequest& req = static_cast<SSLRawSessionRequest&>(request);
-                       if ((req.fd >= 0) && (req.fd < ServerInstance->SE->GetMaxFds()))
-                               req.data = reinterpret_cast<void*>(sessions[req.fd].sess);
+                       certinfo->unknownsigner = true;
+                       certinfo->trusted = false;
                }
-       }
 
-       void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
-       {
-               int fd = user->GetFd();
-
-               issl_session* session = &sessions[fd];
+               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();
 
-               session->sess = SSL_new(ctx);
-               session->status = ISSL_NONE;
-               session->outbound = false;
-               session->data_to_write = false;
+               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 (session->sess == NULL)
-                       return;
+               if (!X509_digest(cert, GetProfile().GetDigest(), md, &n))
+               {
+                       certinfo->error = "Out of memory generating fingerprint";
+               }
+               else
+               {
+                       certinfo->fingerprint = BinToHex(md, n);
+               }
 
-               if (SSL_set_fd(session->sess, fd) == 0)
+               if ((ASN1_UTCTIME_cmp_time_t(X509_getm_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_getm_notBefore(cert), ServerInstance->Time()) == 0))
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
-                       return;
+                       certinfo->error = "Not activated, or expired certificate";
                }
 
-               Handshake(user, session);
+               X509_free(cert);
        }
 
-       void OnStreamSocketConnect(StreamSocket* user)
+       void SSLInfoCallback(int where, int rc)
        {
-               int fd = user->GetFd();
-               /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
-               if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() -1))
-                       return;
+               if ((where & SSL_CB_HANDSHAKE_START) && (status == ISSL_OPEN))
+               {
+                       if (GetProfile().AllowRenegotiation())
+                               return;
 
-               issl_session* session = &sessions[fd];
+                       // 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);
+               }
+       }
 
-               session->sess = SSL_new(clictx);
-               session->status = ISSL_NONE;
-               session->outbound = true;
-               session->data_to_write = false;
+       bool CheckRenego(StreamSocket* sock)
+       {
+               if (status != ISSL_NONE)
+                       return true;
 
-               if (session->sess == NULL)
-                       return;
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Session %p killed, attempted to renegotiate", (void*)sess);
+               CloseSession();
+               sock->SetError("Renegotiation is not allowed");
+               return false;
+       }
 
-               if (SSL_set_fd(session->sess, fd) == 0)
+       // 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)
                {
-                       ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
-                       return;
+                       // The handshake isn't finished, try to finish it
+                       return Handshake(sock);
                }
 
-               Handshake(user, session);
+               CloseSession();
+               return -1;
        }
 
-       void OnStreamSocketClose(StreamSocket* user)
+       // Calls our private SSLInfoCallback()
+       friend void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
+
+ public:
+       OpenSSLIOHook(IOHookProvider* hookprov, StreamSocket* sock, SSL* session)
+               : SSLIOHook(hookprov)
+               , sess(session)
+               , status(ISSL_NONE)
+               , data_to_write(false)
        {
-               int fd = user->GetFd();
-               /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
-               if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
-                       return;
+               // 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);
 
-               CloseSession(&sessions[fd]);
+               SSL_set_ex_data(sess, exdataindex, this);
+               sock->AddIOHook(this);
+               Handshake(sock);
        }
 
-       int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
+       void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
        {
-               int fd = user->GetFd();
-               /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
-               if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
-                       return -1;
-
-               issl_session* session = &sessions[fd];
-
-               if (!session->sess)
-               {
-                       CloseSession(session);
-                       return -1;
-               }
-
-               if (session->status == ISSL_HANDSHAKING)
-               {
-                       // The handshake isn't finished and it wants to read, try to finish it.
-                       if (!Handshake(user, session))
-                       {
-                               // Couldn't resume handshake.
-                               if (session->status == ISSL_NONE)
-                                       return -1;
-                               return 0;
-                       }
-               }
+               CloseSession();
+       }
 
-               // If we resumed the handshake then session->status will be ISSL_OPEN
+       int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
+       {
+               // Finish handshake if needed
+               int prepret = PrepareIO(user);
+               if (prepret <= 0)
+                       return prepret;
 
-               if (session->status == ISSL_OPEN)
+               // If we resumed the handshake then this->status will be ISSL_OPEN
                {
                        ERR_clear_error();
                        char* buffer = ServerInstance->GetReadBuffer();
                        size_t bufsiz = ServerInstance->Config->NetBufferSize;
-                       int ret = SSL_read(session->sess, buffer, bufsiz);
+                       int ret = SSL_read(sess, buffer, bufsiz);
 
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
-                       if (!CheckRenego(user, session))
+                       if (!CheckRenego(user))
                                return -1;
-#endif
 
                        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(session->sess) > 0)
+                               if (SSL_pending(sess) > 0)
                                        mask |= FD_ADD_TRIAL_READ;
-                               if (session->data_to_write)
+                               if (data_to_write)
                                        mask |= FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE;
                                if (mask != 0)
-                                       ServerInstance->SE->ChangeEventMask(user, mask);
+                                       SocketEngine::ChangeEventMask(user, mask);
                                return 1;
                        }
                        else if (ret == 0)
                        {
                                // Client closed connection.
-                               CloseSession(session);
+                               CloseSession();
                                user->SetError("Connection closed");
                                return -1;
                        }
-                       else if (ret < 0)
+                       else // if (ret < 0)
                        {
-                               int err = SSL_get_error(session->sess, ret);
+                               int err = SSL_get_error(sess, ret);
 
                                if (err == SSL_ERROR_WANT_READ)
                                {
-                                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
+                                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ);
                                        return 0;
                                }
                                else if (err == SSL_ERROR_WANT_WRITE)
                                {
-                                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+                                       SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
                                        return 0;
                                }
                                else
                                {
-                                       CloseSession(session);
+                                       CloseSession();
                                        return -1;
                                }
                        }
                }
-
-               return 0;
        }
 
-       int OnStreamSocketWrite(StreamSocket* user, std::string& buffer)
+       int OnStreamSocketWrite(StreamSocket* user, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
        {
-               int fd = user->GetFd();
-
-               issl_session* session = &sessions[fd];
-
-               if (!session->sess)
-               {
-                       CloseSession(session);
-                       return -1;
-               }
-
-               session->data_to_write = true;
+               // Finish handshake if needed
+               int prepret = PrepareIO(user);
+               if (prepret <= 0)
+                       return prepret;
 
-               if (session->status == ISSL_HANDSHAKING)
-               {
-                       if (!Handshake(user, session))
-                       {
-                               // Couldn't resume handshake.
-                               if (session->status == ISSL_NONE)
-                                       return -1;
-                               return 0;
-                       }
-               }
+               data_to_write = true;
 
-               if (session->status == ISSL_OPEN)
+               // Session is ready for transferring application data
+               while (!sendq.empty())
                {
                        ERR_clear_error();
-                       int ret = SSL_write(session->sess, buffer.data(), buffer.size());
+                       FlattenSendQueue(sendq, GetProfile().GetOutgoingRecordSize());
+                       const StreamSocket::SendQueue::Element& buffer = sendq.front();
+                       int ret = SSL_write(sess, buffer.data(), buffer.size());
 
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
-                       if (!CheckRenego(user, session))
+                       if (!CheckRenego(user))
                                return -1;
-#endif
 
                        if (ret == (int)buffer.length())
                        {
-                               session->data_to_write = false;
-                               ServerInstance->SE->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);
-                               ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+                               sendq.erase_front(ret);
+                               SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
                                return 0;
                        }
                        else if (ret == 0)
                        {
-                               CloseSession(session);
+                               CloseSession();
                                return -1;
                        }
-                       else if (ret < 0)
+                       else // if (ret < 0)
                        {
-                               int err = SSL_get_error(session->sess, ret);
+                               int err = SSL_get_error(sess, ret);
 
                                if (err == SSL_ERROR_WANT_WRITE)
                                {
-                                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+                                       SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
                                        return 0;
                                }
                                else if (err == SSL_ERROR_WANT_READ)
                                {
-                                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
+                                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ);
                                        return 0;
                                }
                                else
                                {
-                                       CloseSession(session);
+                                       CloseSession();
                                        return -1;
                                }
                        }
                }
-               return 0;
+
+               data_to_write = false;
+               SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+               return 1;
        }
 
-       bool Handshake(StreamSocket* user, issl_session* session)
+       void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
        {
-               int ret;
+               if (!IsHandshakeDone())
+                       return;
+               out.append(SSL_get_version(sess)).push_back('-');
+               out.append(SSL_get_cipher(sess));
+       }
 
-               ERR_clear_error();
-               if (session->outbound)
-                       ret = SSL_connect(session->sess);
-               else
-                       ret = SSL_accept(session->sess);
+       bool GetServerName(std::string& out) const CXX11_OVERRIDE
+       {
+               const char* name = SSL_get_servername(sess, TLSEXT_NAMETYPE_host_name);
+               if (!name)
+                       return false;
 
-               if (ret < 0)
+               out.append(name);
+               return true;
+       }
+
+       bool IsHandshakeDone() const { return (status == ISSL_OPEN); }
+       OpenSSL::Profile& GetProfile();
+};
+
+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 IOHookProvider
+{
+       OpenSSL::Profile profile;
+
+ public:
+       OpenSSLIOHookProvider(Module* mod, const std::string& profilename, ConfigTag* tag)
+               : IOHookProvider(mod, "ssl/" + profilename, IOHookProvider::IOH_SSL)
+               , profile(profilename, tag)
+       {
+               ServerInstance->Modules->AddService(*this);
+       }
+
+       ~OpenSSLIOHookProvider()
+       {
+               ServerInstance->Modules->DelService(*this);
+       }
+
+       void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+       {
+               new OpenSSLIOHook(this, sock, profile.CreateServerSession());
+       }
+
+       void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+       {
+               new OpenSSLIOHook(this, sock, profile.CreateClientSession());
+       }
+
+       OpenSSL::Profile& GetProfile() { return profile; }
+};
+
+OpenSSL::Profile& OpenSSLIOHook::GetProfile()
+{
+       IOHookProvider* hookprov = prov;
+       return static_cast<OpenSSLIOHookProvider*>(hookprov)->GetProfile();
+}
+
+class ModuleSSLOpenSSL : public Module
+{
+       typedef std::vector<reference<OpenSSLIOHookProvider> > ProfileList;
+
+       ProfileList profiles;
+
+       void ReadProfiles()
+       {
+               ProfileList newprofiles;
+               ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
+               if (tags.first == tags.second)
                {
-                       int err = SSL_get_error(session->sess, ret);
+                       // Create a default profile named "openssl"
+                       const std::string defname = "openssl";
+                       ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found, using settings from the <openssl> tag");
 
-                       if (err == SSL_ERROR_WANT_READ)
+                       try
                        {
-                               ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
-                               session->status = ISSL_HANDSHAKING;
-                               return true;
+                               newprofiles.push_back(new OpenSSLIOHookProvider(this, defname, tag));
                        }
-                       else if (err == SSL_ERROR_WANT_WRITE)
-                       {
-                               ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
-                               session->status = ISSL_HANDSHAKING;
-                               return true;
-                       }
-                       else
+                       catch (OpenSSL::Exception& ex)
                        {
-                               CloseSession(session);
+                               throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
                        }
-
-                       return false;
                }
-               else if (ret > 0)
+
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
                {
-                       // Handshake complete.
-                       VerifyCertificate(session, user);
+                       ConfigTag* tag = i->second;
+                       if (!stdalgo::string::equalsci(tag->getString("provider"), "openssl"))
+                               continue;
 
-                       session->status = ISSL_OPEN;
+                       std::string name = tag->getString("name");
+                       if (name.empty())
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
+                               continue;
+                       }
 
-                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+                       reference<OpenSSLIOHookProvider> prov;
+                       try
+                       {
+                               prov = new OpenSSLIOHookProvider(this, name, tag);
+                       }
+                       catch (CoreException& ex)
+                       {
+                               throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+                       }
 
-                       return true;
+                       newprofiles.push_back(prov);
                }
-               else if (ret == 0)
+
+               for (ProfileList::iterator i = profiles.begin(); i != profiles.end(); ++i)
                {
-                       CloseSession(session);
+                       OpenSSLIOHookProvider& prov = **i;
+                       ServerInstance->Modules.DelService(prov);
                }
-               return false;
+
+               profiles.swap(newprofiles);
        }
 
-       void CloseSession(issl_session* session)
+ public:
+       ModuleSSLOpenSSL()
        {
-               if (session->sess)
-               {
-                       SSL_shutdown(session->sess);
-                       SSL_free(session->sess);
-               }
+               // Initialize OpenSSL
+               OPENSSL_init_ssl(0, NULL);
+#ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
+               biomethods = OpenSSL::BIOMethod::alloc();
+       }
 
-               session->sess = NULL;
-               session->status = ISSL_NONE;
-               session->cert = NULL;
+       ~ModuleSSLOpenSSL()
+       {
+               BIO_meth_free(biomethods);
+#endif
        }
 
-       void VerifyCertificate(issl_session* session, StreamSocket* user)
+       void init() CXX11_OVERRIDE
        {
-               if (!session->sess || !user)
-                       return;
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "OpenSSL lib version \"%s\" module was compiled for \"" OPENSSL_VERSION_TEXT "\"", OpenSSL_version(OPENSSL_VERSION));
 
-               X509* cert;
-               ssl_cert* certinfo = new ssl_cert;
-               session->cert = certinfo;
-               unsigned int n;
-               unsigned char md[EVP_MAX_MD_SIZE];
-               const EVP_MD *digest = use_sha ? EVP_sha1() : EVP_md5();
+               // 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");
 
-               cert = SSL_get_peer_certificate((SSL*)session->sess);
+               ReadProfiles();
+       }
 
-               if (!cert)
-               {
-                       certinfo->error = "Could not get peer certificate: "+std::string(get_error());
+       void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
+       {
+               if (param != "ssl")
                        return;
-               }
-
-               certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
 
-               if (!SelfSigned)
+               try
                {
-                       certinfo->unknownsigner = false;
-                       certinfo->trusted = true;
+                       ReadProfiles();
                }
-               else
+               catch (ModuleException& ex)
                {
-                       certinfo->unknownsigner = true;
-                       certinfo->trusted = false;
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
                }
+       }
 
-               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, digest, md, &n))
-               {
-                       certinfo->error = "Out of memory generating fingerprint";
-               }
-               else
+       void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+       {
+               if (type == ExtensionItem::EXT_USER)
                {
-                       certinfo->fingerprint = irc::hex(md, n);
-               }
+                       LocalUser* user = IS_LOCAL((User*)item);
 
-               if ((ASN1_UTCTIME_cmp_time_t(X509_getm_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_getm_notBefore(cert), ServerInstance->Time()) == 0))
-               {
-                       certinfo->error = "Not activated, or expired certificate";
+                       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.
+                               ServerInstance->Users->QuitUser(user, "SSL module unloading");
+                       }
                }
+       }
 
-               X509_free(cert);
+       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;
        }
-};
 
-static int error_callback(const char *str, size_t len, void *u)
-{
-       ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "SSL error: " + std::string(str, len - 1));
-
-       //
-       // XXX: Remove this line, it causes valgrind warnings...
-       //
-       // MD_update(&m, buf, j);
-       //
-       //
-       // ... ONLY JOKING! :-)
-       //
-
-       return 0;
-}
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides SSL support via OpenSSL", VF_VENDOR);
+       }
+};
 
 MODULE_INIT(ModuleSSLOpenSSL)
diff --git a/src/modules/extra/m_sslrehashsignal.cpp b/src/modules/extra/m_sslrehashsignal.cpp
new file mode 100644 (file)
index 0000000..fea3232
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2018 Peter Powell <petpow@saberuk.com>
+ *   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"
+
+static volatile sig_atomic_t signaled;
+
+class ModuleSSLRehashSignal : public Module
+{
+ private:
+       static void SignalHandler(int)
+       {
+               signaled = 1;
+       }
+
+ public:
+       ~ModuleSSLRehashSignal()
+       {
+               signal(SIGUSR1, SIG_DFL);
+       }
+
+       void init()
+       {
+               signal(SIGUSR1, SignalHandler);
+       }
+
+       void OnBackgroundTimer(time_t)
+       {
+               if (!signaled)
+                       return;
+
+               const std::string feedbackmsg = "Got SIGUSR1, reloading SSL credentials";
+               ServerInstance->SNO->WriteGlobalSno('a', feedbackmsg);
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, feedbackmsg);
+
+               const std::string str = "ssl";
+               FOREACH_MOD(OnModuleRehash, (NULL, str));
+               signaled = 0;
+       }
+
+       Version GetVersion()
+       {
+               return Version("Reloads SSL credentials on SIGUSR1", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleSSLRehashSignal)
diff --git a/src/modules/hash.h b/src/modules/hash.h
deleted file mode 100644 (file)
index f7bf85e..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 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
- * 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/>.
- */
-
-
-#ifndef HASH_H
-#define HASH_H
-
-#include "modules.h"
-
-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)
-       {
-               return BinToHex(sum(data));
-       }
-
-       inline std::string b64sum(const std::string& data)
-       {
-               return BinToBase64(sum(data), NULL, 0);
-       }
-
-       /** Allows the IVs for the hash to be specified. As the choice of initial IV is
-        * important for the security of a hash, this should not be used except to
-        * maintain backwards compatability. This also allows you to change the hex
-        * sequence from its default of "0123456789abcdef", which does not improve the
-        * strength of the output, but helps confuse those attempting to implement it.
-        *
-        * Example:
-        * \code
-        * unsigned int iv[] = { 0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0xCCCCCCCC };
-        * std::string result = Hash.sumIV(iv, "fedcba9876543210", "data");
-        * \endcode
-        */
-       virtual std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata) = 0;
-
-       /** 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;
-               kbuf.resize(block_size);
-
-               for (size_t n = 0; n < block_size; n++)
-               {
-                       hmac1.push_back(static_cast<char>(kbuf[n] ^ 0x5C));
-                       hmac2.push_back(static_cast<char>(kbuf[n] ^ 0x36));
-               }
-               hmac2.append(msg);
-               hmac1.append(sum(hmac2));
-               return sum(hmac1);
-       }
-};
-
-#endif
-
diff --git a/src/modules/httpd.h b/src/modules/httpd.h
deleted file mode 100644 (file)
index 56fd22d..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- *   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   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/>.
- */
-
-
-#include "base.h"
-
-#ifndef HTTPD_H
-#define HTTPD_H
-
-#include <string>
-#include <sstream>
-#include <map>
-
-/** A modifyable list of HTTP header fields
- */
-class HTTPHeaders
-{
- protected:
-       std::map<std::string,std::string> headers;
- public:
-
-       /** Set the value of a header
-        * Sets the value of the named header. If the header is already present, it will be replaced
-        */
-       void SetHeader(const std::string &name, const std::string &data)
-       {
-               headers[name] = data;
-       }
-
-       /** Set the value of a header, only if it doesn't exist already
-        * Sets the value of the named header. If the header is already present, it will NOT be updated
-        */
-       void CreateHeader(const std::string &name, const std::string &data)
-       {
-               if (!IsSet(name))
-                       SetHeader(name, data);
-       }
-
-       /** Remove the named header
-        */
-       void RemoveHeader(const std::string &name)
-       {
-               headers.erase(name);
-       }
-
-       /** Remove all headers
-        */
-       void Clear()
-       {
-               headers.clear();
-       }
-
-       /** Get the value of a header
-        * @return The value of the header, or an empty string
-        */
-       std::string GetHeader(const std::string &name)
-       {
-               std::map<std::string,std::string>::iterator it = headers.find(name);
-               if (it == headers.end())
-                       return std::string();
-
-               return it->second;
-       }
-
-       /** Check if the given header is specified
-        * @return true if the header is specified
-        */
-       bool IsSet(const std::string &name)
-       {
-               std::map<std::string,std::string>::iterator it = headers.find(name);
-               return (it != headers.end());
-       }
-
-       /** Get all headers, formatted by the HTTP protocol
-        * @return Returns all headers, formatted according to the HTTP protocol. There is no request terminator at the end
-        */
-       std::string GetFormattedHeaders()
-       {
-               std::string re;
-
-               for (std::map<std::string,std::string>::iterator i = headers.begin(); i != headers.end(); i++)
-                       re += i->first + ": " + i->second + "\r\n";
-
-               return re;
-       }
-};
-
-class HttpServerSocket;
-
-/** This class represents a HTTP request.
- */
-class HTTPRequest : public Event
-{
- protected:
-       std::string type;
-       std::string document;
-       std::string ipaddr;
-       std::string postdata;
-
- public:
-
-       HTTPHeaders *headers;
-       int errorcode;
-
-       /** A socket pointer, which you must return in your HTTPDocument class
-        * if you reply to this request.
-        */
-       HttpServerSocket* sock;
-
-       /** Initialize HTTPRequest.
-        * This constructor is called by m_httpd.so to initialize the class.
-        * @param request_type The request type, e.g. GET, POST, HEAD
-        * @param uri The URI, e.g. /page
-        * @param hdr The headers sent with the request
-        * @param opaque An opaque pointer used internally by m_httpd, which you must pass back to the module in your reply.
-        * @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,
-               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)
-       {
-       }
-
-       /** Get the post data (request content).
-        * All post data will be returned, including carriage returns and linefeeds.
-        * @return The postdata
-        */
-       std::string& GetPostData()
-       {
-               return postdata;
-       }
-
-       /** Get the request type.
-        * Any request type can be intercepted, even ones which are invalid in the HTTP/1.1 spec.
-        * @return The request type, e.g. GET, POST, HEAD
-        */
-       std::string& GetType()
-       {
-               return type;
-       }
-
-       /** Get URI.
-        * The URI string (URL minus hostname and scheme) will be provided by this function.
-        * @return The URI being requested
-        */
-       std::string& GetURI()
-       {
-               return document;
-       }
-
-       /** Get IP address of requester.
-        * The requesting system's ip address will be returned.
-        * @return The IP address as a string
-        */
-       std::string& GetIP()
-       {
-               return ipaddr;
-       }
-};
-
-/** You must return a HTTPDocument to the httpd module by using the Request class.
- * When you initialize this class you may initialize it with all components required to
- * form a valid HTTP response, including document data, headers, and a response code.
- */
-class HTTPDocumentResponse : public Request
-{
- public:
-       std::stringstream* document;
-       int responsecode;
-       HTTPHeaders headers;
-       HTTPRequest& src;
-
-       /** Initialize a HTTPRequest ready for sending to m_httpd.so.
-        * @param opaque The socket pointer you obtained from the HTTPRequest at an earlier time
-        * @param doc A stringstream containing the document body
-        * @param response A valid HTTP/1.0 or HTTP/1.1 response code. The response text will be determined for you
-        * based upon the response code.
-        * @param extra Any extra headers to include with the defaults, seperated by carriage return and linefeed.
-        */
-       HTTPDocumentResponse(Module* me, HTTPRequest& req, std::stringstream* doc, int response)
-               : Request(me, req.source, "HTTP-DOC"), document(doc), responsecode(response), src(req)
-       {
-       }
-};
-
-#endif
-
index 1e8f7117669083465047b6094584acb5233e5628..963dd5f16513276be27265b63829335f2e03587b 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides the ability to abbreviate commands a-la BBC BASIC keywords. */
+enum
+{
+       // InspIRCd-specific.
+       ERR_AMBIGUOUSCOMMAND = 420
+};
 
 class ModuleAbbreviation : public Module
 {
  public:
-       void init()
-       {
-               ServerInstance->Modules->Attach(I_OnPreCommand, this);
-       }
-
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_FIRST);
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides the ability to abbreviate commands a-la BBC BASIC keywords.",VF_VENDOR);
+               return Version("Provides the ability to abbreviate commands a-la BBC BASIC keywords", VF_VENDOR);
        }
 
-       virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
        {
                /* Command is already validated, has a length of 0, or last character is not a . */
                if (validated || command.empty() || *command.rbegin() != '.')
                        return MOD_RES_PASSTHRU;
 
-               /* Whack the . off the end */
-               command.erase(command.end() - 1);
-
                /* Look for any command that starts with the same characters, if it does, replace the command string with it */
-               size_t clen = command.length();
+               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 (n->first.length() < clen)
-                               continue;
-
-                       if (command == n->first.substr(0, clen))
+                       if (!command.compare(0, clen, n->first, 0, clen))
                        {
                                if (matchlist.length() > 450)
                                {
-                                       user->WriteNumeric(420, "%s :Ambiguous abbreviation and too many possible matches.", user->nick.c_str());
+                                       user->WriteNumeric(ERR_AMBIGUOUSCOMMAND, "Ambiguous abbreviation and too many possible matches.");
                                        return MOD_RES_DENY;
                                }
 
@@ -79,16 +73,11 @@ class ModuleAbbreviation : public Module
                /* Ambiguous command, list the matches */
                if (!matchlist.empty())
                {
-                       user->WriteNumeric(420, "%s :Ambiguous abbreviation, possible matches: %s%s", user->nick.c_str(), foundcommand.c_str(), matchlist.c_str());
+                       user->WriteNumeric(ERR_AMBIGUOUSCOMMAND, InspIRCd::Format("Ambiguous abbreviation, possible matches: %s%s", foundcommand.c_str(), matchlist.c_str()));
                        return MOD_RES_DENY;
                }
 
-               if (foundcommand.empty())
-               {
-                       /* No match, we have to put the . back again so that the invalid command numeric looks correct. */
-                       command += '.';
-               }
-               else
+               if (!foundcommand.empty())
                {
                        command = foundcommand;
                }
index 32fc80b64d5427638a4a7919b2d437fa34afdec3..f6aa5bd0229e8640ad0a07c04546146149689575 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides aliases of commands. */
-
 /** An alias definition
  */
 class Alias
 {
  public:
        /** The text of the alias command */
-       irc::string AliasedCommand;
+       std::string AliasedCommand;
 
        /** Text to replace with */
        std::string ReplaceFormat;
@@ -44,9 +42,6 @@ class Alias
        /** Requires oper? */
        bool OperOnly;
 
-       /* is case sensitive params */
-       bool CaseSensitive;
-
        /* whether or not it may be executed via fantasy (default OFF) */
        bool ChannelCommand;
 
@@ -59,62 +54,66 @@ class Alias
 
 class ModuleAlias : public Module
 {
- private:
-
-       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
-   */
-       std::multimap<irc::string, Alias> Aliases;
+        */
+       typedef insp::flat_multimap<std::string, Alias, irc::insensitive_swo> AliasMap;
+
+       AliasMap Aliases;
 
        /* whether or not +B users are allowed to use fantasy commands */
        bool AllowBots;
+       UserModeReference botmode;
 
-       virtual void ReadAliases()
-       {
-               ConfigTag* fantasy = ServerInstance->Config->ConfValue("fantasy");
-               AllowBots = fantasy->getBool("allowbots", false);
-               std::string fpre = fantasy->getString("prefix", "!");
-               fprefix = fpre.empty() ? '!' : fpre[0];
+       // Whether we are actively executing an alias.
+       bool active;
 
-               Aliases.clear();
+ public:
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               AliasMap newAliases;
                ConfigTagList tags = ServerInstance->Config->ConfTags("alias");
                for(ConfigIter i = tags.first; i != tags.second; ++i)
                {
                        ConfigTag* tag = i->second;
                        Alias a;
-                       std::string aliastext = tag->getString("text");
-                       a.AliasedCommand = aliastext.c_str();
+                       a.AliasedCommand = tag->getString("text");
+                       if (a.AliasedCommand.empty())
+                               throw ModuleException("<alias:text> is empty! at " + tag->getTagLocation());
+
                        tag->readString("replace", a.ReplaceFormat, true);
+                       if (a.ReplaceFormat.empty())
+                               throw ModuleException("<alias:replace> is empty! at " + tag->getTagLocation());
+
                        a.RequiredNick = tag->getString("requires");
                        a.ULineOnly = tag->getBool("uline");
                        a.ChannelCommand = tag->getBool("channelcommand", false);
                        a.UserCommand = tag->getBool("usercommand", true);
                        a.OperOnly = tag->getBool("operonly");
                        a.format = tag->getString("format");
-                       a.CaseSensitive = tag->getBool("matchcase");
-                       Aliases.insert(std::make_pair(a.AliasedCommand, a));
-               }
-       }
 
- public:
+                       std::transform(a.AliasedCommand.begin(), a.AliasedCommand.end(), a.AliasedCommand.begin(), ::toupper);
+                       newAliases.insert(std::make_pair(a.AliasedCommand, a));
+               }
 
-       void init()
-       {
-               ReadAliases();
-               Implementation eventlist[] = { I_OnPreCommand, I_OnRehash, I_OnUserMessage };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               ConfigTag* fantasy = ServerInstance->Config->ConfValue("fantasy");
+               AllowBots = fantasy->getBool("allowbots", false);
+               fprefix = fantasy->getString("prefix", "!", 1, ServerInstance->Config->Limits.MaxLine);
+               Aliases.swap(newAliases);
        }
 
-       virtual ~ModuleAlias()
+       ModuleAlias()
+               : botmode(this, "bot")
+               , active(false)
        {
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides aliases of commands.", VF_VENDOR);
+               return Version("Provides aliases of commands", VF_VENDOR);
        }
 
        std::string GetVar(std::string varname, const std::string &original_line)
@@ -142,10 +141,22 @@ class ModuleAlias : public Module
                return word;
        }
 
-       virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       std::string CreateRFCMessage(const std::string& command, CommandBase::Params& parameters)
        {
-               std::multimap<irc::string, Alias>::iterator i, upperbound;
+               std::string message(command);
+               for (CommandBase::Params::const_iterator iter = parameters.begin(); iter != parameters.end();)
+               {
+                       const std::string& parameter = *iter++;
+                       message.push_back(' ');
+                       if (iter == parameters.end() && (parameter.empty() || parameter.find(' ') != std::string::npos))
+                               message.push_back(':');
+                       message.append(parameter);
+               }
+               return message;
+       }
 
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
+       {
                /* If theyre not registered yet, we dont want
                 * to know.
                 */
@@ -153,19 +164,17 @@ class ModuleAlias : public Module
                        return MOD_RES_PASSTHRU;
 
                /* We dont have any commands looking like this? Stop processing. */
-               i = Aliases.find(command.c_str());
-               if (i == Aliases.end())
+               std::pair<AliasMap::iterator, AliasMap::iterator> iters = Aliases.equal_range(command);
+               if (iters.first == iters.second)
                        return MOD_RES_PASSTHRU;
-               /* Avoid iterating on to different aliases if no patterns match. */
-               upperbound = Aliases.upper_bound(command.c_str());
 
-               irc::string c = command.c_str();
                /* The parameters for the command in their original form, with the command stripped off */
-               std::string compare = original_line.substr(command.length());
+               std::string original_line = CreateRFCMessage(command, parameters);
+               std::string compare(original_line, command.length());
                while (*(compare.c_str()) == ' ')
                        compare.erase(compare.begin());
 
-               while (i != upperbound)
+               for (AliasMap::iterator i = iters.first; i != iters.second; ++i)
                {
                        if (i->second.UserCommand)
                        {
@@ -174,120 +183,107 @@ class ModuleAlias : public Module
                                        return MOD_RES_DENY;
                                }
                        }
-
-                       i++;
                }
 
                // If we made it here, no aliases actually matched.
                return MOD_RES_PASSTHRU;
        }
 
-       virtual void OnUserMessage(User *user, void *dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               if (target_type != TYPE_CHANNEL)
+               // Don't echo anything which is caused by an alias.
+               if (active)
+                       details.echo = false;
+
+               return MOD_RES_PASSTHRU;
+       }
+
+       void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
+       {
+               if ((target.type != MessageTarget::TYPE_CHANNEL) || (details.type != MSG_PRIVMSG))
                {
                        return;
                }
 
                // fcommands are only for local users. Spanningtree will send them back out as their original cmd.
-               if (!user || !IS_LOCAL(user))
+               if (!IS_LOCAL(user))
                {
                        return;
                }
 
                /* Stop here if the user is +B and allowbot is set to no. */
-               if (!AllowBots && user->IsModeSet('B'))
+               if (!AllowBots && user->IsModeSet(botmode))
                {
                        return;
                }
 
-               Channel *c = (Channel *)dest;
+               Channel *c = target.Get<Channel>();
                std::string scommand;
 
                // text is like "!moo cows bite me", we want "!moo" first
-               irc::spacesepstream ss(text);
+               irc::spacesepstream ss(details.text);
                ss.GetToken(scommand);
-               irc::string fcommand = scommand.c_str();
 
-               if (fcommand.empty())
+               if (scommand.size() <= fprefix.size())
                {
                        return; // wtfbbq
                }
 
                // we don't want to touch non-fantasy stuff
-               if (*fcommand.c_str() != fprefix)
+               if (scommand.compare(0, fprefix.size(), fprefix) != 0)
                {
                        return;
                }
 
                // nor do we give a shit about the prefix
-               fcommand.erase(fcommand.begin());
-
-               std::multimap<irc::string, Alias>::iterator i = Aliases.find(fcommand);
+               scommand.erase(0, fprefix.size());
 
-               if (i == Aliases.end())
+               std::pair<AliasMap::iterator, AliasMap::iterator> iters = Aliases.equal_range(scommand);
+               if (iters.first == iters.second)
                        return;
 
-               /* Avoid iterating on to other aliases if no patterns match */
-               std::multimap<irc::string, Alias>::iterator upperbound = Aliases.upper_bound(fcommand);
-
-
                /* The parameters for the command in their original form, with the command stripped off */
-               std::string compare = text.substr(fcommand.length() + 1);
+               std::string compare(details.text, scommand.length() + fprefix.size());
                while (*(compare.c_str()) == ' ')
                        compare.erase(compare.begin());
 
-               while (i != upperbound)
+               for (AliasMap::iterator i = iters.first; i != iters.second; ++i)
                {
                        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, details.text.substr(fprefix.size())))
                                        return;
                        }
-
-                       i++;
                }
        }
 
 
        int DoAlias(User *user, Channel *c, Alias *a, const std::string& compare, const std::string& safe)
        {
-               User *u = NULL;
-
                /* Does it match the pattern? */
                if (!a->format.empty())
                {
-                       if (a->CaseSensitive)
-                       {
-                               if (!InspIRCd::Match(compare, a->format, rfc_case_sensitive_map))
-                                       return 0;
-                       }
-                       else
-                       {
-                               if (!InspIRCd::Match(compare, a->format))
-                                       return 0;
-                       }
+                       if (!InspIRCd::Match(compare, a->format))
+                               return 0;
                }
 
-               if ((a->OperOnly) && (!IS_OPER(user)))
+               if ((a->OperOnly) && (!user->IsOper()))
                        return 0;
 
                if (!a->RequiredNick.empty())
                {
-                       u = ServerInstance->FindNick(a->RequiredNick);
+                       User* u = ServerInstance->FindNick(a->RequiredNick);
                        if (!u)
                        {
-                               user->WriteNumeric(401, ""+user->nick+" "+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 ((u != NULL) && (!a->RequiredNick.empty()) && (a->ULineOnly))
-               {
-                       if (!ServerInstance->ULine(u->server))
+
+                       if ((a->ULineOnly) && (!u->server->IsULine()))
                        {
-                               ServerInstance->SNO->WriteToSnoMask('a', "NOTICE -- Service "+a->RequiredNick+" required by alias "+std::string(a->AliasedCommand.c_str())+" is not on a u-lined server, possibly underhanded antics detected!");
-                               user->WriteNumeric(401, ""+user->nick+" "+a->RequiredNick+" :is an imposter! Please inform an IRC operator as soon as possible.");
+                               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 a server operator as soon as possible.");
                                return 1;
                        }
                }
@@ -298,7 +294,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
@@ -307,16 +303,16 @@ 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(MAXBUF);
+               result.reserve(newline.length());
                for (unsigned int i = 0; i < newline.length(); i++)
                {
                        char c = newline[i];
@@ -324,37 +320,42 @@ class ModuleAlias : public Module
                        {
                                if (isdigit(newline[i+1]))
                                {
-                                       int len = ((i + 2 < newline.length()) && (newline[i+2] == '-')) ? 3 : 2;
+                                       size_t len = ((i + 2 < newline.length()) && (newline[i+2] == '-')) ? 3 : 2;
                                        std::string var = newline.substr(i, len);
                                        result.append(GetVar(var, original_line));
                                        i += len - 1;
                                }
-                               else if (newline.substr(i, 5) == "$nick")
+                               else if (!newline.compare(i, 5, "$nick", 5))
                                {
                                        result.append(user->nick);
                                        i += 4;
                                }
-                               else if (newline.substr(i, 5) == "$host")
+                               else if (!newline.compare(i, 5, "$host", 5))
                                {
-                                       result.append(user->host);
+                                       result.append(user->GetRealHost());
                                        i += 4;
                                }
-                               else if (newline.substr(i, 5) == "$chan")
+                               else if (!newline.compare(i, 5, "$chan", 5))
                                {
                                        if (chan)
                                                result.append(chan->name);
                                        i += 4;
                                }
-                               else if (newline.substr(i, 6) == "$ident")
+                               else if (!newline.compare(i, 6, "$ident", 6))
                                {
                                        result.append(user->ident);
                                        i += 5;
                                }
-                               else if (newline.substr(i, 6) == "$vhost")
+                               else if (!newline.compare(i, 6, "$vhost", 6))
                                {
-                                       result.append(user->dhost);
+                                       result.append(user->GetDisplayedHost());
                                        i += 5;
                                }
+                               else if (!newline.compare(i, 12, "$requirement", 12))
+                               {
+                                       result.append(a->RequiredNick);
+                                       i += 11;
+                               }
                                else
                                        result.push_back(c);
                        }
@@ -363,27 +364,25 @@ class ModuleAlias : public Module
                }
 
                irc::tokenstream ss(result);
-               std::vector<std::string> pars;
+               CommandBase::Params pars;
                std::string command, token;
 
-               ss.GetToken(command);
-               while (ss.GetToken(token) && (pars.size() <= MAXPARAMETERS))
+               ss.GetMiddle(command);
+               while (ss.GetTrailing(token))
                {
                        pars.push_back(token);
                }
-               ServerInstance->Parser->CallHandler(command, pars, user);
-       }
 
-       virtual void OnRehash(User* user)
-       {
-               ReadAliases();
-       }
+               active = true;
+               ServerInstance->Parser.CallHandler(command, pars, user);
+               active = false;
+       }
 
-       virtual void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                // 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_OnUserPostMessage, PRIORITY_AFTER, linkmod);
        }
 };
 
index 08a5f542aeab68ac8e36e6b469cc615e015d1989..45e54d2ccdadad4ae62de006a78c034c30e0ee46 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for channel mode +A, allowing /invite freely on a channel and extban A to deny specific users it */
-
-class AllowInvite : public SimpleChannelModeHandler
-{
- public:
-       AllowInvite(Module* Creator) : SimpleChannelModeHandler(Creator, "allowinvite", 'A') { }
-};
-
 class ModuleAllowInvite : public Module
 {
-       AllowInvite ni;
+       SimpleChannelModeHandler ni;
  public:
 
-       ModuleAllowInvite() : ni(this)
+       ModuleAllowInvite()
+               : ni(this, "allowinvite", 'A')
        {
        }
 
-       void init()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(ni);
-               Implementation eventlist[] = { I_OnUserPreInvite, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               tokens["EXTBAN"].push_back('A');
        }
 
-       virtual void On005Numeric(std::string &output)
-       {
-               ServerInstance->AddExtBanChar('A');
-       }
-
-       virtual ModResult OnUserPreInvite(User* user,User* dest,Channel* channel, time_t timeout)
+       ModResult OnUserPreInvite(User* user,User* dest,Channel* channel, time_t timeout) CXX11_OVERRIDE
        {
                if (IS_LOCAL(user))
                {
@@ -56,10 +42,10 @@ class ModuleAllowInvite : public Module
                        if (res == MOD_RES_DENY)
                        {
                                // Matching extban, explicitly deny /invite
-                               user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You are banned from using INVITE", user->nick.c_str(), channel->name.c_str());
+                               user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You are banned from using INVITE");
                                return res;
                        }
-                       if (channel->IsModeSet('A') || res == MOD_RES_ALLOW)
+                       if (channel->IsModeSet(ni) || res == MOD_RES_ALLOW)
                        {
                                // Explicitly allow /invite
                                return MOD_RES_ALLOW;
@@ -69,13 +55,9 @@ class ModuleAllowInvite : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ~ModuleAllowInvite()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for channel mode +A, allowing /invite freely on a channel and extban A to deny specific users it",VF_VENDOR);
+               return Version("Provides channel mode +A to allow /INVITE freely on a channel, and extban 'A' to deny specific users it", VF_VENDOR);
        }
 };
 
index 38ae4b254f7c7853ab30d99d7c0d5975e8ffa02b..a7ff2bd075acb309bdf1d6cf9501cbe4ea9eecf0 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Display timestamps from all servers connected to the network */
-
 class CommandAlltime : public Command
 {
  public:
        CommandAlltime(Module* Creator) : Command(Creator, "ALLTIME", 0)
        {
                flags_needed = 'o';
-               translation.push_back(TR_END);
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               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;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_OPT_BCAST;
        }
 };
 
-
 class Modulealltime : public Module
 {
        CommandAlltime mycommand;
@@ -62,18 +56,9 @@ class Modulealltime : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(mycommand);
-       }
-
-       virtual ~Modulealltime()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Display timestamps from all servers connected to the network", VF_OPTCOMMON | VF_VENDOR);
+               return Version("Provides the ALLTIME command, displays timestamps from all servers connected to the network", VF_OPTCOMMON | VF_VENDOR);
        }
 
 };
diff --git a/src/modules/m_anticaps.cpp b/src/modules/m_anticaps.cpp
new file mode 100644 (file)
index 0000000..463ff80
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * 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 "inspircd.h"
+#include "modules/exemption.h"
+
+enum AntiCapsMethod
+{
+       ACM_BAN,
+       ACM_BLOCK,
+       ACM_MUTE,
+       ACM_KICK,
+       ACM_KICK_BAN
+};
+
+class AntiCapsSettings
+{
+ public:
+       const AntiCapsMethod method;
+       const uint16_t minlen;
+       const uint8_t percent;
+
+       AntiCapsSettings(const AntiCapsMethod& Method, const uint16_t& MinLen, const uint8_t& Percent)
+               : method(Method)
+               , minlen(MinLen)
+               , percent(Percent)
+       {
+       }
+};
+
+class AntiCapsMode : public ParamMode<AntiCapsMode, SimpleExtItem<AntiCapsSettings> >
+{
+ private:
+       bool ParseMethod(irc::sepstream& stream, AntiCapsMethod& method)
+       {
+               std::string methodstr;
+               if (!stream.GetToken(methodstr))
+                       return false;
+
+               if (irc::equals(methodstr, "ban"))
+                       method = ACM_BAN;
+               else if (irc::equals(methodstr, "block"))
+                       method = ACM_BLOCK;
+               else if (irc::equals(methodstr, "mute"))
+                       method = ACM_MUTE;
+               else if (irc::equals(methodstr, "kick"))
+                       method = ACM_KICK;
+               else if (irc::equals(methodstr, "kickban"))
+                       method = ACM_KICK_BAN;
+               else
+                       return false;
+
+               return true;
+       }
+
+       bool ParseMinimumLength(irc::sepstream& stream, uint16_t& minlen)
+       {
+               std::string minlenstr;
+               if (!stream.GetToken(minlenstr))
+                       return false;
+
+               uint16_t result = ConvToNum<uint16_t>(minlenstr);
+               if (result < 1 || result > ServerInstance->Config->Limits.MaxLine)
+                       return false;
+
+               minlen = result;
+               return true;
+       }
+
+       bool ParsePercent(irc::sepstream& stream, uint8_t& percent)
+       {
+               std::string percentstr;
+               if (!stream.GetToken(percentstr))
+                       return false;
+
+               uint8_t result = ConvToNum<uint8_t>(percentstr);
+               if (result < 1 || result > 100)
+                       return false;
+
+               percent = result;
+               return true;
+       }
+
+ public:
+       AntiCapsMode(Module* Creator)
+               : ParamMode<AntiCapsMode, SimpleExtItem<AntiCapsSettings> >(Creator, "anticaps", 'B')
+       {
+       }
+
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
+       {
+               irc::sepstream stream(parameter, ':');
+               AntiCapsMethod method;
+               uint16_t minlen;
+               uint8_t percent;
+
+               // Attempt to parse the method.
+               if (!ParseMethod(stream, method) || !ParseMinimumLength(stream, minlen) || !ParsePercent(stream, percent))
+               {
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, "Invalid anticaps mode parameter. Syntax: <ban|block|mute|kick|kickban>:{minlen}:{percent}."));
+                       return MODEACTION_DENY;
+               }
+
+               ext.set(channel, new AntiCapsSettings(method, minlen, percent));
+               return MODEACTION_ALLOW;
+       }
+
+       void SerializeParam(Channel* chan, const AntiCapsSettings* acs, std::string& out)
+       {
+               switch (acs->method)
+               {
+                       case ACM_BAN:
+                               out.append("ban");
+                               break;
+                       case ACM_BLOCK:
+                               out.append("block");
+                               break;
+                       case ACM_MUTE:
+                               out.append("mute");
+                               break;
+                       case ACM_KICK:
+                               out.append("kick");
+                               break;
+                       case ACM_KICK_BAN:
+                               out.append("kickban");
+                               break;
+                       default:
+                               out.append("unknown~");
+                               out.append(ConvToStr(acs->method));
+                               break;
+               }
+               out.push_back(':');
+               out.append(ConvToStr(acs->minlen));
+               out.push_back(':');
+               out.append(ConvNumeric(acs->percent));
+       }
+};
+
+class ModuleAntiCaps : public Module
+{
+ private:
+       CheckExemption::EventProvider exemptionprov;
+       std::bitset<UCHAR_MAX> uppercase;
+       std::bitset<UCHAR_MAX> lowercase;
+       AntiCapsMode mode;
+
+       void CreateBan(Channel* channel, User* user, bool mute)
+       {
+               std::string banmask(mute ? "m:" : "");
+               banmask.append("*!*@");
+               banmask.append(user->GetDisplayedHost());
+
+               Modes::ChangeList changelist;
+               changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), banmask);
+               ServerInstance->Modes->Process(ServerInstance->FakeClient, channel, NULL, changelist);
+       }
+
+       void InformUser(Channel* channel, User* user, const std::string& message)
+       {
+               user->WriteNumeric(ERR_CANNOTSENDTOCHAN, channel, message + " and was blocked.");
+       }
+
+ public:
+       ModuleAntiCaps()
+               : exemptionprov(this)
+               , mode(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("anticaps");
+
+               uppercase.reset();
+               const std::string upper = tag->getString("uppercase", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+               for (std::string::const_iterator iter = upper.begin(); iter != upper.end(); ++iter)
+                       uppercase.set(static_cast<unsigned char>(*iter));
+
+               lowercase.reset();
+               const std::string lower = tag->getString("lowercase", "abcdefghijklmnopqrstuvwxyz");
+               for (std::string::const_iterator iter = lower.begin(); iter != lower.end(); ++iter)
+                       lowercase.set(static_cast<unsigned char>(*iter));
+       }
+
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
+       {
+               // We only want to operate on messages from local users.
+               if (!IS_LOCAL(user))
+                       return MOD_RES_PASSTHRU;
+
+               // The mode can only be applied to channels.
+               if (target.type != MessageTarget::TYPE_CHANNEL)
+                       return MOD_RES_PASSTHRU;
+
+               // We only act if the channel has the mode set.
+               Channel* channel = target.Get<Channel>();
+               if (!channel->IsModeSet(&mode))
+                       return MOD_RES_PASSTHRU;
+
+               // If the user is exempt from anticaps then we don't need
+               // to do anything else.
+               ModResult result = CheckExemption::Call(exemptionprov, user, channel, "anticaps");
+               if (result == MOD_RES_ALLOW)
+                       return MOD_RES_PASSTHRU;
+
+               // If the message is a CTCP then we skip it unless it is
+               // an ACTION in which case we just check against the body.
+               std::string ctcpname;
+               std::string msgbody(details.text);
+               if (details.IsCTCP(ctcpname, msgbody))
+               {
+                       // If the CTCP is not an action then skip it.
+                       if (!irc::equals(ctcpname, "ACTION"))
+                               return MOD_RES_PASSTHRU;
+               }
+
+               // Retrieve the anticaps config. This should never be
+               // null but its better to be safe than sorry.
+               AntiCapsSettings* config = mode.ext.get(channel);
+               if (!config)
+                       return MOD_RES_PASSTHRU;
+
+               // If the message is shorter than the minimum length then
+               // we don't need to do anything else.
+               size_t length = msgbody.length();
+               if (length < config->minlen)
+                       return MOD_RES_PASSTHRU;
+
+               // Count the characters to see how many upper case and
+               // ignored (non upper or lower) characters there are.
+               size_t upper = 0;
+               for (std::string::const_iterator iter = msgbody.begin(); iter != msgbody.end(); ++iter)
+               {
+                       unsigned char chr = static_cast<unsigned char>(*iter);
+                       if (uppercase.test(chr))
+                               upper += 1;
+                       else if (!lowercase.test(chr))
+                               length -= 1;
+               }
+
+               // If the message was entirely symbols then the message
+               // can't contain any upper case letters.
+               if (length == 0)
+                       return MOD_RES_PASSTHRU;
+
+               // Calculate the percentage.
+               double percent = round((upper * 100) / length);
+               if (percent < config->percent)
+                       return MOD_RES_PASSTHRU;
+
+               const std::string message = InspIRCd::Format("Your message exceeded the %d%% upper case character threshold for %s",
+                       config->percent, channel->name.c_str());
+
+               switch (config->method)
+               {
+                       case ACM_BAN:
+                               InformUser(channel, user, message);
+                               CreateBan(channel, user, false);
+                               break;
+
+                       case ACM_BLOCK:
+                               InformUser(channel, user, message);
+                               break;
+
+                       case ACM_MUTE:
+                               InformUser(channel, user, message);
+                               CreateBan(channel, user, true);
+                               break;
+
+                       case ACM_KICK:
+                               channel->KickUser(ServerInstance->FakeClient, user, message);
+                               break;
+
+                       case ACM_KICK_BAN:
+                               CreateBan(channel, user, false);
+                               channel->KickUser(ServerInstance->FakeClient, user, message);
+                               break;
+               }
+               return MOD_RES_DENY;
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides support for punishing users that send capitalised messages", VF_COMMON|VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleAntiCaps)
index 2a8edb9d49f1a464eb26fe597817073b44b1ba18..f708ea9d0b253b452c7646aec3cf10b83e2af0a2 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/exemption.h"
+#include "modules/names.h"
+#include "modules/who.h"
 
-/* $ModDesc: Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list */
-
-class AuditoriumMode : public ModeHandler
+class AuditoriumMode : public SimpleChannelModeHandler
 {
  public:
-       AuditoriumMode(Module* Creator) : ModeHandler(Creator, "auditorium", 'u', PARAM_NONE, MODETYPE_CHANNEL)
+       AuditoriumMode(Module* Creator) : SimpleChannelModeHandler(Creator, "auditorium", 'u')
        {
-               levelrequired = OP_VALUE;
+               ranktoset = ranktounset = OP_VALUE;
        }
+};
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
-       {
-               if (channel->IsModeSet(this) == adding)
-                       return MODEACTION_DENY;
-               channel->SetMode(this, adding);
-               return MODEACTION_ALLOW;
-       }
+class ModuleAuditorium;
+
+namespace
+{
+
+/** Hook handler for join client protocol events.
+ * This allows us to block join protocol events completely, including all associated messages (e.g. MODE, away-notify AWAY).
+ * This is not the same as OnUserJoin() because that runs only when a real join happens but this runs also when a module
+ * such as delayjoin or hostcycle generates a join.
+ */
+class JoinHook : public ClientProtocol::EventHook
+{
+       ModuleAuditorium* const parentmod;
+       bool active;
+
+ public:
+       JoinHook(ModuleAuditorium* mod);
+       void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE;
+       ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE;
 };
 
-class ModuleAuditorium : public Module
+}
+
+class ModuleAuditorium
+       : public Module
+       , public Names::EventListener
+       , public Who::EventListener
 {
- private:
+       CheckExemption::EventProvider exemptionprov;
        AuditoriumMode aum;
        bool OpsVisible;
        bool OpsCanSee;
        bool OperCanSee;
- public:
-       ModuleAuditorium() : aum(this)
-       {
-       }
-
-       void init()
-       {
-               ServerInstance->Modules->AddService(aum);
+       JoinHook joinhook;
 
-               OnRehash(NULL);
-
-               Implementation eventlist[] = {
-                       I_OnUserJoin, I_OnUserPart, I_OnUserKick,
-                       I_OnBuildNeighborList, I_OnNamesListItem, I_OnSendWhoLine,
-                       I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       ~ModuleAuditorium()
+ public:
+       ModuleAuditorium()
+               : Names::EventListener(this)
+               , Who::EventListener(this)
+               , exemptionprov(this)
+               , aum(this)
+               , joinhook(this)
        {
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("auditorium");
                OpsVisible = tag->getBool("opvisible");
@@ -78,9 +87,9 @@ class ModuleAuditorium : public Module
                OperCanSee = tag->getBool("opercansee", true);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list", VF_VENDOR);
+               return Version("Provides channel mode +u, auditorium channels where nobody can see others joining and parting or the nick list", VF_VENDOR);
        }
 
        /* Can they be seen by everyone? */
@@ -89,7 +98,7 @@ class ModuleAuditorium : public Module
                if (!memb->chan->IsModeSet(&aum))
                        return true;
 
-               ModResult res = ServerInstance->OnCheckExemption(memb->user, memb->chan, "auditorium-vis");
+               ModResult res = CheckExemption::Call(exemptionprov, memb->user, memb->chan, "auditorium-vis");
                return res.check(OpsVisible && memb->getRank() >= OP_VALUE);
        }
 
@@ -105,26 +114,23 @@ class ModuleAuditorium : public Module
                        return true;
 
                // Can you see the list by permission?
-               ModResult res = ServerInstance->OnCheckExemption(issuer,memb->chan,"auditorium-see");
+               ModResult res = CheckExemption::Call(exemptionprov, issuer, memb->chan, "auditorium-see");
                if (res.check(OpsCanSee && memb->chan->GetPrefixValue(issuer) >= OP_VALUE))
                        return true;
 
                return false;
        }
 
-       void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+       ModResult OnNamesListItem(LocalUser* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
        {
-               // Some module already hid this from being displayed, don't bother
-               if (nick.empty())
-                       return;
-
                if (IsVisible(memb))
-                       return;
+                       return MOD_RES_PASSTHRU;
 
                if (CanSee(issuer, memb))
-                       return;
+                       return MOD_RES_PASSTHRU;
 
-               nick.clear();
+               // Don't display this user in the NAMES list
+               return MOD_RES_DENY;
        }
 
        /** Build CUList for showing this join/part/kick */
@@ -133,43 +139,40 @@ 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);
                }
        }
 
-       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+       void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE
        {
                BuildExcept(memb, excepts);
        }
 
-       void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
+       void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) CXX11_OVERRIDE
        {
                BuildExcept(memb, excepts);
        }
 
-       void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
+       void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE
        {
-               BuildExcept(memb, excepts);
-       }
-
-       void OnBuildNeighborList(User* source, UserChanList &include, std::map<User*,bool> &exception)
-       {
-               UCListIter i = include.begin();
-               while (i != include.end())
+               for (IncludeChanList::iterator i = include.begin(); i != include.end(); )
                {
-                       Channel* c = *i++;
-                       Membership* memb = c->GetUser(source);
-                       if (!memb || IsVisible(memb))
+                       Membership* memb = *i;
+                       if (IsVisible(memb))
+                       {
+                               ++i;
                                continue;
+                       }
+
                        // this channel should not be considered when listing my neighbors
-                       include.erase(c);
+                       i = include.erase(i);
                        // however, that might hide me from ops that can see me...
-                       const UserMembList* users = c->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;
@@ -177,18 +180,37 @@ class ModuleAuditorium : public Module
                }
        }
 
-       void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
+       ModResult OnWhoLine(const Who::Request& request, LocalUser* source, User* user, Membership* memb, Numeric::Numeric& numeric) CXX11_OVERRIDE
        {
-               Channel* channel = ServerInstance->FindChan(params[0]);
-               if (!channel)
-                       return;
-               Membership* memb = channel->GetUser(user);
-               if ((!memb) || (IsVisible(memb)))
-                       return;
+               if (!memb)
+                       return MOD_RES_PASSTHRU;
+               if (IsVisible(memb))
+                       return MOD_RES_PASSTHRU;
                if (CanSee(source, memb))
-                       return;
-               line.clear();
+                       return MOD_RES_PASSTHRU;
+               return MOD_RES_DENY;
        }
 };
 
+JoinHook::JoinHook(ModuleAuditorium* mod)
+       : ClientProtocol::EventHook(mod, "JOIN", 10)
+       , parentmod(mod)
+{
+}
+
+void JoinHook::OnEventInit(const ClientProtocol::Event& ev)
+{
+       const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+       active = !parentmod->IsVisible(join.GetMember());
+}
+
+ModResult JoinHook::OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist)
+{
+       if (!active)
+               return MOD_RES_PASSTHRU;
+
+       const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+       return ((parentmod->CanSee(user, join.GetMember())) ? MOD_RES_PASSTHRU : MOD_RES_DENY);
+}
+
 MODULE_INIT(ModuleAuditorium)
index 0c0e8f579a1e458094b758557332904db4c4d9b3..83c40533800ecb6d9a380843ded9d27a0a06999d 100644 (file)
 
 
 #include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides support for the +w channel mode, autoop list */
+#include "listmode.h"
 
 /** Handles +w channel mode
  */
 class AutoOpList : public ListModeBase
 {
  public:
-       AutoOpList(Module* Creator) : ListModeBase(Creator, "autoop", 'w', "End of Channel Access List", 910, 911, true)
+       AutoOpList(Module* Creator)
+               : ListModeBase(Creator, "autoop", 'w', "End of Channel Access List", 910, 911, true)
        {
-               levelrequired = OP_VALUE;
+               ranktoset = ranktounset = OP_VALUE;
                tidy = false;
        }
 
-       ModeHandler* FindMode(const std::string& mid)
+       PrefixMode* FindMode(const std::string& mid)
        {
                if (mid.length() == 1)
-                       return ServerInstance->Modes->FindMode(mid[0], MODETYPE_CHANNEL);
-               for(char c='A'; c <= 'z'; c++)
-               {
-                       ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
-                       if (mh && mh->name == mid)
-                               return mh;
-               }
-               return NULL;
+                       return ServerInstance->Modes->FindPrefixMode(mid[0]);
+
+               ModeHandler* mh = ServerInstance->Modes->FindMode(mid, MODETYPE_CHANNEL);
+               return mh ? mh->IsPrefixMode() : NULL;
        }
 
-       ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding)
+       ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding) CXX11_OVERRIDE
        {
                std::string::size_type pos = parameter.find(':');
                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);
-               ModeHandler* mh = FindMode(mid);
+               std::string mid(parameter, 0, pos);
+               PrefixMode* mh = FindMode(mid);
 
-               if (adding && (!mh || !mh->GetPrefixRank()))
+               if (adding && !mh)
                {
-                       source->WriteNumeric(415, "%s %s :Cannot find prefix mode '%s' for autoop",
-                               source->nick.c_str(), mid.c_str(), mid.c_str());
+                       source->WriteNumeric(ERR_UNKNOWNMODE, mid, InspIRCd::Format("Cannot find prefix mode '%s' for autoop", mid.c_str()));
                        return MOD_RES_DENY;
                }
                else if (!mh)
@@ -68,10 +62,9 @@ class AutoOpList : public ListModeBase
                std::string dummy;
                if (mh->AccessCheck(source, channel, dummy, true) == MOD_RES_DENY)
                        return MOD_RES_DENY;
-               if (mh->GetLevelRequired() > mylevel)
+               if (mh->GetLevelRequired(true) > mylevel)
                {
-                       source->WriteNumeric(482, "%s %s :You must be able to set mode '%s' to include it in an autoop",
-                               source->nick.c_str(), 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;
@@ -82,64 +75,44 @@ class ModuleAutoOp : public Module
 {
        AutoOpList mh;
 
-public:
+ public:
        ModuleAutoOp() : mh(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(mh);
-               mh.DoImplements(this);
-
-               Implementation list[] = { I_OnPostJoin, };
-               ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation));
-       }
-
-       void OnPostJoin(Membership *memb)
+       void OnPostJoin(Membership *memb) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(memb->user))
                        return;
 
-               modelist* list = mh.extItem.get(memb->chan);
+               ListModeBase::ModeList* list = mh.GetList(memb->chan);
                if (list)
                {
-                       std::string modeline("+");
-                       std::vector<std::string> modechange;
-                       modechange.push_back(memb->chan->name);
-                       for (modelist::iterator it = list->begin(); it != list->end(); it++)
+                       Modes::ChangeList changelist;
+                       for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
                        {
                                std::string::size_type colon = it->mask.find(':');
                                if (colon == std::string::npos)
                                        continue;
                                if (memb->chan->CheckBan(memb->user, it->mask.substr(colon+1)))
                                {
-                                       ModeHandler* given = mh.FindMode(it->mask.substr(0, colon));
-                                       if (given && given->GetPrefixRank())
-                                               modeline.push_back(given->GetModeChar());
+                                       PrefixMode* given = mh.FindMode(it->mask.substr(0, colon));
+                                       if (given)
+                                               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->SendGlobalMode(modechange, ServerInstance->FakeClient);
+                       ServerInstance->Modes->Process(ServerInstance->FakeClient, memb->chan, NULL, changelist);
                }
        }
 
-       void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
-       {
-               mh.DoSyncChannel(chan, proto, opaque);
-       }
-
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                mh.DoRehash();
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for the +w channel mode", VF_VENDOR);
+               return Version("Provides channel mode +w, basic channel access controls", VF_VENDOR);
        }
 };
 
index 7531c5c1250bc3cd6d01845f6c339cd0fd9e5b3d..c7864ea9e88b56311eef5aa8b172eec865a2bdbe 100644 (file)
 
 
 #include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides support for the +e channel mode */
-/* $ModDep: ../../include/u_listmode.h */
+#include "listmode.h"
 
 /* Written by Om<om@inspircd.org>, April 2005. */
 /* Rewritten to use the listmode utility by Om, December 2005 */
 class BanException : public ListModeBase
 {
  public:
-       BanException(Module* Creator) : ListModeBase(Creator, "banexception", 'e', "End of Channel Exception List", 348, 349, true) { }
+       BanException(Module* Creator)
+               : ListModeBase(Creator, "banexception", 'e', "End of Channel Exception List", 348, 349, true)
+       {
+       }
 };
 
 
@@ -49,87 +49,65 @@ class ModuleBanException : public Module
 {
        BanException be;
 
-public:
+ public:
        ModuleBanException() : be(this)
        {
        }
 
-       void init()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(be);
-
-               be.DoImplements(this);
-               Implementation list[] = { I_OnRehash, I_On005Numeric, I_OnExtBanCheck, I_OnCheckChannelBan };
-               ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation));
+               tokens["EXCEPTS"] = ConvToStr(be.GetModeChar());
        }
 
-       void On005Numeric(std::string &output)
+       ModResult OnExtBanCheck(User *user, Channel *chan, char type) CXX11_OVERRIDE
        {
-               output.append(" EXCEPTS=e");
-       }
+               ListModeBase::ModeList* list = be.GetList(chan);
+               if (!list)
+                       return MOD_RES_PASSTHRU;
 
-       ModResult OnExtBanCheck(User *user, Channel *chan, char type)
-       {
-               if (chan != NULL)
+               for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
                {
-                       modelist *list = be.extItem.get(chan);
-
-                       if (!list)
-                               return MOD_RES_PASSTHRU;
+                       if (it->mask.length() <= 2 || it->mask[0] != type || it->mask[1] != ':')
+                               continue;
 
-                       for (modelist::iterator it = list->begin(); it != list->end(); it++)
+                       if (chan->CheckBan(user, it->mask.substr(2)))
                        {
-                               if (it->mask.length() <= 2 || it->mask[0] != type || it->mask[1] != ':')
-                                       continue;
-
-                               if (chan->CheckBan(user, it->mask.substr(2)))
-                               {
-                                       // They match an entry on the list, so let them pass this.
-                                       return MOD_RES_ALLOW;
-                               }
+                               // They match an entry on the list, so let them pass this.
+                               return MOD_RES_ALLOW;
                        }
                }
 
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnCheckChannelBan(User* user, Channel* chan)
+       ModResult OnCheckChannelBan(User* user, Channel* chan) CXX11_OVERRIDE
        {
-               if (chan)
+               ListModeBase::ModeList* list = be.GetList(chan);
+               if (!list)
                {
-                       modelist *list = be.extItem.get(chan);
-
-                       if (!list)
-                       {
-                               // No list, proceed normally
-                               return MOD_RES_PASSTHRU;
-                       }
+                       // No list, proceed normally
+                       return MOD_RES_PASSTHRU;
+               }
 
-                       for (modelist::iterator it = list->begin(); it != list->end(); it++)
+               for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
+               {
+                       if (chan->CheckBan(user, it->mask))
                        {
-                               if (chan->CheckBan(user, it->mask))
-                               {
-                                       // They match an entry on the list, so let them in.
-                                       return MOD_RES_ALLOW;
-                               }
+                               // They match an entry on the list, so let them in.
+                               return MOD_RES_ALLOW;
                        }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
-       {
-               be.DoSyncChannel(chan, proto, opaque);
-       }
-
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                be.DoRehash();
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for the +e channel mode", VF_VENDOR);
+               return Version("Provides channel mode +e, ban exceptions", VF_VENDOR);
        }
 };
 
index 3df8b5e6627d30d040b48d00c310c06a770cb789..752aedd905dad1d1106d9cef3fe5ddecfe3f8bfb 100644 (file)
@@ -23,9 +23,7 @@
 
 
 #include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Allows an extended ban (+b) syntax redirecting banned users to another channel */
+#include "listmode.h"
 
 /* Originally written by Om, January 2009
  */
@@ -43,18 +41,20 @@ class BanRedirectEntry
 };
 
 typedef std::vector<BanRedirectEntry> BanRedirectList;
-typedef std::deque<std::string> StringDeque;
 
 class BanRedirect : public ModeWatcher
 {
+       ChanModeReference ban;
  public:
        SimpleExtItem<BanRedirectList> extItem;
-       BanRedirect(Module* parent) : ModeWatcher(parent, 'b', MODETYPE_CHANNEL),
-               extItem("banredirect", parent)
+       BanRedirect(Module* parent)
+               : ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
+               , ban(parent, "ban")
+               , extItem("banredirect", ExtensionItem::EXT_CHANNEL, parent)
        {
        }
 
-       bool BeforeMode(User* source, User* dest, Channel* channel, std::string &param, bool adding, ModeType type)
+       bool BeforeMode(User* source, User* dest, Channel* channel, std::string& param, bool adding) CXX11_OVERRIDE
        {
                /* nick!ident@host -> nick!ident@host
                 * nick!ident@host#chan -> nick!ident@host#chan
@@ -63,14 +63,13 @@ class BanRedirect : public ModeWatcher
                 * nick#chan -> nick!*@*#chan
                 */
 
-               if(channel && (type == MODETYPE_CHANNEL) && param.length())
+               if ((channel) && !param.empty())
                {
                        BanRedirectList* redirects;
 
                        std::string mask[4];
                        enum { NICK, IDENT, HOST, CHAN } current = NICK;
                        std::string::iterator start_pos = param.begin();
-                       long maxbans = channel->GetMaxBans();
 
                        if (param.length() >= 2 && param[1] == ':')
                                return true;
@@ -78,10 +77,12 @@ class BanRedirect : public ModeWatcher
                        if (param.find('#') == std::string::npos)
                                return true;
 
-                       if(adding && (channel->bans.size() > static_cast<unsigned>(maxbans)))
+                       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(478, "%s %s %c :Channel ban list for %s is full (maximum entries for this channel is %ld)",
-                                       source->nick.c_str(), channel->name.c_str(), mode, channel->name.c_str(), maxbans);
+                               source->WriteNumeric(ERR_BANLISTFULL, channel->name, banlm->GetModeChar(), InspIRCd::Format("Channel ban list for %s is full (maximum entries for this channel is %u)", channel->name.c_str(), maxbans));
                                return false;
                        }
 
@@ -90,23 +91,25 @@ class BanRedirect : public ModeWatcher
                                switch(*curr)
                                {
                                        case '!':
+                                               if (current != NICK)
+                                                       break;
                                                mask[current].assign(start_pos, curr);
                                                current = IDENT;
                                                start_pos = curr+1;
                                                break;
                                        case '@':
+                                               if (current != IDENT)
+                                                       break;
                                                mask[current].assign(start_pos, curr);
                                                current = HOST;
                                                start_pos = curr+1;
                                                break;
                                        case '#':
-                                               /* bug #921: don't barf when redirecting to ## channels */
-                                               if (current != CHAN)
-                                               {
-                                                       mask[current].assign(start_pos, curr);
-                                                       current = CHAN;
-                                                       start_pos = curr;
-                                               }
+                                               if (current == CHAN)
+                                                       break;
+                                               mask[current].assign(start_pos, curr);
+                                               current = CHAN;
+                                               start_pos = curr;
                                                break;
                                }
                        }
@@ -145,27 +148,27 @@ class BanRedirect : public ModeWatcher
                        {
                                if (adding && IS_LOCAL(source))
                                {
-                                       if (!ServerInstance->IsChannel(mask[CHAN].c_str(),  ServerInstance->Config->Limits.ChanMax))
+                                       if (!ServerInstance->IsChannel(mask[CHAN]))
                                        {
-                                               source->WriteNumeric(403, "%s %s :Invalid channel name in redirection (%s)", source->nick.c_str(), 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, "%s :Target channel %s must exist to be set as a redirect.",source->nick.c_str(),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, "%s :You must be opped on %s to set it as a redirect.",source->nick.c_str(), 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 %s :You cannot set a ban redirection to the channel the ban is on", source->nick.c_str(), channel->name.c_str());
+                                               source->WriteNumeric(690, channel->name, "You cannot set a ban redirection to the channel the ban is on");
                                                return false;
                                        }
                                }
@@ -184,7 +187,7 @@ class BanRedirect : public ModeWatcher
                                                for (BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); ++redir)
                                                {
                                                        // Mimic the functionality used when removing the mode
-                                                       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))
                                                        {
                                                                // Make sure the +b handler will still set the right ban
                                                                param.append(mask[CHAN]);
@@ -211,8 +214,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);
 
@@ -240,61 +242,42 @@ class ModuleBanRedirect : public Module
 {
        BanRedirect re;
        bool nofollow;
+       ChanModeReference limitmode;
+       ChanModeReference redirectmode;
 
  public:
        ModuleBanRedirect()
-       : re(this)
+               : re(this)
+               , nofollow(false)
+               , limitmode(this, "limit")
+               , redirectmode(this, "redirect")
        {
-               nofollow = false;
-       }
-
-
-       void init()
-       {
-               if(!ServerInstance->Modes->AddModeWatcher(&re))
-                       throw ModuleException("Could not add mode watcher");
-
-               ServerInstance->Modules->AddService(re.extItem);
-               Implementation list[] = { I_OnUserPreJoin };
-               ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation));
        }
 
-       virtual void OnCleanup(int target_type, void* item)
+       void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
        {
-               if(target_type == TYPE_CHANNEL)
+               if (type == ExtensionItem::EXT_CHANNEL)
                {
                        Channel* chan = static_cast<Channel*>(item);
                        BanRedirectList* redirects = re.extItem.get(chan);
 
                        if(redirects)
                        {
-                               irc::modestacker modestack(false);
-                               StringDeque stackresult;
-                               std::vector<std::string> mode_junk;
-                               mode_junk.push_back(chan->name);
+                               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);
 
-                               while(modestack.GetStackedLine(stackresult))
-                               {
-                                       mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
-                                       ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
-                                       mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
-                               }
+                               ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist, ModeParser::MODE_LOCALONLY);
                        }
                }
        }
 
-       virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
                if (chan)
                {
@@ -338,19 +321,19 @@ class ModuleBanRedirect : public Module
                                                std::string destlimit;
 
                                                if (destchan)
-                                                       destlimit = destchan->GetModeParameter('l');
+                                                       destlimit = destchan->GetModeParameter(limitmode);
 
-                                               if(destchan && ServerInstance->Modules->Find("m_redirect.so") && destchan->IsModeSet('L') && !destlimit.empty() && (destchan->GetUserCounter() >= atoi(destlimit.c_str())))
+                                               if(destchan && destchan->IsModeSet(redirectmode) && !destlimit.empty() && (destchan->GetUserCounter() >= ConvToNum<size_t>(destlimit)))
                                                {
-                                                       user->WriteNumeric(474, "%s %s :Cannot join channel (You are banned)", user->nick.c_str(), chan->name.c_str());
+                                                       user->WriteNumeric(ERR_BANNEDFROMCHAN, chan->name, "Cannot join channel (you're banned)");
                                                        return MOD_RES_DENY;
                                                }
                                                else
                                                {
-                                                       user->WriteNumeric(474, "%s %s :Cannot join channel (You are banned)", user->nick.c_str(), chan->name.c_str());
-                                                       user->WriteNumeric(470, "%s %s %s :You are banned from this channel, so you are automatically transferred to the redirected channel.", user->nick.c_str(), chan->name.c_str(), redir->targetchan.c_str());
+                                                       user->WriteNumeric(ERR_BANNEDFROMCHAN, chan->name, "Cannot join channel (you're banned)");
+                                                       user->WriteNumeric(470, chan->name, redir->targetchan, "You are banned from this channel, so you are automatically being transferred to the redirected channel.");
                                                        nofollow = true;
-                                                       Channel::JoinUser(user, redir->targetchan.c_str(), false, "", false, ServerInstance->Time());
+                                                       Channel::JoinUser(user, redir->targetchan);
                                                        nofollow = false;
                                                        return MOD_RES_DENY;
                                                }
@@ -361,14 +344,7 @@ class ModuleBanRedirect : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ~ModuleBanRedirect()
-       {
-               /* XXX is this the best place to do this? */
-               if (!ServerInstance->Modes->DelModeWatcher(&re))
-                       ServerInstance->Logs->Log("m_banredirect.so", DEBUG, "Failed to delete modewatcher!");
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Allows an extended ban (+b) syntax redirecting banned users to another channel", VF_COMMON|VF_VENDOR);
        }
diff --git a/src/modules/m_bcrypt.cpp b/src/modules/m_bcrypt.cpp
new file mode 100644 (file)
index 0000000..59ee155
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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/>.
+ */
+
+/// $CompilerFlags: -Ivendor_directory("bcrypt")
+
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+#include <crypt_blowfish.c>
+
+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->getUInt("rounds", 10, 1);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Implements bcrypt hashing", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleBCrypt)
index be861447f36effcab132852d2f8e32836fe45387..5ab627c71bedc2964705dfd4a9f4f0c79039b027 100644 (file)
@@ -23,8 +23,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Attempt to block /amsg, at least some of the irritating mIRC scripts. */
-
 enum BlockAction { IBLOCK_KILL, IBLOCK_KILLOPERS, IBLOCK_NOTICE, IBLOCK_NOTICEOPERS, IBLOCK_SILENT };
 /*     IBLOCK_NOTICE           - Send a notice to the user informing them of what happened.
  *     IBLOCK_NOTICEOPERS      - Send a notice to the user informing them and send an oper notice.
@@ -37,64 +35,53 @@ enum BlockAction { IBLOCK_KILL, IBLOCK_KILLOPERS, IBLOCK_NOTICE, IBLOCK_NOTICEOP
  */
 class BlockedMessage
 {
-public:
+ 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)
        {
        }
 };
 
 class ModuleBlockAmsg : public Module
 {
-       int ForgetDelay;
+       unsigned int ForgetDelay;
        BlockAction action;
        SimpleExtItem<BlockedMessage> blockamsg;
 
  public:
-       ModuleBlockAmsg() : blockamsg("blockamsg", this)
-       {
-       }
-
-       void init()
-       {
-               this->OnRehash(NULL);
-               ServerInstance->Modules->AddService(blockamsg);
-               Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModuleBlockAmsg()
+       ModuleBlockAmsg()
+               : blockamsg("blockamsg", ExtensionItem::EXT_USER, this)
        {
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Attempt to block /amsg, at least some of the irritating mIRC scripts.",VF_VENDOR);
+               return Version("Attempt to block /amsg or /ame, at least some of the irritating client scripts", VF_VENDOR);
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("blockamsg");
-               ForgetDelay = tag->getInt("delay", -1);
+               ForgetDelay = tag->getDuration("delay", 3);
                std::string act = tag->getString("action");
 
-               if(act == "notice")
+               if (stdalgo::string::equalsci(act, "notice"))
                        action = IBLOCK_NOTICE;
-               else if(act == "noticeopers")
+               else if (stdalgo::string::equalsci(act, "noticeopers"))
                        action = IBLOCK_NOTICEOPERS;
-               else if(act == "silent")
+               else if (stdalgo::string::equalsci(act, "silent"))
                        action = IBLOCK_SILENT;
-               else if(act == "kill")
+               else if (stdalgo::string::equalsci(act, "kill"))
                        action = IBLOCK_KILL;
                else
                        action = IBLOCK_KILLOPERS;
        }
 
-       virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
        {
                // Don't do anything with unregistered users
                if (user->registered != REG_ALL)
@@ -102,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);
@@ -138,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 && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == user->chans.size())))
                        {
                                // Block it...
-                               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_KILLOPERS || action == IBLOCK_NOTICEOPERS)
+                                       ServerInstance->SNO->WriteToSnoMask('a', "%s had an /amsg or /ame blocked", 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) blocked");
 
                                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);
                        }
                }
@@ -169,5 +147,4 @@ class ModuleBlockAmsg : public Module
        }
 };
 
-
 MODULE_INIT(ModuleBlockAmsg)
index 7146ee068d421b0cf83525117eaad13355cd7b09..fa780427cbaa051c5f7fd5cf31ee551a09857c9a 100644 (file)
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides support to block all-CAPS channel messages and notices */
-
-
-/** Handles the +B channel mode
- */
-class BlockCaps : public SimpleChannelModeHandler
-{
- public:
-       BlockCaps(Module* Creator) : SimpleChannelModeHandler(Creator, "blockcaps", 'B') { }
-};
+#include "modules/exemption.h"
 
 class ModuleBlockCAPS : public Module
 {
-       BlockCaps bc;
-       int percent;
+       CheckExemption::EventProvider exemptionprov;
+       SimpleChannelModeHandler bc;
+       unsigned int percent;
        unsigned int minlen;
-       char capsmap[256];
-public:
+       std::bitset<UCHAR_MAX> lowercase;
+       std::bitset<UCHAR_MAX> uppercase;
 
-       ModuleBlockCAPS() : bc(this)
-       {
-       }
-
-       void init()
-       {
-               OnRehash(NULL);
-               ServerInstance->Modules->AddService(bc);
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual void On005Numeric(std::string &output)
+public:
+       ModuleBlockCAPS()
+               : exemptionprov(this)
+               , bc(this, "blockcaps", 'B')
        {
-               ServerInstance->AddExtBanChar('B');
        }
 
-       virtual void OnRehash(User* user)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ReadConf();
+               tokens["EXTBAN"].push_back('B');
        }
 
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               if (target_type == TYPE_CHANNEL)
+               if (target.type == MessageTarget::TYPE_CHANNEL)
                {
-                       if ((!IS_LOCAL(user)) || (text.length() < minlen) || (text == "\1ACTION\1") || (text == "\1ACTION"))
+                       if (!IS_LOCAL(user))
                                return MOD_RES_PASSTHRU;
 
-                       Channel* c = (Channel*)dest;
-                       ModResult res = ServerInstance->OnCheckExemption(user,c,"blockcaps");
+                       Channel* c = target.Get<Channel>();
+                       ModResult res = CheckExemption::Call(exemptionprov, user, c, "blockcaps");
 
                        if (res == MOD_RES_ALLOW)
                                return MOD_RES_PASSTHRU;
 
-                       if (!c->GetExtBanStatus(user, 'B').check(!c->IsModeSet('B')))
+                       if (!c->GetExtBanStatus(user, 'B').check(!c->IsModeSet(bc)))
                        {
-                               int caps = 0;
-                               const char* actstr = "\1ACTION ";
-                               int act = 0;
+                               // If the message is a CTCP then we skip it unless it is
+                               // an ACTION in which case we just check against the body.
+                               std::string ctcpname;
+                               std::string message(details.text);
+                               if (details.IsCTCP(ctcpname, message))
+                               {
+                                       // If the CTCP is not an action then skip it.
+                                       if (!irc::equals(ctcpname, "ACTION"))
+                                               return MOD_RES_PASSTHRU;
+                               }
 
-                               for (std::string::iterator i = text.begin(); i != text.end(); i++)
+                               // If the message is shorter than the minimum length
+                               // then we don't need to do anything else.
+                               size_t length = message.length();
+                               if (length < minlen)
+                                       return MOD_RES_PASSTHRU;
+
+                               // Count the characters to see how many upper case and
+                               // ignored (non upper or lower) characters there are.
+                               size_t upper = 0;
+                               for (std::string::const_iterator iter = message.begin(); iter != message.end(); ++iter)
                                {
-                                       /* Smart fix for suggestion from Jobe, ignore CTCP ACTION (part of /ME) */
-                                       if (*actstr && *i == *actstr++ && act != -1)
-                                       {
-                                               act++;
-                                               continue;
-                                       }
-                                       else
-                                               act = -1;
-
-                                       caps += capsmap[(unsigned char)*i];
+                                       unsigned char chr = static_cast<unsigned char>(*iter);
+                                       if (uppercase.test(chr))
+                                               upper += 1;
+                                       else if (!lowercase.test(chr))
+                                               length -= 1;
                                }
-                               if ( ((caps*100)/(int)text.length()) >= percent )
+
+                               // Calculate the percentage which is upper case. If the
+                               // message was entirely symbols then it can't contain
+                               // any upper case letters.
+                               if (length > 0 && round((upper * 100) / length) >= percent)
                                {
-                                       user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s %s :Your message cannot contain more than %d%% capital letters if it's longer than %d characters", user->nick.c_str(), 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;
                                }
                        }
@@ -105,37 +101,24 @@ public:
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
-       }
-
-       void ReadConf()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("blockcaps");
-               percent = tag->getInt("percent", 100);
-               minlen = tag->getInt("minlen", 1);
-               std::string hmap = tag->getString("capsmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-               memset(capsmap, 0, sizeof(capsmap));
-               for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
-                       capsmap[(unsigned char)*n] = 1;
-               if (percent < 1 || percent > 100)
-               {
-                       ServerInstance->Logs->Log("CONFIG",DEFAULT, "<blockcaps:percent> out of range, setting to default of 100.");
-                       percent = 100;
-               }
-               if (minlen < 1 || minlen > MAXBUF-1)
-               {
-                       ServerInstance->Logs->Log("CONFIG",DEFAULT, "<blockcaps:minlen> out of range, setting to default of 1.");
-                       minlen = 1;
-               }
-       }
-
-       virtual ~ModuleBlockCAPS()
-       {
+               percent = tag->getUInt("percent", 100, 1, 100);
+               minlen = tag->getUInt("minlen", 1, 1, ServerInstance->Config->Limits.MaxLine);
+
+               lowercase.reset();
+               const std::string lower = tag->getString("lowercase", "abcdefghijklmnopqrstuvwxyz");
+               for (std::string::const_iterator iter = lower.begin(); iter != lower.end(); ++iter)
+                       lowercase.set(static_cast<unsigned char>(*iter));
+
+               uppercase.reset();
+               const std::string upper = tag->getString("uppercase", tag->getString("capsmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+               for (std::string::const_iterator iter = upper.begin(); iter != upper.end(); ++iter)
+                       uppercase.set(static_cast<unsigned char>(*iter));
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support to block all-CAPS channel messages and notices", VF_VENDOR);
        }
index 3cc01b4c05bcc842f98f121e41da040ec5b366fa..25345506e5a517faae5273543584f4b26c973056 100644 (file)
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides channel mode +c to block color */
-
-/** Handles the +c channel mode
- */
-class BlockColor : public SimpleChannelModeHandler
-{
- public:
-       BlockColor(Module* Creator) : SimpleChannelModeHandler(Creator, "blockcolor", 'c') { }
-};
+#include "modules/exemption.h"
 
 class ModuleBlockColor : public Module
 {
-       BlockColor bc;
+       CheckExemption::EventProvider exemptionprov;
+       SimpleChannelModeHandler bc;
  public:
 
-       ModuleBlockColor() : bc(this)
+       ModuleBlockColor()
+               : exemptionprov(this)
+               , bc(this, "blockcolor", 'c')
        {
        }
 
-       void init()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(bc);
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               tokens["EXTBAN"].push_back('c');
        }
 
-       virtual void On005Numeric(std::string &output)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('c');
-       }
-
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+               if ((target.type == MessageTarget::TYPE_CHANNEL) && (IS_LOCAL(user)))
                {
-                       Channel* c = (Channel*)dest;
-                       ModResult res = ServerInstance->OnCheckExemption(user,c,"blockcolor");
+                       Channel* c = target.Get<Channel>();
+                       ModResult res = CheckExemption::Call(exemptionprov, user, c, "blockcolor");
 
                        if (res == MOD_RES_ALLOW)
                                return MOD_RES_PASSTHRU;
 
-                       if (!c->GetExtBanStatus(user, 'c').check(!c->IsModeSet('c')))
+                       if (!c->GetExtBanStatus(user, 'c').check(!c->IsModeSet(bc)))
                        {
-                               for (std::string::iterator i = text.begin(); i != text.end(); i++)
+                               for (std::string::iterator i = details.text.begin(); i != details.text.end(); i++)
                                {
-                                       switch (*i)
+                                       // Block all control codes except \001 for CTCP
+                                       if ((*i >= 0) && (*i < 32) && (*i != 1))
                                        {
-                                               case 2:
-                                               case 3:
-                                               case 15:
-                                               case 21:
-                                               case 22:
-                                               case 31:
-                                                       user->WriteNumeric(404, "%s %s :Can't send colors to channel (+c set)",user->nick.c_str(), c->name.c_str());
-                                                       return MOD_RES_DENY;
-                                               break;
+                                               user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send colors to channel (+c is set)");
+                                               return MOD_RES_DENY;
                                        }
                                }
                        }
@@ -86,16 +67,7 @@ class ModuleBlockColor : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
-       }
-
-       virtual ~ModuleBlockColor()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides channel mode +c to block color",VF_VENDOR);
        }
index b29c58240689be37d9e86a4d8be12193c860ef11..44241e82c5e83151391bb48a33cba37865f50a77 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/cap.h"
+#include "modules/whois.h"
 
-/* $ModDesc: Provides user mode +B to mark the user as a bot */
-
-/** Handles user mode +B
- */
-class BotMode : public SimpleUserModeHandler
+enum
 {
- public:
-       BotMode(Module* Creator) : SimpleUserModeHandler(Creator, "bot", 'B') { }
+       // From UnrealIRCd.
+       RPL_WHOISBOT = 335
 };
 
-class ModuleBotMode : public Module
+class BotTag : public ClientProtocol::MessageTagProvider
 {
-       BotMode bm;
+ private:
+       SimpleUserModeHandler& botmode;
+       Cap::Reference ctctagcap;
+
  public:
-       ModuleBotMode()
-               : bm(this)
+       BotTag(Module* mod, SimpleUserModeHandler& bm)
+               : ClientProtocol::MessageTagProvider(mod)
+               , botmode(bm)
+               , ctctagcap(mod, "message-tags")
        {
        }
 
-       void init()
+       void OnPopulateTags(ClientProtocol::Message& msg) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(bm);
-               Implementation eventlist[] = { I_OnWhois };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               User* const user = msg.GetSourceUser();
+               if (user && user->IsModeSet(botmode))
+                       msg.AddTag("inspircd.org/bot", this, "");
        }
 
-       virtual ~ModuleBotMode()
+       bool ShouldSendTag(LocalUser* user, const ClientProtocol::MessageTagData& tagdata) CXX11_OVERRIDE
        {
+               return ctctagcap.get(user);
        }
+};
+
+class ModuleBotMode : public Module, public Whois::EventListener
+{
+ private:
+       SimpleUserModeHandler bm;
+       BotTag tag;
 
-       virtual Version GetVersion()
+ public:
+       ModuleBotMode()
+               : Whois::EventListener(this)
+               , bm(this, "bot", 'B')
+               , tag(this, bm)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides user mode +B to mark the user as a bot",VF_VENDOR);
        }
 
-       virtual void OnWhois(User* src, User* dst)
+       void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
        {
-               if (dst->IsModeSet('B'))
+               if (whois.GetTarget()->IsModeSet(bm))
                {
-                       ServerInstance->SendWhoisLine(src, dst, 335, src->nick+" "+dst->nick+" :is a bot on "+ServerInstance->Config->Network);
+                       whois.SendLine(RPL_WHOISBOT, "is a bot on " + ServerInstance->Config->Network);
                }
        }
-
 };
 
-
 MODULE_INIT(ModuleBotMode)
index 2df6d7af0cdb46c9a3c42b160bbe266ce69057aa..a13d4d613fb36e2f4ba728959365a0789550e1ac 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/callerid.h"
+#include "modules/ctctags.h"
 
-/* $ModDesc: Implementation of callerid, usermode +g, /accept */
+enum
+{
+       RPL_ACCEPTLIST = 281,
+       RPL_ENDOFACCEPT = 282,
+       ERR_ACCEPTFULL = 456,
+       ERR_ACCEPTEXIST = 457,
+       ERR_ACCEPTNOT = 458,
+       ERR_TARGUMODEG = 716,
+       RPL_TARGNOTIFY = 717,
+       RPL_UMODEGMSG = 718
+};
 
 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) { }
 
@@ -43,7 +58,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.
@@ -56,36 +71,41 @@ class callerid_data
 struct CallerIDExtInfo : public ExtensionItem
 {
        CallerIDExtInfo(Module* parent)
-               : ExtensionItem("callerid_data", parent)
+               : ExtensionItem("callerid_data", ExtensionItem::EXT_USER, parent)
        {
        }
 
-       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const
+       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
        {
-               callerid_data* dat = static_cast<callerid_data*>(item);
-               return dat->ToString(format);
+               std::string ret;
+               if (format != FORMAT_NETWORK)
+               {
+                       callerid_data* dat = static_cast<callerid_data*>(item);
+                       ret = dat->ToString(format);
+               }
+               return ret;
        }
 
-       void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
        {
+               if (format == FORMAT_NETWORK)
+                       return;
+
                void* old = get_raw(container);
                if (old)
-                       this->free(old);
+                       this->free(NULL, old);
                callerid_data* dat = new callerid_data;
                set_raw(container, dat);
 
                irc::commasepstream s(value);
                std::string tok;
                if (s.GetToken(tok))
-                       dat->lastnotify = ConvToInt(tok);
+                       dat->lastnotify = ConvToNum<time_t>(tok);
 
                while (s.GetToken(tok))
                {
-                       if (tok.empty())
-                               continue;
-
                        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)
                                {
@@ -107,39 +127,52 @@ struct CallerIDExtInfo : public ExtensionItem
                return dat;
        }
 
-       void free(void* item)
+       void free(Extensible* container, void* item) CXX11_OVERRIDE
        {
                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);
 
                        if (!targ)
                        {
-                               ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (1)");
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (1)");
                                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
-                               ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (2)");
+                       if (!stdalgo::vector::swaperase(targ->wholistsme, dat))
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (2)");
                }
                delete dat;
        }
 };
 
-class User_g : public SimpleUserModeHandler
-{
-public:
-       User_g(Module* Creator) : SimpleUserModeHandler(Creator, "callerid", 'g') { }
-};
-
 class CommandAccept : public Command
 {
+       /** Pair: first is the target, second is true to add, false to remove
+        */
+       typedef std::pair<User*, bool> ACCEPTAction;
+
+       static ACCEPTAction GetTargetAndAction(std::string& tok, User* cmdfrom = NULL)
+       {
+               bool remove = (tok[0] == '-');
+               if ((remove) || (tok[0] == '+'))
+                       tok.erase(tok.begin());
+
+               User* target;
+               if (!cmdfrom || !IS_LOCAL(cmdfrom))
+                       target = ServerInstance->FindNick(tok);
+               else
+                       target = ServerInstance->FindNickOnly(tok);
+
+               if ((!target) || (target->registered != REG_ALL) || (target->quitting))
+                       target = NULL;
+
+               return std::make_pair(target, !remove);
+       }
+
 public:
        CallerIDExtInfo extInfo;
        unsigned int maxaccepts;
@@ -147,43 +180,22 @@ public:
                extInfo(Creator)
        {
                allow_empty_last_param = false;
-               syntax = "*|(+|-)<nick>[,(+|-)<nick> ...]";
-               TRANSLATE2(TR_CUSTOM, TR_END);
+               syntax = "*|(+|-)<nick>[,(+|-)<nick>]+";
+               TRANSLATE1(TR_CUSTOM);
        }
 
-       virtual void EncodeParameter(std::string& parameter, int index)
+       void EncodeParameter(std::string& parameter, unsigned int index) CXX11_OVERRIDE
        {
-               if (index != 0)
+               // Send lists as-is (part of 2.0 compat)
+               if (parameter.find(',') != std::string::npos)
                        return;
-               std::string out;
-               irc::commasepstream nicks(parameter);
-               std::string tok;
-               while (nicks.GetToken(tok))
-               {
-                       if (tok == "*")
-                       {
-                               continue; // Drop list requests, since remote servers ignore them anyway.
-                       }
-                       if (!out.empty())
-                               out.append(",");
-                       bool dash = false;
-                       if (tok[0] == '-')
-                       {
-                               dash = true;
-                               tok.erase(0, 1); // Remove the dash.
-                       }
-                       else if (tok[0] == '+')
-                               tok.erase(0, 1);
 
-                       User* u = ServerInstance->FindNick(tok);
-                       if ((!u) || (u->registered != REG_ALL) || (u->quitting) || (IS_SERVER(u)))
-                               continue;
+               // Convert a (+|-)<nick> into a [-]<uuid>
+               ACCEPTAction action = GetTargetAndAction(parameter);
+               if (!action.first)
+                       return;
 
-                       if (dash)
-                               out.append("-");
-                       out.append(u->uuid);
-               }
-               parameter = out;
+               parameter = (action.second ? "" : "-") + action.first->uuid;
        }
 
        /** Will take any number of nicks (up to MaxTargets), which can be seperated by commas.
@@ -191,56 +203,60 @@ public:
         * /accept nick1,nick2,nick3,*
         * to add 3 nicks and then show your list
         */
-       CmdResult Handle(const std::vector<std::string> &parameters, User* user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
+               if (CommandParser::LoopCall(user, this, parameters, 0))
                        return CMD_SUCCESS;
+
                /* Even if callerid mode is not set, we let them manage their ACCEPT list so that if they go +g they can
                 * have a list already setup. */
 
-               const std::string& tok = parameters[0];
-
-               if (tok == "*")
+               if (parameters[0] == "*")
                {
-                       if (IS_LOCAL(user))
-                               ListAccept(user);
+                       ListAccept(user);
                        return CMD_SUCCESS;
                }
-               else if (tok[0] == '-')
+
+               std::string tok = parameters[0];
+               ACCEPTAction action = GetTargetAndAction(tok, user);
+               if (!action.first)
                {
-                       User* whotoremove;
-                       if (IS_LOCAL(user))
-                               whotoremove = ServerInstance->FindNickOnly(tok.substr(1));
-                       else
-                               whotoremove = ServerInstance->FindNick(tok.substr(1));
-
-                       if (whotoremove)
-                               return (RemoveAccept(user, whotoremove) ? CMD_SUCCESS : CMD_FAILURE);
-                       else
-                               return CMD_FAILURE;
+                       user->WriteNumeric(Numerics::NoSuchNick(tok));
+                       return CMD_FAILURE;
                }
+
+               if ((!IS_LOCAL(user)) && (!IS_LOCAL(action.first)))
+                       // Neither source nor target is local, forward the command to the server of target
+                       return CMD_SUCCESS;
+
+               // The second item in the pair is true if the first char is a '+' (or nothing), false if it's a '-'
+               if (action.second)
+                       return (AddAccept(user, action.first) ? CMD_SUCCESS : CMD_FAILURE);
                else
-               {
-                       const std::string target = (tok[0] == '+' ? tok.substr(1) : tok);
-                       User* whotoadd;
-                       if (IS_LOCAL(user))
-                               whotoadd = ServerInstance->FindNickOnly(target);
-                       else
-                               whotoadd = ServerInstance->FindNick(target);
-
-                       if ((whotoadd) && (whotoadd->registered == REG_ALL) && (!whotoadd->quitting) && (!IS_SERVER(whotoadd)))
-                               return (AddAccept(user, whotoadd) ? CMD_SUCCESS : CMD_FAILURE);
-                       else
-                       {
-                               user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), tok.c_str());
-                               return CMD_FAILURE;
-                       }
-               }
+                       return (RemoveAccept(user, action.first) ? CMD_SUCCESS : CMD_FAILURE);
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               return ROUTE_BROADCAST;
+               // There is a list in parameters[0] in two cases:
+               // Either when the source is remote, this happens because 2.0 servers send comma seperated uuid lists,
+               // we don't split those but broadcast them, as before.
+               //
+               // Or if the source is local then LoopCall() runs OnPostCommand() after each entry in the list,
+               // meaning the linking module has sent an ACCEPT already for each entry in the list to the
+               // appropiate server and the ACCEPT with the list of nicks (this) doesn't need to be sent anywhere.
+               if ((!IS_LOCAL(user)) && (parameters[0].find(',') != std::string::npos))
+                       return ROUTE_BROADCAST;
+
+               // Find the target
+               std::string targetstring = parameters[0];
+               ACCEPTAction action = GetTargetAndAction(targetstring, user);
+               if (!action.first)
+                       // Target is a "*" or source is local and the target is a list of nicks
+                       return ROUTE_LOCALONLY;
+
+               // Route to the server of the target
+               return ROUTE_UNICAST(action.first->server);
        }
 
        void ListAccept(User* user)
@@ -248,10 +264,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)
-                               user->WriteNumeric(281, "%s %s", user->nick.c_str(), (*i)->nick.c_str());
+                       for (callerid_data::UserSet::iterator i = dat->accepting.begin(); i != dat->accepting.end(); ++i)
+                               user->WriteNumeric(RPL_ACCEPTLIST, (*i)->nick);
                }
-               user->WriteNumeric(282, "%s :End of ACCEPT list", user->nick.c_str());
+               user->WriteNumeric(RPL_ENDOFACCEPT, "End of ACCEPT list");
        }
 
        bool AddAccept(User* user, User* whotoadd)
@@ -260,12 +276,12 @@ public:
                callerid_data* dat = extInfo.get(user, true);
                if (dat->accepting.size() >= maxaccepts)
                {
-                       user->WriteNumeric(456, "%s :Accept list is full (limit is %d)", user->nick.c_str(), 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(457, "%s %s :is already on your accept list", user->nick.c_str(), whotoadd->nick.c_str());
+                       user->WriteNumeric(ERR_ACCEPTEXIST, whotoadd->nick, "is already on your accept list");
                        return false;
                }
 
@@ -273,7 +289,7 @@ public:
                callerid_data *targ = extInfo.get(whotoadd, true);
                targ->wholistsme.push_back(dat);
 
-               user->WriteServ("NOTICE %s :%s is now on your accept list", user->nick.c_str(), whotoadd->nick.c_str());
+               user->WriteNotice(whotoadd->nick + " is now on your accept list");
                return true;
        }
 
@@ -283,48 +299,62 @@ public:
                callerid_data* dat = extInfo.get(user, false);
                if (!dat)
                {
-                       user->WriteNumeric(458, "%s %s :is not on your accept list", user->nick.c_str(), 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(458, "%s %s :is not on your accept list", user->nick.c_str(), 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)
                {
                        // How the fuck is this possible.
-                       ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (3)");
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (3)");
                        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
-                       ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (4)");
+               if (!stdalgo::vector::swaperase(dat2->wholistsme, dat))
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (4)");
 
 
-               user->WriteServ("NOTICE %s :%s is no longer on your accept list", user->nick.c_str(), whotoremove->nick.c_str());
+               user->WriteNotice(whotoremove->nick + " is no longer on your accept list");
                return true;
        }
 };
 
-class ModuleCallerID : public Module
+class CallerIDAPIImpl : public CallerID::APIBase
+{
+ private:
+       CallerIDExtInfo& ext;
+
+ public:
+       CallerIDAPIImpl(Module* Creator, CallerIDExtInfo& Ext)
+               : CallerID::APIBase(Creator)
+               , ext(Ext)
+       {
+       }
+
+       bool IsOnAcceptList(User* source, User* target) CXX11_OVERRIDE
+       {
+               callerid_data* dat = ext.get(target, true);
+               return dat->accepting.count(source);
+       }
+};
+
+
+class ModuleCallerID
+       : public Module
+       , public CTCTags::EventListener
 {
-private:
        CommandAccept cmd;
-       User_g myumode;
+       CallerIDAPIImpl api;
+       SimpleUserModeHandler myumode;
 
        // Configuration variables:
-       bool operoverride; // Operators can override callerid.
        bool tracknick; // Allow ACCEPT entries to update with nick changes.
        unsigned int notify_cooldown; // Seconds between notifications.
 
@@ -339,74 +369,61 @@ private:
                        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
-                               ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (5)");
+                       if (!dat->accepting.erase(who))
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (5)");
                }
 
                userdata->wholistsme.clear();
        }
 
 public:
-       ModuleCallerID() : cmd(this), myumode(this)
+       ModuleCallerID()
+               : CTCTags::EventListener(this)
+               , cmd(this)
+               , api(this, cmd.extInfo)
+               , myumode(this, "callerid", 'g')
        {
        }
 
-       void init()
-       {
-               OnRehash(NULL);
-
-               ServerInstance->Modules->AddService(myumode);
-               ServerInstance->Modules->AddService(cmd);
-               ServerInstance->Modules->AddService(cmd.extInfo);
-
-               Implementation eventlist[] = { I_OnRehash, I_OnUserPostNick, I_OnUserQuit, I_On005Numeric, I_OnUserPreNotice, I_OnUserPreMessage };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModuleCallerID()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Implementation of callerid, provides user mode +g and the ACCEPT command", VF_COMMON | VF_VENDOR);
        }
 
-       virtual Version GetVersion()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               return Version("Implementation of callerid, usermode +g, /accept", VF_COMMON | VF_VENDOR);
+               tokens["ACCEPT"] = ConvToStr(cmd.maxaccepts);
+               tokens["CALLERID"] = ConvToStr(myumode.GetModeChar());
        }
 
-       virtual void On005Numeric(std::string& output)
+       ModResult HandleMessage(User* user, const MessageTarget& target)
        {
-               output += " CALLERID=g";
-       }
+               if (!IS_LOCAL(user) || target.type != MessageTarget::TYPE_USER)
+                       return MOD_RES_PASSTHRU;
 
-       ModResult PreText(User* user, User* dest, std::string& text)
-       {
-               if (!dest->IsModeSet('g') || (user == dest))
+               User* dest = target.Get<User>();
+               if (!dest->IsModeSet(myumode) || (user == dest))
                        return MOD_RES_PASSTHRU;
 
-               if (operoverride && IS_OPER(user))
+               if (user->HasPrivPermission("users/ignore-callerid"))
                        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(716, "%s %s :is in +g mode (server-side ignore).", user->nick.c_str(), 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(717, "%s %s :has been informed that you messaged them.", user->nick.c_str(), dest->nick.c_str());
-                               dest->SendText(":%s 718 %s %s %s@%s :is messaging you, and you have umode +g. Use /ACCEPT +%s to allow.",
-                                       ServerInstance->Config->ServerName.c_str(), 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->GetDisplayedHost().c_str()), InspIRCd::Format("is messaging you, and you have user mode +g set. Use /ACCEPT +%s to allow.",
+                                               user->nick.c_str()));
                                dat->lastnotify = now;
                        }
                        return MOD_RES_DENY;
@@ -414,43 +431,40 @@ public:
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               if (IS_LOCAL(user) && target_type == TYPE_USER)
-                       return PreText(user, (User*)dest, text);
-
-               return MOD_RES_PASSTHRU;
+               return HandleMessage(user, target);
        }
 
-       virtual ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList &exempt_list)
+       ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
        {
-               if (IS_LOCAL(user) && target_type == TYPE_USER)
-                       return PreText(user, (User*)dest, text);
-
-               return MOD_RES_PASSTHRU;
+               return HandleMessage(user, target);
        }
 
-       void OnUserPostNick(User* user, const std::string& oldnick)
+       void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE
        {
                if (!tracknick)
                        RemoveFromAllAccepts(user);
        }
 
-       void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
+       void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
        {
                RemoveFromAllAccepts(user);
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("callerid");
-               cmd.maxaccepts = tag->getInt("maxaccepts", 16);
-               operoverride = tag->getBool("operoverride");
+               cmd.maxaccepts = tag->getUInt("maxaccepts", 30);
                tracknick = tag->getBool("tracknick");
-               notify_cooldown = tag->getInt("cooldown", 60);
+               notify_cooldown = tag->getDuration("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)
-
-
index ae9e824f448c61ab2b94dfe20f29c47572c2a5e0..6e3033bf27edd28e9cd164ef7040697519e5deaa 100644 (file)
@@ -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
 
 
 #include "inspircd.h"
-#include "m_cap.h"
+#include "modules/reload.h"
+#include "modules/cap.h"
 
-/* $ModDesc: Provides the CAP negotiation mechanism seen in ratbox-derived ircds */
+enum
+{
+       // From IRCv3 capability-negotiation-3.1.
+       ERR_INVALIDCAPCMD = 410
+};
 
-/*
-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
        {
-               irc::string subcommand = parameters[0].c_str();
+               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");
+       }
+
+       void OnReloadModuleSave(Module* mod, ReloadModule::CustomData& cd) CXX11_OVERRIDE
+       {
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnReloadModuleSave()");
+               if (mod == creator)
+                       return;
 
-                       CapEvent Data(creator, user, CapEvent::CAPEVENT_REQ);
+               CapModData* capmoddata = new CapModData;
+               cd.add(this, capmoddata);
 
-                       // tokenize the input into a nice list of requested caps
-                       std::string cap_;
-                       irc::spacesepstream cap_stream(parameters[1]);
+               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();
 
-                       while (cap_stream.GetToken(cap_))
+                       // 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)
                        {
-                               // Whilst the handling of extraneous spaces is not currently defined in the CAP specification
-                               // every single other implementation ignores extraneous spaces. Lets copy them for
-                               // compatibility purposes.
-                               trim(cap_);
-                               if (!cap_.empty())
-                                       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.wanted.empty())
+       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)
                        {
-                               user->WriteServ("CAP %s ACK :%s", user->nick.c_str(), parameters[1].c_str());
-                               return CMD_SUCCESS;
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cap %s is no longer available after reload", capdata.name.c_str());
+                               continue;
                        }
 
-                       // HACK: reset all of the caps which were enabled on this user because a cap request is atomic.
-                       for (std::vector<std::pair<GenericCap*, int> >::iterator iter = Data.changed.begin(); iter != Data.changed.end(); ++iter)
-                               iter->first->ext.set(user, iter->second);
+                       // 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)
+                       {
+                               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;
+                               }
 
-                       user->WriteServ("CAP %s NAK :%s", user->nick.c_str(), parameters[1].c_str());
+                               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"))
-               {
-                       CapEvent Data(creator, user, subcommand == "LS" ? CapEvent::CAPEVENT_LS : CapEvent::CAPEVENT_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;
 
-                       reghold.set(user, 1);
-                       Data.Send();
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unregistering cap %s", cap->GetName().c_str());
 
-                       std::string Result;
-                       if (Data.wanted.size() > 0)
-                               Result = irc::stringjoiner(" ", Data.wanted, 0, Data.wanted.size() - 1).GetJoined();
+               // 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));
 
-                       user->WriteServ("CAP %s %s :%s", user->nick.c_str(), subcommand.c_str(), Result.c_str());
+               // 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)
+               {
+                       LocalUser* user = *i;
+                       cap->set(user, false);
                }
-               else if (subcommand == "CLEAR")
+
+               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));
+       }
+
+       Protocol GetProtocol(LocalUser* user) const
+       {
+               return ((capext.get(user) & CAP_302_BIT) ? CAP_302 : CAP_LEGACY);
+       }
+
+       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); )
                {
-                       CapEvent Data(creator, user, CapEvent::CAPEVENT_CLEAR);
+                       bool remove = (capname[0] == '-');
+                       if (remove)
+                               capname.erase(capname.begin());
 
-                       reghold.set(user, 1);
-                       Data.Send();
+                       Capability* cap = ManagerImpl::Find(capname);
+                       if ((!cap) || (!CanRequest(user, usercaps, cap, !remove)))
+                               return false;
 
-                       std::string Result;
-                       if (!Data.ack.empty())
-                               Result = irc::stringjoiner(" ", Data.ack, 0, Data.ack.size() - 1).GetJoined();
-                       user->WriteServ("CAP %s ACK :%s", user->nick.c_str(), Result.c_str());
+                       if (remove)
+                               usercaps = cap->DelFromMask(usercaps);
+                       else
+                               usercaps = cap->AddToMask(usercaps);
                }
-               else
+
+               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)
                {
-                       user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, "%s %s :Invalid CAP subcommand", user->nick.c_str(), subcommand.empty() ? "*" : subcommand.c_str());
-                       return CMD_FAILURE;
+                       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(' ');
                }
+       }
 
-               return CMD_SUCCESS;
+       void HandleClear(LocalUser* user, std::string& result)
+       {
+               HandleList(result, user, false, false, true);
+               capext.unset(user);
        }
 };
 
-class ModuleCAP : public Module
+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 CapMessage : public Cap::MessageBase
 {
-       CommandCAP cmd;
  public:
-       ModuleCAP()
-               : cmd(this)
+       CapMessage(LocalUser* user, const std::string& subcmd, const std::string& result)
+               : Cap::MessageBase(subcmd)
        {
+               SetUser(user);
+               PushParamRef(result);
        }
+};
 
-       void init()
+class CommandCap : public SplitCommand
+{
+       Events::ModuleEventProvider evprov;
+       Cap::ManagerImpl manager;
+       ClientProtocol::EventProvider protoevprov;
+
+       void DisplayResult(LocalUser* user, const std::string& subcmd, std::string& result)
        {
-               ServerInstance->Modules->AddService(cmd);
-               ServerInstance->Modules->AddService(cmd.reghold);
+               if (*result.rbegin() == ' ')
+                       result.erase(result.end()-1);
+               DisplayResult2(user, subcmd, result);
+       }
+
+       void DisplayResult2(LocalUser* user, const std::string& subcmd, const std::string& result)
+       {
+               CapMessage msg(user, subcmd, result);
+               ClientProtocol::Event ev(protoevprov, msg);
+               user->Send(ev);
+       }
+
+ public:
+       LocalIntExt holdext;
 
-               Implementation eventlist[] = { I_OnCheckReady };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+       CommandCap(Module* mod)
+               : SplitCommand(mod, "CAP", 1)
+               , evprov(mod, "event/cap")
+               , manager(mod, evprov)
+               , protoevprov(mod, name)
+               , holdext("cap_hold", ExtensionItem::EXT_USER, mod)
+       {
+               works_before_reg = true;
        }
 
-       ModResult OnCheckReady(LocalUser* user)
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
        {
-               /* Users in CAP state get held until CAP END */
-               if (cmd.reghold.get(user))
-                       return MOD_RES_DENY;
+               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")
+               {
+                       if (parameters.size() < 2)
+                               return CMD_FAILURE;
+
+                       const std::string replysubcmd = (manager.HandleReq(user, parameters[1]) ? "ACK" : "NAK");
+                       DisplayResult2(user, replysubcmd, parameters[1]);
+               }
+               else if (subcommand == "END")
+               {
+                       holdext.unset(user);
+               }
+               else if ((subcommand == "LS") || (subcommand == "LIST"))
+               {
+                       Cap::Protocol capversion = Cap::CAP_LEGACY;
+                       const bool is_ls = (subcommand.length() == 2);
+                       if ((is_ls) && (parameters.size() > 1))
+                       {
+                               unsigned int version = ConvToNum<unsigned int>(parameters[1]);
+                               if (version >= 302)
+                               {
+                                       capversion = Cap::CAP_302;
+                                       manager.Set302Protocol(user);
+                               }
+                       }
 
-               return MOD_RES_PASSTHRU;
+                       std::string result;
+                       // Show values only if supports v3.2 and doing LS
+                       manager.HandleList(result, user, is_ls, ((is_ls) && (capversion != Cap::CAP_LEGACY)));
+                       DisplayResult(user, subcommand, result);
+               }
+               else if ((subcommand == "CLEAR") && (manager.GetProtocol(user) == Cap::CAP_LEGACY))
+               {
+                       std::string result;
+                       manager.HandleClear(user, result);
+                       DisplayResult(user, "ACK", result);
+               }
+               else
+               {
+                       user->WriteNumeric(ERR_INVALIDCAPCMD, subcommand.empty() ? "*" : subcommand, "Invalid CAP subcommand");
+                       return CMD_FAILURE;
+               }
+
+               return CMD_SUCCESS;
        }
+};
+
+class ModuleCap : public Module
+{
+       CommandCap cmd;
 
-       ~ModuleCAP()
+ public:
+       ModuleCap()
+               : cmd(this)
        {
        }
 
-       Version GetVersion()
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
-               return Version("Client CAP extension support", VF_VENDOR);
+               return (cmd.holdext.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU);
        }
-};
 
-MODULE_INIT(ModuleCAP)
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides support for CAP capability negotiation", VF_VENDOR);
+       }
+};
 
+MODULE_INIT(ModuleCap)
diff --git a/src/modules/m_cap.h b/src/modules/m_cap.h
deleted file mode 100644 (file)
index 23cf8cf..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@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/>.
- */
-
-
-#ifndef M_CAP_H
-#define M_CAP_H
-
-class GenericCap;
-
-class CapEvent : public Event
-{
- public:
-       enum CapEventType
-       {
-               CAPEVENT_REQ,
-               CAPEVENT_LS,
-               CAPEVENT_LIST,
-               CAPEVENT_CLEAR
-       };
-
-       CapEventType type;
-       std::vector<std::string> wanted;
-       std::vector<std::string> ack;
-       std::vector<std::pair<GenericCap*, int> > changed; // HACK: clean this up before 2.2
-       User* user;
-       CapEvent(Module* sender, User* u, CapEventType capevtype) : Event(sender, "cap_request"), type(capevtype), user(u) {}
-};
-
-class GenericCap
-{
- public:
-       LocalIntExt ext;
-       const std::string cap;
-       GenericCap(Module* parent, const std::string &Cap) : ext("cap_" + Cap, parent), cap(Cap)
-       {
-               ServerInstance->Modules->AddService(ext);
-       }
-
-       void HandleEvent(Event& ev)
-       {
-               if (ev.id != "cap_request")
-                       return;
-
-               CapEvent *data = static_cast<CapEvent*>(&ev);
-               if (data->type == CapEvent::CAPEVENT_REQ)
-               {
-                       for (std::vector<std::string>::iterator it = data->wanted.begin(); it != data->wanted.end(); ++it)
-                       {
-                               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);
-                                       data->changed.push_back(std::make_pair(this, ext.set(data->user, enablecap ? 1 : 0)));
-                                       break;
-                               }
-                       }
-               }
-               else if (data->type == CapEvent::CAPEVENT_LS)
-               {
-                       data->wanted.push_back(cap);
-               }
-               else if (data->type == CapEvent::CAPEVENT_LIST)
-               {
-                       if (ext.get(data->user))
-                               data->wanted.push_back(cap);
-               }
-               else if (data->type == CapEvent::CAPEVENT_CLEAR)
-               {
-                       data->ack.push_back("-" + cap);
-                       ext.set(data->user, 0);
-               }
-       }
-};
-
-#endif
index fb78e41b29cb45f9dede6dc908c219e94060c203..62fb993afc15f07feb672ca709bcf1f02d4b698f 100644 (file)
 
 #include "inspircd.h"
 #include "xline.h"
+#include "modules/stats.h"
 
-/* $ModDesc: Gives /cban, aka C:lines. Think Q:lines, for channels. */
+enum
+{
+       // InspIRCd-specific.
+       ERR_BADCHANNEL = 926
+};
 
 /** Holds a CBAN item
  */
 class CBan : public XLine
 {
-public:
-       irc::string matchtext;
+private:
+       std::string matchtext;
 
-       CBan(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& ch)
+public:
+       CBan(time_t s_time, unsigned long d, const std::string& src, const std::string& re, const std::string& ch)
                : XLine(s_time, d, src, re, "CBAN")
-       {
-               this->matchtext = ch.c_str();
-       }
-
-       ~CBan()
+               , matchtext(ch)
        {
        }
 
        // XXX I shouldn't have to define this
-       bool Matches(User *u)
+       bool Matches(User* u) CXX11_OVERRIDE
        {
                return false;
        }
 
-       bool Matches(const std::string &s)
+       bool Matches(const std::string& s) CXX11_OVERRIDE
        {
-               if (matchtext == s)
-                       return true;
-               return false;
+               return irc::equals(matchtext, s);
        }
 
-       void DisplayExpiry()
+       const std::string& Displayable() CXX11_OVERRIDE
        {
-               ServerInstance->SNO->WriteToSnoMask('x',"Removing expired CBan %s (set by %s %ld seconds ago)",
-                       this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
-       }
-
-       const char* Displayable()
-       {
-               return matchtext.c_str();
+               return matchtext;
        }
 };
 
@@ -76,12 +70,12 @@ class CBanFactory : public XLineFactory
 
        /** Generate a CBAN
        */
-       XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+       XLine* Generate(time_t set_time, unsigned long duration, const std::string& source, const std::string& reason, const std::string& xline_specific_mask) CXX11_OVERRIDE
        {
                return new CBan(set_time, duration, source, reason, xline_specific_mask);
        }
 
-       bool AutoApplyToUserList(XLine *x)
+       bool AutoApplyToUserList(XLine* x) CXX11_OVERRIDE
        {
                return false; // No, we apply to channels.
        }
@@ -94,31 +88,37 @@ class CommandCBan : public Command
  public:
        CommandCBan(Module* Creator) : Command(Creator, "CBAN", 1, 3)
        {
-               flags_needed = 'o'; this->syntax = "<channel> [<duration> :<reason>]";
-               TRANSLATE4(TR_TEXT,TR_TEXT,TR_TEXT,TR_END);
+               flags_needed = 'o'; this->syntax = "<channel> [<duration> [:<reason>]]";
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                /* syntax: CBAN #channel time :reason goes here */
                /* 'time' is a human-readable timestring, like 2d3h2s. */
 
                if (parameters.size() == 1)
                {
-                       if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "CBAN", user))
+                       std::string reason;
+
+                       if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "CBAN", reason, user))
                        {
-                               ServerInstance->SNO->WriteGlobalSno('x', "%s removed CBan on %s.",user->nick.c_str(),parameters[0].c_str());
+                               ServerInstance->SNO->WriteGlobalSno('x', "%s removed CBan on %s: %s", user->nick.c_str(), parameters[0].c_str(), reason.c_str());
                        }
                        else
                        {
-                               user->WriteServ("NOTICE %s :*** CBan %s not found in list, try /stats C.",user->nick.c_str(),parameters[0].c_str());
+                               user->WriteNotice("*** CBan " + parameters[0] + " not found on the list.");
                                return CMD_FAILURE;
                        }
                }
                else
                {
                        // Adding - XXX todo make this respect <insane> tag perhaps..
-                       long duration = ServerInstance->Duration(parameters[1]);
+                       unsigned long duration;
+                       if (!InspIRCd::Duration(parameters[1], duration))
+                       {
+                               user->WriteNotice("*** Invalid duration for CBan.");
+                               return CMD_FAILURE;
+                       }
                        const char *reason = (parameters.size() > 2) ? parameters[2].c_str() : "No reason supplied";
                        CBan* r = new CBan(ServerInstance->Time(), duration, user->nick.c_str(), reason, parameters[0].c_str());
 
@@ -130,22 +130,22 @@ class CommandCBan : public Command
                                }
                                else
                                {
-                                       time_t c_requires_crap = duration + ServerInstance->Time();
-                                       std::string timestr = ServerInstance->TimeString(c_requires_crap);
-                                       ServerInstance->SNO->WriteGlobalSno('x', "%s added timed CBan for %s, expires on %s: %s", user->nick.c_str(), parameters[0].c_str(), timestr.c_str(), reason);
+                                       ServerInstance->SNO->WriteGlobalSno('x', "%s added timed CBan for %s, expires in %s (on %s): %s",
+                                               user->nick.c_str(), parameters[0].c_str(), InspIRCd::DurationString(duration).c_str(),
+                                               InspIRCd::TimeString(ServerInstance->Time() + duration).c_str(), reason);
                                }
                        }
                        else
                        {
                                delete r;
-                               user->WriteServ("NOTICE %s :*** CBan for %s already exists", user->nick.c_str(), parameters[0].c_str());
+                               user->WriteNotice("*** CBan for " + parameters[0] + " already exists");
                                return CMD_FAILURE;
                        }
                }
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                if (IS_LOCAL(user))
                        return ROUTE_LOCALONLY; // spanningtree will send ADDLINE
@@ -154,61 +154,58 @@ class CommandCBan : public Command
        }
 };
 
-class ModuleCBan : public Module
+class ModuleCBan : public Module, public Stats::EventListener
 {
        CommandCBan mycommand;
        CBanFactory f;
 
  public:
-       ModuleCBan() : mycommand(this)
+       ModuleCBan()
+               : Stats::EventListener(this)
+               , mycommand(this)
        {
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
                ServerInstance->XLines->RegisterFactory(&f);
-
-               ServerInstance->Modules->AddService(mycommand);
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnStats };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       virtual ~ModuleCBan()
+       ~ModuleCBan()
        {
                ServerInstance->XLines->DelAll("CBAN");
                ServerInstance->XLines->UnregisterFactory(&f);
        }
 
-       virtual ModResult OnStats(char symbol, User* user, string_list &out)
+       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;
        }
 
-       virtual ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
                XLine *rl = ServerInstance->XLines->MatchesLine("CBAN", cname);
 
                if (rl)
                {
                        // Channel is banned.
-                       user->WriteServ( "384 %s %s :Cannot join channel, CBANed (%s)", user->nick.c_str(), cname, rl->reason.c_str());
+                       user->WriteNumeric(ERR_BADCHANNEL, cname, InspIRCd::Format("Channel %s is CBANed: %s", cname.c_str(), rl->reason.c_str()));
                        ServerInstance->SNO->WriteGlobalSno('a', "%s tried to join %s which is CBANed (%s)",
-                                user->nick.c_str(), cname, rl->reason.c_str());
+                                user->nick.c_str(), cname.c_str(), rl->reason.c_str());
                        return MOD_RES_DENY;
                }
 
                return MOD_RES_PASSTHRU;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Gives /cban, aka C:lines. Think Q:lines, for channels.", VF_COMMON | VF_VENDOR);
+               return Version("Provides the CBAN command, like Q-lines, but for channels", VF_COMMON | VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleCBan)
-
index 65b965df288f8acac65d152f05cded53d3a9eb86..a97cc8fcc2f2f85324e7a6135a9d861ce6a6436c 100644 (file)
  */
 
 
-/* $ModDesc: Provides user and channel +G mode */
-
-#define _CRT_SECURE_NO_DEPRECATE
-#define _SCL_SECURE_NO_DEPRECATE
-
 #include "inspircd.h"
-#include <iostream>
-
-typedef std::map<irc::string,irc::string> censor_t;
-
-/** Handles usermode +G
- */
-class CensorUser : public SimpleUserModeHandler
-{
- public:
-       CensorUser(Module* Creator) : SimpleUserModeHandler(Creator, "u_censor", 'G') { }
-};
+#include "modules/exemption.h"
 
-/** Handles channel mode +G
- */
-class CensorChannel : public SimpleChannelModeHandler
-{
- public:
-       CensorChannel(Module* Creator) : SimpleChannelModeHandler(Creator, "censor", 'G') { }
-};
+typedef insp::flat_map<std::string, std::string, irc::insensitive_swo> censor_t;
 
 class ModuleCensor : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        censor_t censors;
-       CensorUser cu;
-       CensorChannel cc;
+       SimpleUserModeHandler cu;
+       SimpleChannelModeHandler cc;
 
  public:
-       ModuleCensor() : cu(this), cc(this) { }
-
-       void init()
-       {
-               /* Read the configuration file on startup.
-                */
-               OnRehash(NULL);
-               ServerInstance->Modules->AddService(cu);
-               ServerInstance->Modules->AddService(cc);
-               Implementation eventlist[] = { I_OnRehash, I_OnUserPreMessage, I_OnUserPreNotice };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-
-       virtual ~ModuleCensor()
+       ModuleCensor()
+               : exemptionprov(this)
+               , cu(this, "u_censor", 'G')
+               , cc(this, "censor", 'G')
        {
        }
 
        // format of a config entry is <badword text="shit" replace="poo">
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return MOD_RES_PASSTHRU;
 
-               bool active = false;
+               int numeric = 0;
+               const char* targetname = NULL;
 
-               if (target_type == TYPE_USER)
-                       active = ((User*)dest)->IsModeSet('G');
-               else if (target_type == TYPE_CHANNEL)
+               switch (target.type)
                {
-                       active = ((Channel*)dest)->IsModeSet('G');
-                       Channel* c = (Channel*)dest;
-                       ModResult res = ServerInstance->OnCheckExemption(user,c,"censor");
+                       case MessageTarget::TYPE_USER:
+                       {
+                               User* targuser = target.Get<User>();
+                               if (!targuser->IsModeSet(cu))
+                                       return MOD_RES_PASSTHRU;
+
+                               numeric = ERR_CANTSENDTOUSER;
+                               targetname = targuser->nick.c_str();
+                               break;
+                       }
+
+                       case MessageTarget::TYPE_CHANNEL:
+                       {
+                               Channel* targchan = target.Get<Channel>();
+                               if (!targchan->IsModeSet(cc))
+                                       return MOD_RES_PASSTHRU;
+
+                               ModResult result = CheckExemption::Call(exemptionprov, user, targchan, "censor");
+                               if (result == MOD_RES_ALLOW)
+                                       return MOD_RES_PASSTHRU;
 
-                       if (res == MOD_RES_ALLOW)
+                               numeric = ERR_CANNOTSENDTOCHAN;
+                               targetname = targchan->name.c_str();
+                               break;
+                       }
+
+                       default:
                                return MOD_RES_PASSTHRU;
                }
 
-               if (!active)
-                       return MOD_RES_PASSTHRU;
-
-               irc::string text2 = text.c_str();
                for (censor_t::iterator index = censors.begin(); index != censors.end(); index++)
                {
-                       if (text2.find(index->first) != irc::string::npos)
+                       size_t censorpos;
+                       while ((censorpos = irc::find(details.text, index->first)) != std::string::npos)
                        {
                                if (index->second.empty())
                                {
-                                       user->WriteNumeric(ERR_WORDFILTERED, "%s %s %s :Your message contained a censored word, and was blocked", user->nick.c_str(), ((target_type == TYPE_CHANNEL) ? ((Channel*)dest)->name.c_str() : ((User*)dest)->nick.c_str()), index->first.c_str());
+                                       user->WriteNumeric(numeric, targetname, "Your message contained a censored word (" + index->first + "), and was blocked");
                                        return MOD_RES_DENY;
                                }
 
-                               SearchAndReplace(text2, index->first, index->second);
+                               details.text.replace(censorpos, index->first.size(), index->second);
                        }
                }
-               text = text2.c_str();
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
-       }
-
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                /*
                 * reload our config file on rehash - we must destroy and re-allocate the classes
                 * to call the constructor again and re-read our data.
                 */
-               censors.clear();
+               censor_t newcensors;
 
                ConfigTagList badwords = ServerInstance->Config->ConfTags("badword");
                for (ConfigIter i = badwords.first; i != badwords.second; ++i)
                {
                        ConfigTag* tag = i->second;
-                       std::string str = tag->getString("text");
-                       irc::string pattern(str.c_str());
-                       str = tag->getString("replace");
-                       censors[pattern] = irc::string(str.c_str());
+                       const std::string text = tag->getString("text");
+                       if (text.empty())
+                               throw ModuleException("<badword:text> is empty! at " + tag->getTagLocation());
+
+                       const std::string replace = tag->getString("replace");
+                       newcensors[text] = replace;
                }
+               censors.swap(newcensors);
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides user and channel +G mode",VF_VENDOR);
+               return Version("Provides user and channel mode +G", VF_VENDOR);
        }
 
 };
index 13f1c1fb5f7c1a4e2d3aa7f6b53a8c1af2d9daf8..d80719c1725b6ad03f44d05d8921a6cb9f91ccc1 100644 (file)
 
 
 #include "inspircd.h"
-#include "xline.h"
+#include "modules/ssl.h"
+#include "modules/webirc.h"
+#include "modules/whois.h"
 
-/* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */
+enum
+{
+       // InspIRCd-specific.
+       RPL_WHOISGATEWAY = 350
+};
 
-enum CGItype { PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC };
+// Encapsulates information about an ident host.
+class IdentHost
+{
+ private:
+       std::string hostmask;
+       std::string newident;
 
+ public:
+       IdentHost(const std::string& mask, const std::string& ident)
+               : hostmask(mask)
+               , newident(ident)
+       {
+       }
 
-/** Holds a CGI site's details
- */
-class CGIhost
+       const std::string& GetIdent() const
+       {
+               return newident;
+       }
+
+       bool Matches(LocalUser* user) const
+       {
+               if (!InspIRCd::Match(user->GetRealHost(), hostmask, ascii_case_insensitive_map))
+                       return false;
+
+               return InspIRCd::MatchCIDR(user->GetIPString(), hostmask, ascii_case_insensitive_map);
+       }
+};
+
+// Encapsulates information about a WebIRC host.
+class WebIRCHost
 {
-public:
+ private:
        std::string hostmask;
-       CGItype type;
+       std::string fingerprint;
        std::string password;
+       std::string passhash;
+
+ public:
+       WebIRCHost(const std::string& mask, const std::string& fp, const std::string& pass, const std::string& hash)
+               : hostmask(mask)
+               , fingerprint(fp)
+               , password(pass)
+               , passhash(hash)
+       {
+       }
 
-       CGIhost(const std::string &mask, CGItype t, const std::string &spassword)
-       : hostmask(mask), type(t), password(spassword)
+       bool Matches(LocalUser* user, const std::string& pass, UserCertificateAPI& sslapi) const
        {
+               // Did the user send a valid password?
+               if (!password.empty() && !ServerInstance->PassCompare(user, password, pass, passhash))
+                       return false;
+
+               // Does the user have a valid fingerprint?
+               const std::string fp = sslapi ? sslapi->GetFingerprint(user) : "";
+               if (!fingerprint.empty() && !InspIRCd::TimingSafeCompare(fp, fingerprint))
+                       return false;
+
+               // Does the user's hostname match our hostmask?
+               if (InspIRCd::Match(user->GetRealHost(), hostmask, ascii_case_insensitive_map))
+                       return true;
+
+               // Does the user's IP address match our hostmask?
+               return InspIRCd::MatchCIDR(user->GetIPString(), hostmask, ascii_case_insensitive_map);
        }
 };
-typedef std::vector<CGIhost> CGIHostlist;
 
 /*
  * WEBIRC
  *  This is used for the webirc method of CGIIRC auth, and is (really) the best way to do these things.
- *  Syntax: WEBIRC password client hostname ip
- *  Where password is a shared key, client is the name of the "client" and version (e.g. cgiirc), hostname
+ *  Syntax: WEBIRC password gateway hostname ip
+ *  Where password is a shared key, gateway is the name of the WebIRC gateway and version (e.g. cgiirc), hostname
  *  is the resolved host of the client issuing the command and IP is the real IP of the client.
  *
  * How it works:
  *  To tie in with the rest of cgiirc module, and to avoid race conditions, /webirc is only processed locally
  *  and simply sets metadata on the user, which is later decoded on full connect to give something meaningful.
  */
-class CommandWebirc : public Command
+class CommandWebIRC : public SplitCommand
 {
  public:
+       std::vector<WebIRCHost> hosts;
        bool notify;
+       StringExtItem gateway;
        StringExtItem realhost;
        StringExtItem realip;
-       LocalStringExt webirc_hostname;
-       LocalStringExt webirc_ip;
-
-       CGIHostlist Hosts;
-       CommandWebirc(Module* Creator)
-               : Command(Creator, "WEBIRC", 4),
-                 realhost("cgiirc_realhost", Creator), realip("cgiirc_realip", Creator),
-                 webirc_hostname("cgiirc_webirc_hostname", Creator), webirc_ip("cgiirc_webirc_ip", Creator)
-               {
-                       allow_empty_last_param = false;
-                       works_before_reg = true;
-                       this->syntax = "password client hostname ip";
-               }
-               CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       UserCertificateAPI sslapi;
+       Events::ModuleEventProvider webircevprov;
+
+       CommandWebIRC(Module* Creator)
+               : SplitCommand(Creator, "WEBIRC", 4)
+               , gateway("cgiirc_gateway", ExtensionItem::EXT_USER, Creator)
+               , realhost("cgiirc_realhost", ExtensionItem::EXT_USER, Creator)
+               , realip("cgiirc_realip", ExtensionItem::EXT_USER, Creator)
+               , sslapi(Creator)
+               , webircevprov(Creator, "event/webirc")
+       {
+               allow_empty_last_param = false;
+               works_before_reg = true;
+               this->syntax = "<password> <gateway> <hostname> <ip> [<flags>]";
+       }
+
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               if (user->registered == REG_ALL || realhost.get(user))
+                       return CMD_FAILURE;
+
+               for (std::vector<WebIRCHost>::const_iterator iter = hosts.begin(); iter != hosts.end(); ++iter)
                {
-                       if(user->registered == REG_ALL)
-                               return CMD_FAILURE;
+                       // If we don't match the host then skip to the next host.
+                       if (!iter->Matches(user, parameters[0], sslapi))
+                               continue;
 
                        irc::sockets::sockaddrs ipaddr;
-                       if (!irc::sockets::aptosa(parameters[3], 0, ipaddr))
+                       if (!irc::sockets::aptosa(parameters[3], user->client_sa.port(), 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());
+                               WriteLog("Connecting user %s (%s) tried to use WEBIRC but gave an invalid IP address.",
+                                       user->uuid.c_str(), user->GetIPString().c_str());
+                               ServerInstance->Users->QuitUser(user, "WEBIRC: IP address is invalid: " + parameters[3]);
                                return CMD_FAILURE;
                        }
 
-                       for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
+                       // The user matched a WebIRC block!
+                       gateway.set(user, parameters[1]);
+                       realhost.set(user, user->GetRealHost());
+                       realip.set(user, user->GetIPString());
+
+                       WriteLog("Connecting user %s is using a WebIRC gateway; changing their IP from %s to %s.",
+                               user->uuid.c_str(), user->GetIPString().c_str(), parameters[3].c_str());
+
+                       // If we have custom flags then deal with them.
+                       WebIRC::FlagMap flags;
+                       const bool hasflags = (parameters.size() > 4);
+                       if (hasflags)
                        {
-                               if(InspIRCd::Match(user->host, iter->hostmask, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(user->GetIPString(), iter->hostmask, ascii_case_insensitive_map))
+                               // Parse the flags.
+                               irc::spacesepstream flagstream(parameters[4]);
+                               for (std::string flag; flagstream.GetToken(flag); )
                                {
-                                       if(iter->type == WEBIRC && parameters[0] == iter->password)
+                                       // Does this flag have a value?
+                                       const size_t separator = flag.find('=');
+                                       if (separator == std::string::npos)
                                        {
-                                               realhost.set(user, user->host);
-                                               realip.set(user, user->GetIPString());
-
-                                               // Check if we're happy with the provided hostname. If it's problematic then use the IP instead.
-                                               bool host_ok = (parameters[2].length() < 64) && (parameters[2].find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-") == std::string::npos);
-                                               const std::string& newhost = (host_ok ? parameters[2] : parameters[3]);
-
-                                               if (notify)
-                                                       ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", user->nick.c_str(), user->host.c_str(), newhost.c_str(), user->host.c_str());
-
-                                               webirc_hostname.set(user, newhost);
-                                               webirc_ip.set(user, parameters[3]);
-                                               return CMD_SUCCESS;
+                                               flags[flag];
+                                               continue;
                                        }
+
+                                       // The flag has a value!
+                                       const std::string key = flag.substr(0, separator);
+                                       const std::string value = flag.substr(separator + 1);
+                                       flags[key] = value;
                                }
                        }
 
-                       IS_LOCAL(user)->CommandFloodPenalty += 5000;
-                       ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s tried to use WEBIRC, but didn't match any configured webirc blocks.", user->GetFullRealHost().c_str());
-                       return CMD_FAILURE;
-               }
-};
-
+                       // Inform modules about the WebIRC attempt.
+                       FOREACH_MOD_CUSTOM(webircevprov, WebIRC::EventListener, OnWebIRCAuth, (user, (hasflags ? &flags : NULL)));
 
-/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
- */
-class CGIResolver : public Resolver
-{
-       std::string typ;
-       std::string theiruid;
-       LocalIntExt& waiting;
-       bool notify;
- public:
-       CGIResolver(Module* me, bool NotifyOpers, const std::string &source, LocalUser* u,
-                       const std::string &type, bool &cached, LocalIntExt& ext)
-               : Resolver(source, DNS_QUERY_PTR4, cached, me), typ(type), theiruid(u->uuid),
-               waiting(ext), notify(NotifyOpers)
-       {
-       }
-
-       virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
-       {
-               /* Check the user still exists */
-               User* them = ServerInstance->FindUUID(theiruid);
-               if ((them) && (!them->quitting))
-               {
-                       if (notify)
-                               ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick.c_str(), them->host.c_str(), result.c_str(), typ.c_str());
-
-                       if (result.length() > 64)
-                               return;
-                       them->host = result;
-                       them->dhost = result;
-                       them->InvalidateCache();
-                       them->CheckLines(true);
+                       // Set the IP address sent via WEBIRC. We ignore the hostname and lookup
+                       // instead do our own DNS lookups because of unreliable gateways.
+                       user->SetClientIP(ipaddr);
+                       return CMD_SUCCESS;
                }
-       }
-
-       virtual void OnError(ResolverError e, const std::string &errormessage)
-       {
-               if (!notify)
-                       return;
 
-               User* them = ServerInstance->FindUUID(theiruid);
-               if ((them) && (!them->quitting))
-               {
-                       ServerInstance->SNO->WriteToSnoMask('a', "Connecting user %s detected as using CGI:IRC (%s), but their host can't be resolved from their %s!", them->nick.c_str(), them->host.c_str(), typ.c_str());
-               }
+               WriteLog("Connecting user %s (%s) tried to use WEBIRC but didn't match any configured WebIRC hosts.",
+                       user->uuid.c_str(), user->GetIPString().c_str());
+               ServerInstance->Users->QuitUser(user, "WEBIRC: you don't match any configured WebIRC hosts.");
+               return CMD_FAILURE;
        }
 
-       virtual ~CGIResolver()
+       void WriteLog(const char* message, ...) CUSTOM_PRINTF(2, 3)
        {
-               User* them = ServerInstance->FindUUID(theiruid);
-               if (!them)
-                       return;
-               int count = waiting.get(them);
-               if (count)
-                       waiting.set(them, count - 1);
+               std::string buffer;
+               VAFORMAT(buffer, message, message);
+
+               // If we are sending a snotice then the message will already be
+               // written to the logfile.
+               if (notify)
+                       ServerInstance->SNO->WriteGlobalSno('w', buffer);
+               else
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, buffer);
        }
 };
 
-class ModuleCgiIRC : public Module
+class ModuleCgiIRC
+       : public Module
+       , public WebIRC::EventListener
+       , public Whois::EventListener
 {
-       CommandWebirc cmd;
-       LocalIntExt waiting;
-
-       static void RecheckClass(LocalUser* user)
-       {
-               user->MyClass = NULL;
-               user->SetClass();
-               user->CheckClass();
-       }
+ private:
+       CommandWebIRC cmd;
+       std::vector<IdentHost> hosts;
 
-       static void ChangeIP(LocalUser* user, const std::string& newip)
+       static bool ParseIdent(LocalUser* user, irc::sockets::sockaddrs& out)
        {
-               ServerInstance->Users->RemoveCloneCounts(user);
-               const std::string oldip(user->GetIPString());
-               user->SetClientIP(newip.c_str());
-               user->InvalidateCache();
-               if (user->host == oldip)
-                       user->host = user->GetIPString();
-               if (user->dhost == oldip)
-                       user->dhost = user->GetIPString();
-               ServerInstance->Users->AddLocalClone(user);
-               ServerInstance->Users->AddGlobalClone(user);
-       }
-
-       void HandleIdentOrPass(LocalUser* user, const std::string& newip, bool was_pass)
-       {
-               cmd.realhost.set(user, user->host);
-               cmd.realip.set(user, user->GetIPString());
-               user->host = user->dhost = user->GetIPString();
-               ChangeIP(user, newip);
-               user->InvalidateCache();
-               RecheckClass(user);
-               // Don't create the resolver if the core couldn't put the user in a connect class or when dns is disabled
-               if (user->quitting || ServerInstance->Config->NoUserDns)
-                       return;
-
-               try
+               const char* ident = NULL;
+               if (user->ident.length() == 8)
+               {
+                       // The ident is an IPv4 address encoded in hexadecimal with two characters
+                       // per address segment.
+                       ident = user->ident.c_str();
+               }
+               else if (user->ident.length() == 9 && user->ident[0] == '~')
                {
-                       bool cached;
-                       CGIResolver* r = new CGIResolver(this, cmd.notify, newip, user, (was_pass ? "PASS" : "IDENT"), cached, waiting);
-                       waiting.set(user, waiting.get(user) + 1);
-                       ServerInstance->AddResolver(r, cached);
+                       // The same as above but m_ident got to this user before we did. Strip the
+                       // ident prefix and continue as normal.
+                       ident = user->ident.c_str() + 1;
                }
-               catch (...)
+               else
                {
-                       if (cmd.notify)
-                                ServerInstance->SNO->WriteToSnoMask('a', "Connecting user %s detected as using CGI:IRC (%s), but I could not resolve their hostname!", user->nick.c_str(), user->host.c_str());
+                       // The user either does not have an IPv4 in their ident or the gateway server
+                       // is also running an identd. In the latter case there isn't really a lot we
+                       // can do so we just assume that the client in question is not connecting via
+                       // an ident gateway.
+                       return false;
                }
+
+               // Try to convert the IP address to a string. If this fails then the user
+               // does not have an IPv4 address in their ident.
+               errno = 0;
+               unsigned long address = strtoul(ident, NULL, 16);
+               if (errno)
+                       return false;
+
+               out.in4.sin_family = AF_INET;
+               out.in4.sin_addr.s_addr = htonl(address);
+               return true;
        }
 
-public:
-       ModuleCgiIRC() : cmd(this), waiting("cgiirc-delay", this)
+ public:
+       ModuleCgiIRC()
+               : WebIRC::EventListener(this)
+               , Whois::EventListener(this)
+               , cmd(this)
        {
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
-               OnRehash(NULL);
-               ServiceProvider* providerlist[] = { &cmd, &cmd.realhost, &cmd.realip, &cmd.webirc_hostname, &cmd.webirc_ip, &waiting };
-               ServerInstance->Modules->AddServices(providerlist, sizeof(providerlist)/sizeof(ServiceProvider*));
-
-               Implementation eventlist[] = { I_OnRehash, I_OnUserRegister, I_OnCheckReady };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               ServerInstance->SNO->EnableSnomask('w', "CGIIRC");
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               cmd.Hosts.clear();
-
-               // Do we send an oper notice when a CGI:IRC has their host changed?
-               cmd.notify = ServerInstance->Config->ConfValue("cgiirc")->getBool("opernotice", true);
+               std::vector<IdentHost> identhosts;
+               std::vector<WebIRCHost> webirchosts;
 
                ConfigTagList tags = ServerInstance->Config->ConfTags("cgihost");
                for (ConfigIter i = tags.first; i != tags.second; ++i)
                {
                        ConfigTag* tag = i->second;
-                       std::string hostmask = tag->getString("mask"); // An allowed CGI:IRC host
-                       std::string type = tag->getString("type"); // What type of user-munging we do on this host.
-                       std::string password = tag->getString("password");
 
-                       if(hostmask.length())
+                       // Ensure that we have the <cgihost:mask> parameter.
+                       const std::string mask = tag->getString("mask");
+                       if (mask.empty())
+                               throw ModuleException("<cgihost:mask> is a mandatory field, at " + tag->getTagLocation());
+
+                       // Determine what lookup type this host uses.
+                       const std::string type = tag->getString("type");
+                       if (stdalgo::string::equalsci(type, "ident"))
                        {
-                               if (type == "webirc" && password.empty())
-                               {
-                                       ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
-                               }
-                               else
-                               {
-                                       CGItype cgitype;
-                                       if (type == "pass")
-                                               cgitype = PASS;
-                                       else if (type == "ident")
-                                               cgitype = IDENT;
-                                       else if (type == "passfirst")
-                                               cgitype = PASSFIRST;
-                                       else if (type == "webirc")
-                                               cgitype = WEBIRC;
-                                       else
-                                       {
-                                               cgitype = PASS;
-                                               ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc.so: Invalid <cgihost:type> value in config: %s, setting it to \"pass\"", type.c_str());
-                                       }
+                               // The IP address should be looked up from the hex IP address.
+                               const std::string newident = tag->getString("newident", "gateway", ServerInstance->IsIdent);
+                               identhosts.push_back(IdentHost(mask, newident));
+                       }
+                       else if (stdalgo::string::equalsci(type, "webirc"))
+                       {
+                               // The IP address will be received via the WEBIRC command.
+                               const std::string fingerprint = tag->getString("fingerprint");
+                               const std::string password = tag->getString("password");
 
-                                       cmd.Hosts.push_back(CGIhost(hostmask, cgitype, password));
-                               }
+                               // WebIRC blocks require a password.
+                               if (fingerprint.empty() && password.empty())
+                                       throw ModuleException("When using <cgihost type=\"webirc\"> either the fingerprint or password field is required, at " + tag->getTagLocation());
+
+                               webirchosts.push_back(WebIRCHost(mask, fingerprint, password, tag->getString("hash")));
                        }
                        else
                        {
-                               ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc.so: Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
-                               continue;
+                               throw ModuleException(type + " is an invalid <cgihost:mask> type, at " + tag->getTagLocation());
                        }
                }
+
+               // The host configuration was valid so we can apply it.
+               hosts.swap(identhosts);
+               cmd.hosts.swap(webirchosts);
+
+               // Do we send an oper notice when a m_cgiirc client has their IP changed?
+               cmd.notify = ServerInstance->Config->ConfValue("cgiirc")->getBool("opernotice", true);
        }
 
-       ModResult OnCheckReady(LocalUser *user)
+       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
        {
-               if (waiting.get(user))
+               // If <connect:webirc> is not set then we have nothing to do.
+               const std::string webirc = myclass->config->getString("webirc");
+               if (webirc.empty())
+                       return MOD_RES_PASSTHRU;
+
+               // If the user is not connecting via a WebIRC gateway then they
+               // cannot match this connect class.
+               const std::string* gateway = cmd.gateway.get(user);
+               if (!gateway)
                        return MOD_RES_DENY;
 
-               std::string *webirc_ip = cmd.webirc_ip.get(user);
-               if (!webirc_ip)
-                       return MOD_RES_PASSTHRU;
+               // If the gateway matches the <connect:webirc> constraint then
+               // allow the check to continue. Otherwise, reject it.
+               return InspIRCd::Match(*gateway, webirc) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
+       }
 
-               std::string* webirc_hostname = cmd.webirc_hostname.get(user);
-               user->host = user->dhost = (webirc_hostname ? *webirc_hostname : user->GetIPString());
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
+       {
+               // There is no need to check for gateways if one is already being used.
+               if (cmd.realhost.get(user))
+                       return MOD_RES_PASSTHRU;
 
-               ChangeIP(user, *webirc_ip);
-               user->InvalidateCache();
+               for (std::vector<IdentHost>::const_iterator iter = hosts.begin(); iter != hosts.end(); ++iter)
+               {
+                       // If we don't match the host then skip to the next host.
+                       if (!iter->Matches(user))
+                               continue;
 
-               RecheckClass(user);
-               if (user->quitting)
-                       return MOD_RES_DENY;
+                       // We have matched an <cgihost> block! Try to parse the encoded IPv4 address
+                       // out of the ident.
+                       irc::sockets::sockaddrs address(user->client_sa);
+                       if (!ParseIdent(user, address))
+                               return MOD_RES_PASSTHRU;
 
-               user->CheckLines(true);
-               if (user->quitting)
-                       return MOD_RES_DENY;
+                       // Store the hostname and IP of the gateway for later use.
+                       cmd.realhost.set(user, user->GetRealHost());
+                       cmd.realip.set(user, user->GetIPString());
 
-               cmd.webirc_hostname.unset(user);
-               cmd.webirc_ip.unset(user);
+                       const std::string& newident = iter->GetIdent();
+                       cmd.WriteLog("Connecting user %s is using an ident gateway; changing their IP from %s to %s and their ident from %s to %s.",
+                               user->uuid.c_str(), user->GetIPString().c_str(), address.addr().c_str(), user->ident.c_str(), newident.c_str());
 
+                       user->ChangeIdent(newident);
+                       user->SetClientIP(address);
+                       break;
+               }
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnUserRegister(LocalUser* user)
+       void OnWebIRCAuth(LocalUser* user, const WebIRC::FlagMap* flags) CXX11_OVERRIDE
        {
-               for(CGIHostlist::iterator iter = cmd.Hosts.begin(); iter != cmd.Hosts.end(); iter++)
+               // We are only interested in connection flags. If none have been
+               // given then we have nothing to do.
+               if (!flags)
+                       return;
+
+               WebIRC::FlagMap::const_iterator cport = flags->find("remote-port");
+               if (cport != flags->end())
                {
-                       if(InspIRCd::Match(user->host, iter->hostmask, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(user->GetIPString(), iter->hostmask, ascii_case_insensitive_map))
+                       // If we can't parse the port then just give up.
+                       uint16_t port = ConvToNum<uint16_t>(cport->second);
+                       if (port)
                        {
-                               // Deal with it...
-                               if(iter->type == PASS)
-                               {
-                                       CheckPass(user); // We do nothing if it fails so...
-                                       user->CheckLines(true);
-                               }
-                               else if(iter->type == PASSFIRST && !CheckPass(user))
-                               {
-                                       // If the password lookup failed, try the ident
-                                       CheckIdent(user);       // If this fails too, do nothing
-                                       user->CheckLines(true);
-                               }
-                               else if(iter->type == IDENT)
-                               {
-                                       CheckIdent(user); // Nothing on failure.
-                                       user->CheckLines(true);
-                               }
-                               else if(iter->type == IDENTFIRST && !CheckIdent(user))
-                               {
-                                       // If the ident lookup fails, try the password.
-                                       CheckPass(user);
-                                       user->CheckLines(true);
-                               }
-                               else if(iter->type == WEBIRC)
+                               switch (user->client_sa.family())
                                {
-                                       // We don't need to do anything here
+                                       case AF_INET:
+                                               user->client_sa.in4.sin_port = htons(port);
+                                               break;
+
+                                       case AF_INET6:
+                                               user->client_sa.in6.sin6_port = htons(port);
+                                               break;
+
+                                       default:
+                                               // If we have reached this point then we have encountered a bug.
+                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: OnWebIRCAuth(%s): socket type %d is unknown!",
+                                                       user->uuid.c_str(), user->client_sa.family());
+                                               return;
                                }
-                               return MOD_RES_PASSTHRU;
                        }
                }
-               return MOD_RES_PASSTHRU;
-       }
 
-       bool CheckPass(LocalUser* user)
-       {
-               if(IsValidHost(user->password))
+               WebIRC::FlagMap::const_iterator sport = flags->find("local-port");
+               if (sport != flags->end())
                {
-                       HandleIdentOrPass(user, user->password, true);
-                       user->password.clear();
-                       return true;
+                       // If we can't parse the port then just give up.
+                       uint16_t port = ConvToNum<uint16_t>(sport->second);
+                       if (port)
+                       {
+                               switch (user->server_sa.family())
+                               {
+                                       case AF_INET:
+                                               user->server_sa.in4.sin_port = htons(port);
+                                               break;
+
+                                       case AF_INET6:
+                                               user->server_sa.in6.sin6_port = htons(port);
+                                               break;
+
+                                       default:
+                                               // If we have reached this point then we have encountered a bug.
+                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: OnWebIRCAuth(%s): socket type %d is unknown!",
+                                                       user->uuid.c_str(), user->server_sa.family());
+                                               return;
+                               }
+                       }
                }
-
-               return false;
        }
 
-       bool CheckIdent(LocalUser* user)
+       void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
        {
-               const char* ident;
-               in_addr newip;
-
-               if (user->ident.length() == 8)
-                       ident = user->ident.c_str();
-               else if (user->ident.length() == 9 && user->ident[0] == '~')
-                       ident = user->ident.c_str() + 1;
-               else
-                       return false;
-
-               errno = 0;
-               unsigned long ipaddr = strtoul(ident, NULL, 16);
-               if (errno)
-                       return false;
-               newip.s_addr = htonl(ipaddr);
-               std::string newipstr(inet_ntoa(newip));
-
-               user->ident = "~cgiirc";
-               HandleIdentOrPass(user, newipstr, false);
-
-               return true;
-       }
-
-       bool IsValidHost(const std::string &host)
-       {
-               if(!host.size() || host.size() > 64)
-                       return false;
-
-               for(unsigned int i = 0; i < host.size(); i++)
-               {
-                       if(     ((host[i] >= '0') && (host[i] <= '9')) ||
-                                       ((host[i] >= 'A') && (host[i] <= 'Z')) ||
-                                       ((host[i] >= 'a') && (host[i] <= 'z')) ||
-                                       ((host[i] == '-') && (i > 0) && (i+1 < host.size()) && (host[i-1] != '.') && (host[i+1] != '.')) ||
-                                       ((host[i] == '.') && (i > 0) && (i+1 < host.size())) )
+               if (!whois.IsSelfWhois() && !whois.GetSource()->HasPrivPermission("users/auspex"))
+                       return;
 
-                               continue;
-                       else
-                               return false;
-               }
+               // If these fields are not set then the client is not using a gateway.
+               const std::string* realhost = cmd.realhost.get(whois.GetTarget());
+               const std::string* realip = cmd.realip.get(whois.GetTarget());
+               if (!realhost || !realip)
+                       return;
 
-               return true;
+               const std::string* gateway = cmd.gateway.get(whois.GetTarget());
+               if (gateway)
+                       whois.SendLine(RPL_WHOISGATEWAY, *realhost, *realip, "is connected via the " + *gateway + " WebIRC gateway");
+               else
+                       whois.SendLine(RPL_WHOISGATEWAY, *realhost, *realip, "is connected via an ident gateway");
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Change user's hosts connecting from known CGI:IRC hosts",VF_VENDOR);
+               return Version("Enables forwarding the real IP address of a user from a gateway to the IRC server", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleCgiIRC)
index 997a926489b72e6b1e64df525a3a8885d82c36e6..4f9e4e80326b293cb7fd1fec881c5fc99e8e4504 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides snomasks 'j' and 'J', to which notices about newly created channels are sent */
-
 class ModuleChanCreate : public Module
 {
- private:
  public:
-       void init()
+       void init() CXX11_OVERRIDE
        {
                ServerInstance->SNO->EnableSnomask('j', "CHANCREATE");
-               Implementation eventlist[] = { I_OnUserJoin };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides snomasks 'j' and 'J', to which notices about newly created channels are sent",VF_VENDOR);
+               return Version("Provides snomasks 'j' and 'J', to which notices about newly created channels are sent", VF_VENDOR);
        }
 
-
-       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& except)
+       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& except) CXX11_OVERRIDE
        {
                if ((created) && (IS_LOCAL(memb->user)))
                {
index 651e659b59d437bfd468573ffc1a676a0d03c7f5..051b8c60d0d9062f14f74c18d1749141c769b210 100644 (file)
  */
 
 
-/* $ModDesc: Provides channel-specific censor lists (like mode +G but varies from channel to channel) */
-
-#define _CRT_SECURE_NO_DEPRECATE
-#define _SCL_SECURE_NO_DEPRECATE
-
 #include "inspircd.h"
-#include "u_listmode.h"
+#include "listmode.h"
+#include "modules/exemption.h"
 
 /** Handles channel mode +g
  */
 class ChanFilter : public ListModeBase
 {
  public:
-       ChanFilter(Module* Creator) : ListModeBase(Creator, "filter", 'g', "End of channel spamfilter list", 941, 940, false, "chanfilter") { }
+       unsigned long maxlen;
+
+       ChanFilter(Module* Creator)
+               : ListModeBase(Creator, "filter", 'g', "End of channel spamfilter list", 941, 940, false)
+       {
+       }
 
-       virtual bool ValidateParam(User* user, Channel* chan, std::string &word)
+       bool ValidateParam(User* user, Channel* chan, std::string& word) CXX11_OVERRIDE
        {
-               if ((word.length() > 35) || (word.empty()))
+               if (word.length() > maxlen)
                {
-                       user->WriteNumeric(935, "%s %s %s :word is too %s for censor list",user->nick.c_str(), chan->name.c_str(), word.c_str(), (word.empty() ? "short" : "long"));
+                       user->WriteNumeric(Numerics::InvalidModeParameter(chan, this, word, "Word is too long for the spamfilter list"));
                        return false;
                }
 
                return true;
        }
-
-       virtual bool TellListTooLong(User* user, Channel* chan, std::string &word)
-       {
-               user->WriteNumeric(939, "%s %s %s :Channel spamfilter list is full", user->nick.c_str(), chan->name.c_str(), word.c_str());
-               return true;
-       }
-
-       virtual void TellAlreadyOnList(User* user, Channel* chan, std::string &word)
-       {
-               user->WriteNumeric(937, "%s %s :The word %s is already on the spamfilter list",user->nick.c_str(), chan->name.c_str(), word.c_str());
-       }
-
-       virtual void TellNotSet(User* user, Channel* chan, std::string &word)
-       {
-               user->WriteNumeric(938, "%s %s :No such spamfilter word is set",user->nick.c_str(), chan->name.c_str());
-       }
 };
 
 class ModuleChanFilter : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        ChanFilter cf;
        bool hidemask;
+       bool notifyuser;
 
  public:
 
        ModuleChanFilter()
-               : cf(this)
+               : exemptionprov(this)
+               , cf(this)
        {
        }
 
-       void init()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cf);
-
-               cf.DoImplements(this);
-               Implementation eventlist[] = { I_OnRehash, I_OnUserPreMessage, I_OnUserPreNotice, I_OnSyncChannel };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
-               OnRehash(NULL);
-       }
-
-       virtual void OnRehash(User* user)
-       {
-               hidemask = ServerInstance->Config->ConfValue("chanfilter")->getBool("hidemask");
+               ConfigTag* tag = ServerInstance->Config->ConfValue("chanfilter");
+               hidemask = tag->getBool("hidemask");
+               cf.maxlen = tag->getUInt("maxlen", 35, 10, 100);
+               notifyuser = tag->getBool("notifyuser", true);
                cf.DoRehash();
        }
 
-       virtual ModResult ProcessMessages(User* user,Channel* chan,std::string &text)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               ModResult res = ServerInstance->OnCheckExemption(user,chan,"filter");
+               if (target.type != MessageTarget::TYPE_CHANNEL)
+                       return MOD_RES_PASSTHRU;
+
+               Channel* chan = target.Get<Channel>();
+               ModResult res = CheckExemption::Call(exemptionprov, user, chan, "filter");
 
                if (!IS_LOCAL(user) || res == MOD_RES_ALLOW)
                        return MOD_RES_PASSTHRU;
 
-               modelist* list = cf.extItem.get(chan);
+               ListModeBase::ModeList* list = cf.GetList(chan);
 
                if (list)
                {
-                       for (modelist::iterator i = list->begin(); i != list->end(); i++)
+                       for (ListModeBase::ModeList::iterator i = list->begin(); i != list->end(); i++)
                        {
-                               if (InspIRCd::Match(text, i->mask))
+                               if (InspIRCd::Match(details.text, i->mask))
                                {
+                                       if (!notifyuser)
+                                       {
+                                               details.echo_original = true;
+                                               return MOD_RES_DENY;
+                                       }
+
                                        if (hidemask)
-                                               user->WriteNumeric(404, "%s %s :Cannot send to channel (your message contained a censored word)",user->nick.c_str(), chan->name.c_str());
+                                               user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (your message contained a censored word)");
                                        else
-                                               user->WriteNumeric(404, "%s %s %s :Cannot send to channel (your message contained a censored word)",user->nick.c_str(), chan->name.c_str(), i->mask.c_str());
+                                               user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (your message contained a censored word: " + i->mask + ")");
                                        return MOD_RES_DENY;
                                }
                        }
@@ -122,32 +112,14 @@ class ModuleChanFilter : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               if (target_type == TYPE_CHANNEL)
-               {
-                       return ProcessMessages(user,(Channel*)dest,text);
-               }
-               return MOD_RES_PASSTHRU;
-       }
-
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
-       }
-
-       virtual void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
-       {
-               cf.DoSyncChannel(chan, proto, opaque);
-       }
+               // We don't send any link data if the length is 35 for compatibility with the 2.0 branch.
+               std::string maxfilterlen;
+               if (cf.maxlen != 35)
+                       maxfilterlen.assign(ConvToStr(cf.maxlen));
 
-       virtual Version GetVersion()
-       {
-               return Version("Provides channel-specific censor lists (like mode +G but varies from channel to channel)", VF_VENDOR);
-       }
-
-       virtual ~ModuleChanFilter()
-       {
+               return Version("Provides channel-specific censor lists (like mode +G but varies from channel to channel)", VF_VENDOR, maxfilterlen);
        }
 };
 
index 08f31657853bc610e9c8f8cad69f2930a2c43a2c..540fa29087cbb548307826967d6429e5ae7c1ee7 100644 (file)
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides channel history for a given number of lines */
+#include "modules/ircv3_servertime.h"
+#include "modules/ircv3_batch.h"
+#include "modules/server.h"
 
 struct HistoryItem
 {
        time_t ts;
-       std::string line;
-       HistoryItem(const std::string& Line) : ts(ServerInstance->Time()), line(Line) {}
+       std::string text;
+       std::string sourcemask;
+
+       HistoryItem(User* source, const std::string& Text)
+               : ts(ServerInstance->Time())
+               , text(Text)
+               , sourcemask(source->GetFullHost())
+       {
+       }
 };
 
 struct HistoryList
 {
        std::deque<HistoryItem> lines;
        unsigned int maxlen, maxtime;
-       HistoryList(unsigned int len, unsigned int time) : maxlen(len), maxtime(time) {}
+       std::string param;
+
+       HistoryList(unsigned int len, unsigned int time, const std::string& oparam)
+               : maxlen(len), maxtime(time), param(oparam) { }
 };
 
-class HistoryMode : public ModeHandler
+class HistoryMode : public ParamMode<HistoryMode, SimpleExtItem<HistoryList> >
 {
-       bool IsValidDuration(const std::string& duration)
+ public:
+       unsigned int maxlines;
+       HistoryMode(Module* Creator)
+               : ParamMode<HistoryMode, SimpleExtItem<HistoryList> >(Creator, "history", 'H')
+       {
+       }
+
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
        {
-               for (std::string::const_iterator i = duration.begin(); i != duration.end(); ++i)
+               std::string::size_type colon = parameter.find(':');
+               if (colon == std::string::npos)
                {
-                       unsigned char c = *i;
-                       if (((c >= '0') && (c <= '9')) || (c == 's') || (c == 'S'))
-                               continue;
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+                       return MODEACTION_DENY;
+               }
 
-                       if (duration_multi[c] == 1)
-                               return false;
+               std::string duration(parameter, colon+1);
+               if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!InspIRCd::IsValidDuration(duration))))
+               {
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+                       return MODEACTION_DENY;
                }
-               return true;
-       }
 
- public:
-       SimpleExtItem<HistoryList> ext;
-       unsigned int maxlines;
-       HistoryMode(Module* Creator) : ModeHandler(Creator, "history", 'H', PARAM_SETONLY, MODETYPE_CHANNEL),
-               ext("history", Creator) { }
+               unsigned int len = ConvToNum<unsigned int>(parameter.substr(0, colon));
+               unsigned long time;
+               if (!InspIRCd::Duration(duration, time) || len == 0 || (len > maxlines && IS_LOCAL(source)))
+               {
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+                       return MODEACTION_DENY;
+               }
+               if (len > maxlines)
+                       len = maxlines;
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
-       {
-               if (adding)
+               HistoryList* history = ext.get(channel);
+               if (history)
                {
-                       std::string::size_type colon = parameter.find(':');
-                       if (colon == std::string::npos)
-                               return MODEACTION_DENY;
-
-                       std::string duration = parameter.substr(colon+1);
-                       if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!IsValidDuration(duration))))
-                               return MODEACTION_DENY;
-
-                       unsigned int len = ConvToInt(parameter.substr(0, colon));
-                       int time = ServerInstance->Duration(duration);
-                       if (len == 0 || time < 0)
-                               return MODEACTION_DENY;
-                       if (len > maxlines && IS_LOCAL(source))
-                               return MODEACTION_DENY;
-                       if (len > maxlines)
-                               len = maxlines;
-                       if (parameter == channel->GetModeParameter(this))
-                               return MODEACTION_DENY;
-
-                       HistoryList* history = ext.get(channel);
-                       if (history)
-                       {
-                               // Shrink the list if the new line number limit is lower than the old one
-                               if (len < history->lines.size())
-                                       history->lines.erase(history->lines.begin(), history->lines.begin() + (history->lines.size() - len));
+                       // Shrink the list if the new line number limit is lower than the old one
+                       if (len < history->lines.size())
+                               history->lines.erase(history->lines.begin(), history->lines.begin() + (history->lines.size() - len));
 
-                               history->maxlen = len;
-                               history->maxtime = time;
-                       }
-                       else
-                       {
-                               ext.set(channel, new HistoryList(len, time));
-                       }
-                       channel->SetModeParam('H', parameter);
+                       history->maxlen = len;
+                       history->maxtime = time;
+                       history->param = parameter;
                }
                else
                {
-                       if (!channel->IsModeSet('H'))
-                               return MODEACTION_DENY;
-                       ext.unset(channel);
-                       channel->SetModeParam('H', "");
+                       ext.set(channel, new HistoryList(len, time, parameter));
                }
                return MODEACTION_ALLOW;
        }
+
+       void SerializeParam(Channel* chan, const HistoryList* history, std::string& out)
+       {
+               out.append(history->param);
+       }
 };
 
-class ModuleChanHistory : public Module
+class ModuleChanHistory
+       : public Module
+       , public ServerEventListener
 {
        HistoryMode m;
        bool sendnotice;
+       UserModeReference botmode;
        bool dobots;
- public:
-       ModuleChanHistory() : m(this)
-       {
-       }
+       IRCv3::Batch::CapReference batchcap;
+       IRCv3::Batch::API batchmanager;
+       IRCv3::Batch::Batch batch;
+       IRCv3::ServerTime::API servertimemanager;
 
-       void init()
+ public:
+       ModuleChanHistory()
+               : ServerEventListener(this)
+               , m(this)
+               , botmode(this, "bot")
+               , batchcap(this)
+               , batchmanager(this)
+               , batch("chathistory")
+               , servertimemanager(this)
        {
-               ServerInstance->Modules->AddService(m);
-               ServerInstance->Modules->AddService(m.ext);
-
-               Implementation eventlist[] = { I_OnPostJoin, I_OnUserMessage, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
        }
 
-       void OnRehash(User*)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("chanhistory");
-               m.maxlines = tag->getInt("maxlines", 50);
+               m.maxlines = tag->getUInt("maxlines", 50, 1);
                sendnotice = tag->getBool("notice", true);
                dobots = tag->getBool("bots", true);
        }
 
-       void OnUserMessage(User* user,void* dest,int target_type, const std::string &text, char status, const CUList&)
+       ModResult OnBroadcastMessage(Channel* channel, const Server* server) CXX11_OVERRIDE
        {
-               if (target_type == TYPE_CHANNEL && status == 0)
+               return channel->IsModeSet(m) ? MOD_RES_ALLOW : MOD_RES_PASSTHRU;
+       }
+
+       void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
+       {
+               if ((target.type == MessageTarget::TYPE_CHANNEL) && (target.status == 0) && (details.type == MSG_PRIVMSG))
                {
-                       Channel* c = (Channel*)dest;
+                       Channel* c = target.Get<Channel>();
                        HistoryList* list = m.ext.get(c);
                        if (list)
                        {
-                               char buf[MAXBUF];
-                               snprintf(buf, MAXBUF, ":%s PRIVMSG %s :%s",
-                                       user->GetFullHost().c_str(), c->name.c_str(), text.c_str());
-                               list->lines.push_back(HistoryItem(buf));
+                               list->lines.push_back(HistoryItem(user, details.text));
                                if (list->lines.size() > list->maxlen)
                                        list->lines.pop_front();
                        }
                }
        }
 
-       void OnPostJoin(Membership* memb)
+       void OnPostJoin(Membership* memb) CXX11_OVERRIDE
        {
-               if (IS_REMOTE(memb->user))
+               LocalUser* localuser = IS_LOCAL(memb->user);
+               if (!localuser)
                        return;
 
-               if (!dobots && ServerInstance->Modules->Find("m_botmode.so") && memb->user->IsModeSet('B'))
+               if (memb->user->IsModeSet(botmode) && !dobots)
                        return;
 
                HistoryList* list = m.ext.get(memb->chan);
@@ -168,22 +174,40 @@ class ModuleChanHistory : public Module
                if (list->maxtime)
                        mintime = ServerInstance->Time() - list->maxtime;
 
-               if (sendnotice)
+               if ((sendnotice) && (!batchcap.get(localuser)))
                {
-                       memb->user->WriteServ("NOTICE %s :Replaying up to %d lines of pre-join history spanning up to %d seconds",
-                               memb->chan->name.c_str(), list->maxlen, list->maxtime);
+                       std::string message("Replaying up to " + ConvToStr(list->maxlen) + " lines of pre-join history");
+                       if (list->maxtime > 0)
+                               message.append(" spanning up to " + ConvToStr(list->maxtime) + " seconds");
+                       memb->WriteNotice(message);
+               }
+
+               if (batchmanager)
+               {
+                       batchmanager->Start(batch);
+                       batch.GetBatchStartMessage().PushParamRef(memb->chan->name);
                }
 
                for(std::deque<HistoryItem>::iterator i = list->lines.begin(); i != list->lines.end(); ++i)
                {
-                       if (i->ts >= mintime)
-                               memb->user->Write(i->line);
+                       const HistoryItem& item = *i;
+                       if (item.ts >= mintime)
+                       {
+                               ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, item.sourcemask, memb->chan, item.text);
+                               if (servertimemanager)
+                                       servertimemanager->Set(msg, item.ts);
+                               batch.AddToBatch(msg);
+                               localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
+                       }
                }
+
+               if (batchmanager)
+                       batchmanager->End(batch);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides channel history replayed on join", VF_VENDOR);
+               return Version("Provides channel mode +H, allows for the channel message history to be replayed on join", VF_VENDOR);
        }
 };
 
index 6dbc0e7a860418d1154d36b00646d5bfa38040f4..64b79a39ede443f86aea5dd173c1e5c5a209910e 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Logs snomask output to channel(s). */
-
 class ModuleChanLog : public Module
 {
- private:
        /*
         * 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:
-       void init()
-       {
-               Implementation eventlist[] = { I_OnRehash, I_OnSendSnotice };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
-               OnRehash(NULL);
-       }
-
-       virtual ~ModuleChanLog()
-       {
-       }
-
-       virtual void OnRehash(User *user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                std::string snomasks;
                std::string channel;
-
-               logstreams.clear();
+               ChanLogTargets newlogs;
 
                ConfigTagList tags = ServerInstance->Config->ConfTags("chanlog");
                for (ConfigIter i = tags.first; i != tags.second; ++i)
@@ -59,100 +43,45 @@ class ModuleChanLog : public Module
 
                        if (channel.empty() || snomasks.empty())
                        {
-                               ServerInstance->Logs->Log("m_chanlog", DEFAULT, "Malformed chanlog tag, ignoring");
-                               continue;
+                               throw ModuleException("Malformed chanlog tag at " + i->second->getTagLocation());
                        }
 
                        for (std::string::const_iterator it = snomasks.begin(); it != snomasks.end(); it++)
                        {
-                               logstreams.insert(std::make_pair(*it, channel));
-                               ServerInstance->Logs->Log("m_chanlog", DEFAULT, "Logging %c to %s", *it, channel.c_str());
+                               newlogs.insert(std::make_pair(*it, channel));
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Logging %c to %s", *it, channel.c_str());
                        }
                }
+               logstreams.swap(newlogs);
 
        }
 
-       virtual ModResult OnSendSnotice(char &sno, std::string &desc, const std::string &msg)
+       ModResult OnSendSnotice(char &sno, std::string &desc, const std::string &msg) CXX11_OVERRIDE
        {
                std::pair<ChanLogTargets::const_iterator, ChanLogTargets::const_iterator> itpair = logstreams.equal_range(sno);
                if (itpair.first == itpair.second)
                        return MOD_RES_PASSTHRU;
 
-               char buf[MAXBUF];
-               snprintf(buf, MAXBUF, "\2%s\2: %s", desc.c_str(), msg.c_str());
+               const std::string snotice = "\002" + desc + "\002: " + msg;
 
                for (ChanLogTargets::const_iterator it = itpair.first; it != itpair.second; ++it)
                {
                        Channel *c = ServerInstance->FindChan(it->second);
                        if (c)
                        {
-                               c->WriteChannelWithServ(ServerInstance->Config->ServerName, "PRIVMSG %s :%s", c->name.c_str(), buf);
-                               ServerInstance->PI->SendChannelPrivmsg(c, 0, buf);
+                               ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->Config->ServerName, c, snotice);
+                               c->Write(ServerInstance->GetRFCEvents().privmsg, privmsg);
+                               ServerInstance->PI->SendMessage(c, 0, snotice);
                        }
                }
 
                return MOD_RES_PASSTHRU;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Logs snomask output to channel(s).", VF_VENDOR);
+               return Version("Logs snomask output to channel(s)", VF_VENDOR);
        }
 };
 
-
 MODULE_INIT(ModuleChanLog)
-
-
-
-
-
-
-
-
-
-/*
- * This is for the "old" chanlog module which intercepted messages going to the logfile..
- * I don't consider it all that useful, and it's quite dangerous if setup incorrectly, so
- * this is defined out but left intact in case someone wants to develop it further someday.
- *
- * -- w00t (aug 23rd, 2008)
- */
-#define OLD_CHANLOG 0
-
-#if OLD_CHANLOG
-class ChannelLogStream : public LogStream
-{
- private:
-       std::string channel;
-
- public:
-       ChannelLogStream(int loglevel, const std::string &chan) : LogStream(loglevel), channel(chan)
-       {
-       }
-
-       virtual void OnLog(int loglevel, const std::string &type, const std::string &msg)
-       {
-               Channel *c = ServerInstance->FindChan(channel);
-               static bool Logging = false;
-
-               if (loglevel < this->loglvl)
-                       return;
-
-               if (Logging)
-                       return;
-
-               if (c)
-               {
-                       Logging = true; // this avoids (rare chance) loops with logging server IO on networks
-                       char buf[MAXBUF];
-                       snprintf(buf, MAXBUF, "\2%s\2: %s", type.c_str(), msg.c_str());
-
-                       c->WriteChannelWithServ(ServerInstance->Config->ServerName, "PRIVMSG %s :%s", c->name.c_str(), buf);
-                       ServerInstance->PI->SendChannelPrivmsg(c, 0, buf);
-                       Logging = false;
-               }
-       }
-};
-#endif
-
index 325e8fee11735c46f24f6790d82a9f7a8f233f21..d0d122b436c91225e7a953f5fdcfb1fa2b05f569 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements config tags which allow changing characters allowed in channel names */
-
 static std::bitset<256> allowedmap;
 
-class NewIsChannelHandler : public HandlerBase2<bool, const char*, size_t>
+class NewIsChannelHandler
 {
  public:
-       NewIsChannelHandler() { }
-       virtual ~NewIsChannelHandler() { }
-       virtual bool Call(const char*, size_t);
+       static bool Call(const std::string&);
 };
 
-bool NewIsChannelHandler::Call(const char* c, size_t max)
+bool NewIsChannelHandler::Call(const std::string& channame)
 {
-               /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
-               if (!c || *c++ != '#')
+       if (channame.empty() || channame.length() > ServerInstance->Config->Limits.ChanMax || channame[0] != '#')
+               return false;
+
+       for (std::string::const_iterator c = channame.begin(); c != channame.end(); ++c)
+       {
+               unsigned int i = *c & 0xFF;
+               if (!allowedmap[i])
                        return false;
+       }
 
-               while (*c && --max)
-               {
-                       unsigned int i = *c++ & 0xFF;
-                       if (!allowedmap[i])
-                               return false;
-               }
-               // a name of exactly max length will have max = 1 here; the null does not trigger --max
-               return max;
+       return true;
 }
 
 class ModuleChannelNames : public Module
 {
- private:
-       NewIsChannelHandler myhandler;
-       caller2<bool, const char*, size_t> rememberer;
+       TR1NS::function<bool(const std::string&)> rememberer;
        bool badchan;
+       ChanModeReference permchannelmode;
 
  public:
-       ModuleChannelNames() : rememberer(ServerInstance->IsChannel), badchan(false)
+       ModuleChannelNames()
+               : rememberer(ServerInstance->IsChannel)
+               , badchan(false)
+               , permchannelmode(this, "permanent")
        {
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
-               ServerInstance->IsChannel = &myhandler;
-               Implementation eventlist[] = { I_OnRehash, I_OnUserKick };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
+               ServerInstance->IsChannel = NewIsChannelHandler::Call;
        }
 
        void ValidateChans()
        {
+               Modes::ChangeList removepermchan;
+
                badchan = true;
-               std::vector<Channel*> chanvec;
-               for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); ++i)
-               {
-                       if (!ServerInstance->IsChannel(i->second->name.c_str(), MAXBUF))
-                               chanvec.push_back(i->second);
-               }
-               std::vector<Channel*>::reverse_iterator c2 = chanvec.rbegin();
-               while (c2 != chanvec.rend())
+               const chan_hash& chans = ServerInstance->GetChans();
+               for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); )
                {
-                       Channel* c = *c2++;
-                       if (c->IsModeSet('P') && c->GetUserCounter())
-                       {
-                               std::vector<std::string> modes;
-                               modes.push_back(c->name);
-                               modes.push_back("-P");
+                       Channel* c = i->second;
+                       // Move iterator before we begin kicking
+                       ++i;
+                       if (ServerInstance->IsChannel(c->name))
+                               continue; // The name of this channel is still valid
 
-                               ServerInstance->SendGlobalMode(modes, ServerInstance->FakeClient);
+                       if (c->IsModeSet(permchannelmode) && c->GetUserCounter())
+                       {
+                               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;
@@ -104,7 +98,7 @@ class ModuleChannelNames : public Module
                badchan = false;
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("channames");
                std::string denyToken = tag->getString("denyrange");
@@ -134,24 +128,25 @@ class ModuleChannelNames : public Module
                ValidateChans();
        }
 
-       virtual void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& except_list)
+       void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& except_list) CXX11_OVERRIDE
        {
                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);
                }
        }
 
-       virtual ~ModuleChannelNames()
+       CullResult cull() CXX11_OVERRIDE
        {
                ServerInstance->IsChannel = rememberer;
                ValidateChans();
+               return Module::cull();
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Implements config tags which allow changing characters allowed in channel names", VF_VENDOR);
        }
index 6eec486ea8a1a4cab6149d6e4651f5f3115b3859..c34e0a6c5469df27becb2736c061bda96c7717c3 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements extban +b j: - matching channel bans */
-
 class ModuleBadChannelExtban : public Module
 {
- private:
  public:
-       void init()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               return Version("Provides extban 'j', ban users that are present in another channel, and optionally on their status there", VF_OPTCOMMON|VF_VENDOR);
        }
 
-       ~ModuleBadChannelExtban()
-       {
-       }
-
-       Version GetVersion()
-       {
-               return Version("Extban 'j' - channel status/join ban", VF_OPTCOMMON|VF_VENDOR);
-       }
-
-       ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+       ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
        {
                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).name, rm))
+                               if (InspIRCd::Match((*i)->chan->name, rm))
                                {
-                                       if (status)
-                                       {
-                                               Membership* memb = (**i).GetUser(user);
-                                               if (memb && memb->hasMode(status))
-                                                       return MOD_RES_DENY;
-                                       }
-                                       else
+                                       if ((!status) || ((*i)->HasMode(mh)))
                                                return MOD_RES_DENY;
                                }
                        }
@@ -71,12 +52,10 @@ class ModuleBadChannelExtban : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('j');
+               tokens["EXTBAN"].push_back('j');
        }
 };
 
-
 MODULE_INIT(ModuleBadChannelExtban)
-
diff --git a/src/modules/m_chanprotect.cpp b/src/modules/m_chanprotect.cpp
deleted file mode 100644 (file)
index affd0c8..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2006-2009 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- *   Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
- *   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/>.
- */
-
-
-#include "inspircd.h"
-
-/* $ModDesc: Provides channel modes +a and +q */
-
-#define PROTECT_VALUE 40000
-#define FOUNDER_VALUE 50000
-
-struct ChanProtectSettings
-{
-       bool DeprivSelf;
-       bool DeprivOthers;
-       bool FirstInGetsFounder;
-       bool booting;
-       ChanProtectSettings() : booting(true) {}
-};
-
-static ChanProtectSettings settings;
-
-/** Handles basic operation of +qa channel modes
- */
-class FounderProtectBase
-{
- private:
-       const std::string type;
-       const char mode;
-       const int list;
-       const int end;
- public:
-       FounderProtectBase(char Mode, const std::string &mtype, int l, int e) :
-               type(mtype), mode(Mode), list(l), end(e)
-       {
-       }
-
-       void RemoveMode(Channel* channel, irc::modestacker* stack)
-       {
-               const UserMembList* cl = channel->GetUsers();
-               std::vector<std::string> mode_junk;
-               mode_junk.push_back(channel->name);
-               irc::modestacker modestack(false);
-               std::deque<std::string> stackresult;
-
-               for (UserMembCIter i = cl->begin(); i != cl->end(); i++)
-               {
-                       if (i->second->hasMode(mode))
-                       {
-                               if (stack)
-                                       stack->Push(mode, i->first->nick);
-                               else
-                                       modestack.Push(mode, i->first->nick);
-                       }
-               }
-
-               if (stack)
-                       return;
-
-               while (modestack.GetStackedLine(stackresult))
-               {
-                       mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
-                       ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
-                       mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
-               }
-       }
-
-       void DisplayList(User* user, Channel* channel)
-       {
-               const UserMembList* cl = channel->GetUsers();
-               for (UserMembCIter i = cl->begin(); i != cl->end(); ++i)
-               {
-                       if (i->second->hasMode(mode))
-                       {
-                               user->WriteServ("%d %s %s %s", list, user->nick.c_str(), channel->name.c_str(), i->first->nick.c_str());
-                       }
-               }
-               user->WriteServ("%d %s %s :End of channel %s list", end, user->nick.c_str(), channel->name.c_str(), type.c_str());
-       }
-
-       bool CanRemoveOthers(User* u1, Channel* c)
-       {
-               Membership* m1 = c->GetUser(u1);
-               return (settings.DeprivOthers && m1 && m1->hasMode(mode));
-       }
-};
-
-/** Abstraction of FounderProtectBase for channel mode +q
- */
-class ChanFounder : public ModeHandler, public FounderProtectBase
-{
- public:
-       ChanFounder(Module* Creator)
-               : ModeHandler(Creator, "founder", 'q', PARAM_ALWAYS, MODETYPE_CHANNEL),
-                 FounderProtectBase('q', "founder", 386, 387)
-       {
-               ModeHandler::list = true;
-               levelrequired = FOUNDER_VALUE;
-               m_paramtype = TR_NICK;
-       }
-
-       void setPrefix(int pfx)
-       {
-               prefix = pfx;
-       }
-
-       unsigned int GetPrefixRank()
-       {
-               return FOUNDER_VALUE;
-       }
-
-       void RemoveMode(Channel* channel, irc::modestacker* stack)
-       {
-               FounderProtectBase::RemoveMode(channel, stack);
-       }
-
-       void RemoveMode(User* user, irc::modestacker* stack)
-       {
-       }
-       
-       ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding)
-       {
-               User* theuser = ServerInstance->FindNick(parameter);
-               // remove own privs?
-               if (source == theuser && !adding && settings.DeprivSelf)
-                       return MOD_RES_ALLOW;
-
-               if (!adding && FounderProtectBase::CanRemoveOthers(source, channel))
-               {
-                       return MOD_RES_PASSTHRU;
-               }
-               else
-               {
-                       source->WriteNumeric(468, "%s %s :Only servers may set channel mode +q", source->nick.c_str(), channel->name.c_str());
-                       return MOD_RES_DENY;
-               }
-       }
-
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
-       {
-               return MODEACTION_ALLOW;
-       }
-
-       void DisplayList(User* user, Channel* channel)
-       {
-               FounderProtectBase::DisplayList(user,channel);
-       }
-};
-
-/** Abstraction of FounderProtectBase for channel mode +a
- */
-class ChanProtect : public ModeHandler, public FounderProtectBase
-{
- public:
-       ChanProtect(Module* Creator)
-               : ModeHandler(Creator, "admin", 'a', PARAM_ALWAYS, MODETYPE_CHANNEL),
-                 FounderProtectBase('a',"protected user", 388, 389)
-       {
-               ModeHandler::list = true;
-               levelrequired = PROTECT_VALUE;
-               m_paramtype = TR_NICK;
-       }
-
-       void setPrefix(int pfx)
-       {
-               prefix = pfx;
-       }
-
-
-       unsigned int GetPrefixRank()
-       {
-               return PROTECT_VALUE;
-       }
-
-       void RemoveMode(Channel* channel, irc::modestacker* stack)
-       {
-               FounderProtectBase::RemoveMode(channel, stack);
-       }
-
-       void RemoveMode(User* user, irc::modestacker* stack)
-       {
-       }
-
-       ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding)
-       {
-               User* theuser = ServerInstance->FindNick(parameter);
-               // source has +q
-               if (channel->GetPrefixValue(source) > PROTECT_VALUE)
-                       return MOD_RES_ALLOW;
-
-               // removing own privs?
-               if (source == theuser && !adding && settings.DeprivSelf)
-                       return MOD_RES_ALLOW;
-
-               if (!adding && FounderProtectBase::CanRemoveOthers(source, channel))
-               {
-                       return MOD_RES_PASSTHRU;
-               }
-               else
-               {
-                       source->WriteNumeric(482, "%s %s :You are not a channel founder", source->nick.c_str(), channel->name.c_str());
-                       return MOD_RES_DENY;
-               }
-       }
-
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
-       {
-               return MODEACTION_ALLOW;
-       }
-
-       void DisplayList(User* user, Channel* channel)
-       {
-               FounderProtectBase::DisplayList(user, channel);
-       }
-
-};
-
-class ModuleChanProtect : public Module
-{
-       ChanProtect cp;
-       ChanFounder cf;
- public:
-       ModuleChanProtect() : cp(this), cf(this)
-       {
-       }
-
-       void init()
-       {
-               /* Load config stuff */
-               LoadSettings();
-               settings.booting = false;
-
-               ServerInstance->Modules->AddService(cf);
-               ServerInstance->Modules->AddService(cp);
-
-               Implementation eventlist[] = { I_OnUserPreJoin };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       void LoadSettings()
-       {
-               ConfigTag* tag = ServerInstance->Config->ConfValue("chanprotect");
-
-               settings.FirstInGetsFounder = tag->getBool("noservices");
-
-               std::string qpre = tag->getString("qprefix");
-               char QPrefix = qpre.empty() ? 0 : qpre[0];
-
-               std::string apre = tag->getString("aprefix");
-               char APrefix = apre.empty() ? 0 : apre[0];
-
-               if ((APrefix && QPrefix) && APrefix == QPrefix)
-                       throw ModuleException("What the smeg, why are both your +q and +a prefixes the same character?");
-
-               if (settings.booting)
-               {
-                       if (APrefix && ServerInstance->Modes->FindPrefix(APrefix) && ServerInstance->Modes->FindPrefix(APrefix) != &cp)
-                               throw ModuleException("Looks like the +a prefix you picked for m_chanprotect is already in use. Pick another.");
-
-                       if (QPrefix && ServerInstance->Modes->FindPrefix(QPrefix) && ServerInstance->Modes->FindPrefix(QPrefix) != &cf)
-                               throw ModuleException("Looks like the +q prefix you picked for m_chanprotect is already in use. Pick another.");
-
-                       cp.setPrefix(APrefix);
-                       cf.setPrefix(QPrefix);
-               }
-               settings.DeprivSelf = tag->getBool("deprotectself", true);
-               settings.DeprivOthers = tag->getBool("deprotectothers", true);
-       }
-
-       ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
-       {
-               // if the user is the first user into the channel, mark them as the founder, but only if
-               // the config option for it is set
-
-               if (settings.FirstInGetsFounder && !chan)
-                       privs += 'q';
-
-               return MOD_RES_PASSTHRU;
-       }
-
-       Version GetVersion()
-       {
-               return Version("Founder and Protect modes (+qa)", VF_VENDOR);
-       }
-};
-
-MODULE_INIT(ModuleChanProtect)
index 5063368b81506e023fff508298071b3c516bea81..d6c17a37ed4baeecbe7b958eedaddc3b3c2bb102 100644 (file)
  */
 
 
-/* $ModDesc: Provides the /CHECK command to retrieve information on a user, channel, hostname or IP address */
-
 #include "inspircd.h"
+#include "listmode.h"
 
-/** Handle /CHECK
- */
-class CommandCheck : public Command
+enum
+{
+       RPL_CHECK = 802
+};
+
+class CheckContext
 {
+ private:
+       User* const user;
+       const std::string& target;
+
+       std::string FormatTime(time_t ts)
+       {
+               std::string timestr(InspIRCd::TimeString(ts, "%Y-%m-%d %H:%M:%S UTC (", true));
+               timestr.append(ConvToStr(ts));
+               timestr.push_back(')');
+               return timestr;
+       }
+
  public:
-       CommandCheck(Module* parent) : Command(parent,"CHECK", 1)
+       CheckContext(User* u, const std::string& targetstr)
+               : user(u)
+               , target(targetstr)
        {
-               flags_needed = 'o'; syntax = "<nickname>|<ip>|<hostmask>|<channel> <server>";
+               Write("START", target);
        }
 
-       std::string timestring(time_t time)
+       ~CheckContext()
        {
-               char timebuf[60];
-               struct tm *mytime = gmtime(&time);
-               strftime(timebuf, 59, "%Y-%m-%d %H:%M:%S UTC (", mytime);
-               std::string ret(timebuf);
-               ret.append(ConvToStr(time)).push_back(')');
-               return ret;
+               Write("END", target);
        }
 
-       void dumpExt(User* user, const std::string& checkstr, Extensible* ext)
+       void Write(const std::string& type, const std::string& text)
        {
-               std::stringstream dumpkeys;
-               for(Extensible::ExtensibleStore::const_iterator i = ext->GetExtList().begin(); i != ext->GetExtList().end(); i++)
+               user->WriteRemoteNumeric(RPL_CHECK, type, text);
+       }
+
+       void Write(const std::string& type, time_t ts)
+       {
+               user->WriteRemoteNumeric(RPL_CHECK, type, FormatTime(ts));
+       }
+
+       User* GetUser() const { return user; }
+
+       void DumpListMode(ListModeBase* mode, Channel* chan)
+       {
+               const ListModeBase::ModeList* list = mode->GetList(chan);
+               if (!list)
+                       return;
+
+               for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+               {
+                       CheckContext::List listmode(*this, "listmode");
+                       listmode.Add(ConvToStr(mode->GetModeChar()));
+                       listmode.Add(i->mask);
+                       listmode.Add(i->setter);
+                       listmode.Add(FormatTime(i->time));
+                       listmode.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())
-                               user->SendText(checkstr + " meta:" + item->name + " " + value);
+                               Write("meta:" + item->name, value);
                        else if (!item->name.empty())
-                               dumpkeys << " " << item->name;
+                               extlist.Add(item->name);
                }
-               if (!dumpkeys.str().empty())
-                       user->SendText(checkstr + " metadata", dumpkeys);
+
+               extlist.Flush();
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       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
+{
+       UserModeReference snomaskmode;
+
+       std::string GetSnomasks(User* user)
        {
-               if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName.c_str())
+               std::string ret;
+               if (snomaskmode)
+                       ret = snomaskmode->GetUserParameter(user);
+
+               if (ret.empty())
+                       ret = "+";
+               return ret;
+       }
+
+       static std::string GetAllowedOperOnlyModes(LocalUser* user, ModeType modetype)
+       {
+               std::string ret;
+               const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes.GetModes(modetype);
+               for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); ++i)
+               {
+                       const ModeHandler* const mh = i->second;
+                       if ((mh->NeedsOper()) && (user->HasModePermission(mh)))
+                               ret.push_back(mh->GetModeChar());
+               }
+               return ret;
+       }
+
+ public:
+       CommandCheck(Module* parent)
+               : Command(parent,"CHECK", 1)
+               , snomaskmode(parent, "snomask")
+       {
+               flags_needed = 'o'; syntax = "<nick>|<ipmask>|<hostmask>|<channel> [<servername>]";
+       }
+
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               if (parameters.size() > 1 && !irc::equals(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 +" + targuser->FormatNoticeMasks());
-                       user->SendText(checkstr + " server " + targuser->server);
-                       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->GetRealName());
+                       context.Write("modes", targuser->GetModeLetters());
+                       context.Write("snomasks", GetSnomasks(targuser));
+                       context.Write("server", targuser->server->GetName());
+                       context.Write("uid", targuser->uuid);
+                       context.Write("signon", targuser->signon);
+                       context.Write("nickts", targuser->age);
                        if (loctarg)
-                               user->SendText(checkstr + " lastmsg " + timestring(targuser->idle_lastmsg));
+                               context.Write("lastmsg", loctarg->idle_lastmsg);
 
-                       if (IS_AWAY(targuser))
+                       if (targuser->IsAway())
                        {
                                /* user is away */
-                               user->SendText(checkstr + " awaytime " + timestring(targuser->awaytime));
-                               user->SendText(checkstr + " awaymsg " + targuser->awaymsg);
+                               context.Write("awaytime", targuser->awaytime);
+                               context.Write("awaymsg", targuser->awaymsg);
                        }
 
-                       if (IS_OPER(targuser))
+                       if (targuser->IsOper())
                        {
                                OperInfo* oper = targuser->oper;
                                /* user is an oper of type ____ */
-                               user->SendText(checkstr + " opertype " + oper->NameStr());
+                               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);
+                                       context.Write("chanmodeperms", GetAllowedOperOnlyModes(loctarg, MODETYPE_CHANNEL));
+                                       context.Write("usermodeperms", GetAllowedOperOnlyModes(loctarg, MODETYPE_USER));
+                                       context.Write("commandperms", oper->AllowedOperCommands.ToString());
+                                       context.Write("permissions", oper->AllowedPrivs.ToString());
                                }
                        }
 
                        if (loctarg)
                        {
-                               user->SendText(checkstr + " clientaddr " + irc::sockets::satouser(loctarg->client_sa));
-                               user->SendText(checkstr + " serveraddr " + irc::sockets::satouser(loctarg->server_sa));
+                               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++)
                        {
-                               Channel* c = *i;
-                               chliststr.append(c->GetPrefixChar(targuser)).append(c->name).append(" ");
+                               Membership* memb = *i;
+                               Channel* c = memb->chan;
+                               char prefix = memb->GetPrefixChar();
+                               if (prefix)
+                                       chliststr.push_back(prefix);
+                               chliststr.append(c->name);
+                               chanlist.Add(chliststr);
+                               chliststr.clear();
                        }
 
-                       std::stringstream dump(chliststr);
+                       chanlist.Flush();
 
-                       user->SendText(checkstr + " onchans", dump);
-
-                       dumpExt(user, checkstr, targuser);
+                       context.DumpExt(targuser);
                }
                else if (targchan)
                {
                        /* /check on a channel */
-                       user->SendText(checkstr + " timestamp " + timestring(targchan->age));
+                       context.Write("createdat", targchan->age);
 
                        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", 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)
                        {
-                               char tmpbuf[MAXBUF];
                                /*
-                                * Unlike Asuka, I define a clone as coming from the same host. --w00t
-                                */
-                               snprintf(tmpbuf, MAXBUF, "%-3lu %s%s (%s@%s) %s ", ServerInstance->Users->GlobalCloneCount(i->first), targchan->GetAllPrefixChars(i->first), i->first->nick.c_str(), i->first->ident.c_str(), i->first->dhost.c_str(), i->first->fullname.c_str());
-                               user->SendText(checkstr + " member " + tmpbuf);
+                                * Unlike Asuka, I define a clone as coming from the same host. --w00t
+                                */
+                               const UserManager::CloneCounts& clonecount = ServerInstance->Users->GetCloneCounts(i->first);
+                               context.Write("member", InspIRCd::Format("%u %s%s (%s)", clonecount.global,
+                                       i->second->GetAllPrefixChars().c_str(), i->first->GetFullHost().c_str(),
+                                       i->first->GetRealName().c_str()));
                        }
 
-                       irc::modestacker modestack(true);
-                       for(BanList::iterator b = targchan->bans.begin(); b != targchan->bans.end(); ++b)
-                       {
-                               modestack.Push('b', b->data);
-                       }
-                       std::vector<std::string> stackresult;
-                       std::vector<TranslateType> dummy;
-                       while (modestack.GetStackedLine(stackresult))
-                       {
-                               creator->ProtoSendMode(user, TYPE_CHANNEL, targchan, stackresult, dummy);
-                               stackresult.clear();
-                       }
-                       FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(targchan,creator,user));
-                       dumpExt(user, checkstr, targchan);
+                       const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+                       for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
+                               context.DumpListMode(*i, targchan);
+
+                       context.DumpExt(targchan);
                }
                else
                {
@@ -221,75 +281,48 @@ class CommandCheck : public Command
                        long x = 0;
 
                        /* hostname or other */
-                       for (user_hash::const_iterator a = ServerInstance->Users->clientlist->begin(); a != ServerInstance->Users->clientlist->end(); a++)
+                       const user_hash& users = ServerInstance->Users->GetUsers();
+                       for (user_hash::const_iterator a = users.begin(); a != users.end(); ++a)
                        {
-                               if (InspIRCd::Match(a->second->host, parameters[0], ascii_case_insensitive_map) || InspIRCd::Match(a->second->dhost, parameters[0], ascii_case_insensitive_map))
+                               if (InspIRCd::Match(a->second->GetRealHost(), parameters[0], ascii_case_insensitive_map) || InspIRCd::Match(a->second->GetDisplayedHost(), 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->GetRealName());
                                }
                                /* 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->GetRealName());
                                }
                        }
 
-                       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)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               if (parameters.size() > 1)
+               if ((parameters.size() > 1) && (parameters[1].find('.') != std::string::npos))
                        return ROUTE_OPT_UCAST(parameters[1]);
                return ROUTE_LOCALONLY;
        }
 };
 
-
 class ModuleCheck : public Module
 {
- private:
        CommandCheck mycommand;
  public:
        ModuleCheck() : mycommand(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(mycommand);
-       }
-
-       ~ModuleCheck()
-       {
-       }
-
-       void ProtoSendMode(void* uv, TargetTypeFlags, void*, const std::vector<std::string>& result, const std::vector<TranslateType>&)
-       {
-               User* user = (User*)uv;
-               std::string checkstr(":");
-               checkstr.append(ServerInstance->Config->ServerName);
-               checkstr.append(" 304 ");
-               checkstr.append(user->nick);
-               checkstr.append(" :CHECK modelist");
-               for(unsigned int i=0; i < result.size(); i++)
-               {
-                       checkstr.append(" ");
-                       checkstr.append(result[i]);
-               }
-               user->SendText(checkstr);
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("CHECK command, view user, channel, IP address or hostname information", VF_VENDOR|VF_OPTCOMMON);
+               return Version("Provides the CHECK command to view user, channel, IP address or hostname information", VF_VENDOR|VF_OPTCOMMON);
        }
 };
 
index 6aaed7831dd63e849fb0be1716fba2a97caa7879..0ca7dc78c97e12d18b18d42c60222a29f34b6e7b 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the CHGHOST command */
-
 /** Handle /CHGHOST
  */
 class CommandChghost : public Command
 {
- private:
-       char* hostmap;
  public:
-       CommandChghost(Module* Creator, char* hmap) : Command(Creator,"CHGHOST", 2), hostmap(hmap)
+       std::bitset<UCHAR_MAX> hostmap;
+
+       CommandChghost(Module* Creator)
+               : Command(Creator,"CHGHOST", 2)
        {
                allow_empty_last_param = false;
                flags_needed = 'o';
-               syntax = "<nick> <newhost>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               syntax = "<nick> <host>";
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               const char* x = parameters[1].c_str();
-
-               if (parameters[1].length() > 63)
+               if (parameters[1].length() > ServerInstance->Config->Limits.MaxHost)
                {
-                       user->WriteServ("NOTICE %s :*** CHGHOST: Host too long", user->nick.c_str());
+                       user->WriteNotice("*** CHGHOST: Host too long");
                        return CMD_FAILURE;
                }
 
-               for (; *x; x++)
+               for (std::string::const_iterator x = parameters[1].begin(); x != parameters[1].end(); x++)
                {
-                       if (!hostmap[(unsigned char)*x])
+                       if (!hostmap.test(static_cast<unsigned char>(*x)))
                        {
-                               user->WriteServ("NOTICE "+user->nick+" :*** CHGHOST: Invalid characters in hostname");
+                               user->WriteNotice("*** CHGHOST: Invalid characters in hostname");
                                return CMD_FAILURE;
                        }
                }
@@ -60,30 +57,27 @@ class CommandChghost : public Command
                User* dest = ServerInstance->FindNick(parameters[0]);
 
                // Allow services to change the host of unregistered users
-               if ((!dest) || ((dest->registered != REG_ALL) && (!ServerInstance->ULine(user->server))))
+               if ((!dest) || ((dest->registered != REG_ALL) && (!user->server->IsULine())))
                {
-                       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;
                }
 
                if (IS_LOCAL(dest))
                {
-                       if ((dest->ChangeDisplayedHost(parameters[1].c_str())) && (!ServerInstance->ULine(user->server)))
+                       if ((dest->ChangeDisplayedHost(parameters[1])) && (!user->server->IsULine()))
                        {
                                // fix by brain - ulines set hosts silently
-                               ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used CHGHOST to make the displayed host of "+dest->nick+" become "+dest->dhost);
+                               ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used CHGHOST to make the displayed host of "+dest->nick+" become "+dest->GetDisplayedHost());
                        }
                }
 
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -91,38 +85,26 @@ class CommandChghost : public Command
 class ModuleChgHost : public Module
 {
        CommandChghost cmd;
-       char hostmap[256];
- public:
-       ModuleChgHost() : cmd(this, hostmap)
-       {
-       }
 
-       void init()
+ public:
+       ModuleChgHost()
+               : cmd(this)
        {
-               OnRehash(NULL);
-               ServerInstance->Modules->AddService(cmd);
-               Implementation eventlist[] = { I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                std::string hmap = ServerInstance->Config->ConfValue("hostname")->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789");
 
-               memset(hostmap, 0, sizeof(hostmap));
+               cmd.hostmap.reset();
                for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
-                       hostmap[(unsigned char)*n] = 1;
-       }
-
-       ~ModuleChgHost()
-       {
+                       cmd.hostmap.set(static_cast<unsigned char>(*n));
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for the CHGHOST command", VF_OPTCOMMON | VF_VENDOR);
+               return Version("Provides the CHGHOST command", VF_OPTCOMMON | VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleChgHost)
index 2112e45a3f8d298c6f0e47a4bde6e066d25d920e..a2f6836faa7044f45fb4b4abd0a2b2f601f9d7d6 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the CHGIDENT command */
-
 /** Handle /CHGIDENT
  */
 class CommandChgident : public Command
@@ -33,53 +31,49 @@ class CommandChgident : public Command
        {
                allow_empty_last_param = false;
                flags_needed = 'o';
-               syntax = "<nick> <newident>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               syntax = "<nick> <ident>";
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                User* dest = ServerInstance->FindNick(parameters[0]);
 
                if ((!dest) || (dest->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;
                }
 
                if (parameters[1].length() > ServerInstance->Config->Limits.IdentMax)
                {
-                       user->WriteServ("NOTICE %s :*** CHGIDENT: Ident is too long", user->nick.c_str());
+                       user->WriteNotice("*** CHGIDENT: Ident is too long");
                        return CMD_FAILURE;
                }
 
-               if (!ServerInstance->IsIdent(parameters[1].c_str()))
+               if (!ServerInstance->IsIdent(parameters[1]))
                {
-                       user->WriteServ("NOTICE %s :*** CHGIDENT: Invalid characters in ident", user->nick.c_str());
+                       user->WriteNotice("*** CHGIDENT: Invalid characters in ident");
                        return CMD_FAILURE;
                }
 
                if (IS_LOCAL(dest))
                {
-                       dest->ChangeIdent(parameters[1].c_str());
+                       dest->ChangeIdent(parameters[1]);
 
-                       if (!ServerInstance->ULine(user->server))
+                       if (!user->server->IsULine())
                                ServerInstance->SNO->WriteGlobalSno('a', "%s used CHGIDENT to change %s's ident to '%s'", user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str());
                }
 
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
-
 class ModuleChgIdent : public Module
 {
        CommandChgident cmd;
@@ -89,21 +83,10 @@ public:
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleChgIdent()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Provides the CHGIDENT command", VF_OPTCOMMON | VF_VENDOR);
        }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides support for the CHGIDENT command", VF_OPTCOMMON | VF_VENDOR);
-       }
-
 };
 
 MODULE_INIT(ModuleChgIdent)
-
index 73ae3d4870bc93d5d65d67604f7ffc3175f4e8c3..bcbf22a14f3926fb5465e4cb18c336599b2faa9e 100644 (file)
@@ -20,8 +20,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the CHGNAME command */
-
 /** Handle /CHGNAME
  */
 class CommandChgname : public Command
@@ -31,51 +29,47 @@ class CommandChgname : public Command
        {
                allow_empty_last_param = false;
                flags_needed = 'o';
-               syntax = "<nick> <newname>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               syntax = "<nick> :<realname>";
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                User* dest = ServerInstance->FindNick(parameters[0]);
 
                if ((!dest) || (dest->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;
                }
 
                if (parameters[1].empty())
                {
-                       user->WriteServ("NOTICE %s :*** CHGNAME: GECOS must be specified", user->nick.c_str());
+                       user->WriteNotice("*** CHGNAME: Real name must be specified");
                        return CMD_FAILURE;
                }
 
-               if (parameters[1].length() > ServerInstance->Config->Limits.MaxGecos)
+               if (parameters[1].length() > ServerInstance->Config->Limits.MaxReal)
                {
-                       user->WriteServ("NOTICE %s :*** CHGNAME: GECOS too long", user->nick.c_str());
+                       user->WriteNotice("*** CHGNAME: Real name is too long");
                        return CMD_FAILURE;
                }
 
                if (IS_LOCAL(dest))
                {
-                       dest->ChangeName(parameters[1].c_str());
-                       ServerInstance->SNO->WriteGlobalSno('a', "%s used CHGNAME to change %s's GECOS to '%s'", user->nick.c_str(), dest->nick.c_str(), dest->fullname.c_str());
+                       dest->ChangeRealName(parameters[1]);
+                       ServerInstance->SNO->WriteGlobalSno('a', "%s used CHGNAME to change %s's real name to '%s'", user->nick.c_str(), dest->nick.c_str(), dest->GetRealName().c_str());
                }
 
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
-
 class ModuleChgName : public Module
 {
        CommandChgname cmd;
@@ -85,20 +79,10 @@ public:
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleChgName()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Provides the CHGNAME command", VF_OPTCOMMON | VF_VENDOR);
        }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides support for the CHGNAME command", VF_OPTCOMMON | VF_VENDOR);
-       }
-
 };
 
 MODULE_INIT(ModuleChgName)
diff --git a/src/modules/m_classban.cpp b/src/modules/m_classban.cpp
new file mode 100644 (file)
index 0000000..c8fb422
--- /dev/null
@@ -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("Provides extban 'n', connection class bans", VF_VENDOR | VF_OPTCOMMON);
+       }
+};
+
+MODULE_INIT(ModuleClassBan)
diff --git a/src/modules/m_clearchan.cpp b/src/modules/m_clearchan.cpp
new file mode 100644 (file)
index 0000000..859da46
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * 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 "xline.h"
+
+class CommandClearChan : public Command
+{
+ public:
+       Channel* activechan;
+
+       CommandClearChan(Module* Creator)
+               : Command(Creator, "CLEARCHAN", 1, 3)
+       {
+               syntax = "<channel> [KILL|KICK|G|Z] [:<reason>]";
+               flags_needed = 'o';
+
+               // Stop the linking mod from forwarding ENCAP'd CLEARCHAN commands, see below why
+               force_manual_route = true;
+       }
+
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               Channel* chan = activechan = ServerInstance->FindChan(parameters[0]);
+               if (!chan)
+               {
+                       user->WriteNotice("The channel " + parameters[0] + " does not exist.");
+                       return CMD_FAILURE;
+               }
+
+               // See what method the oper wants to use, default to KILL
+               std::string method("KILL");
+               if (parameters.size() > 1)
+               {
+                       method = parameters[1];
+                       std::transform(method.begin(), method.end(), method.begin(), ::toupper);
+               }
+
+               XLineFactory* xlf = NULL;
+               bool kick = (method == "KICK");
+               if ((!kick) && (method != "KILL"))
+               {
+                       if ((method != "Z") && (method != "G"))
+                       {
+                               user->WriteNotice("Invalid method for clearing " + chan->name);
+                               return CMD_FAILURE;
+                       }
+
+                       xlf = ServerInstance->XLines->GetFactory(method);
+                       if (!xlf)
+                               return CMD_FAILURE;
+               }
+
+               const std::string reason = parameters.size() > 2 ? parameters.back() : "Clearing " + chan->name;
+
+               if (!user->server->IsSilentULine())
+                       ServerInstance->SNO->WriteToSnoMask((IS_LOCAL(user) ? 'a' : 'A'), user->nick + " has cleared \002" + chan->name + "\002 (" + method + "): " + reason);
+
+               user->WriteNotice("Clearing \002" + chan->name + "\002 (" + method + "): " + reason);
+
+               {
+                       // Route this command manually so it is sent before the QUITs we are about to generate.
+                       // The idea is that by the time our QUITs reach the next hop, it has already removed all their
+                       // clients from the channel, meaning victims on other servers won't see the victims on this
+                       // server quitting.
+                       CommandBase::Params eparams;
+                       eparams.push_back(chan->name);
+                       eparams.push_back(method);
+                       eparams.push_back(":");
+                       eparams.back().append(reason);
+                       ServerInstance->PI->BroadcastEncap(this->name, eparams, user, user);
+               }
+
+               // Attach to the appropriate hook so we're able to hide the QUIT/KICK messages
+               Implementation hook = (kick ? I_OnUserKick : I_OnBuildNeighborList);
+               ServerInstance->Modules->Attach(hook, creator);
+
+               std::string mask;
+               // Now remove all local non-opers from the channel
+               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())
+                               continue;
+
+                       // If kicking users, remove them and skip the QuitUser()
+                       if (kick)
+                       {
+                               chan->KickUser(ServerInstance->FakeClient, currit, reason);
+                               continue;
+                       }
+
+                       // If we are banning users then create the XLine and add it
+                       if (xlf)
+                       {
+                               XLine* xline;
+                               try
+                               {
+                                       mask = ((method[0] == 'Z') ? curr->GetIPString() : "*@" + curr->GetRealHost());
+                                       xline = xlf->Generate(ServerInstance->Time(), 60*60, user->nick, reason, mask);
+                               }
+                               catch (ModuleException&)
+                               {
+                                       // Nothing, move on to the next user
+                                       continue;
+                               }
+
+                               if (!ServerInstance->XLines->AddLine(xline, user))
+                                       delete xline;
+                       }
+
+                       ServerInstance->Users->QuitUser(curr, reason);
+               }
+
+               ServerInstance->Modules->Detach(hook, creator);
+               if (xlf)
+                       ServerInstance->XLines->ApplyLines();
+
+               return CMD_SUCCESS;
+       }
+};
+
+class ModuleClearChan : public Module
+{
+       CommandClearChan cmd;
+
+ public:
+       ModuleClearChan()
+               : cmd(this)
+       {
+       }
+
+       void init() CXX11_OVERRIDE
+       {
+               // Only attached while we are working; don't react to events otherwise
+               ServerInstance->Modules->DetachAll(this);
+       }
+
+       void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE
+       {
+               bool found = false;
+               for (IncludeChanList::iterator i = include.begin(); i != include.end(); ++i)
+               {
+                       if ((*i)->chan == cmd.activechan)
+                       {
+                               // Don't show the QUIT to anyone in the channel by default
+                               include.erase(i);
+                               found = true;
+                               break;
+                       }
+               }
+
+               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)
+                               continue;
+
+                       if (curr->IsOper())
+                       {
+                               // If another module has removed the channel we're working on from the list of channels
+                               // to consider for sending the QUIT to then don't add exceptions for opers, because the
+                               // module before us doesn't want them to see it or added the exceptions already.
+                               // If there is a value for this oper in excepts already, this won't overwrite it.
+                               if (found)
+                                       exception.insert(std::make_pair(curr, true));
+                               continue;
+                       }
+                       else if (!include.empty() && curr->chans.size() > 1)
+                       {
+                               // This is a victim and potentially has another common channel with the user quitting,
+                               // add a negative exception overwriting the previous value, if any.
+                               exception[curr] = false;
+                       }
+               }
+       }
+
+       void OnUserKick(User* source, Membership* memb, const std::string& reason, CUList& excepts) CXX11_OVERRIDE
+       {
+               // Hide the KICK from all non-opers
+               User* leaving = memb->user;
+               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))
+                               excepts.insert(curr);
+               }
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the CLEARCHAN command that allows opers to masskick, masskill or mass G/Z-line users on a channel", VF_VENDOR|VF_OPTCOMMON);
+       }
+};
+
+MODULE_INIT(ModuleClearChan)
index f4cfdb54f8b07c7186e11f1e5b27b1196a6c5ee5..d9b2eb78970ea1861f1bdf82ae720eb4e648c7e7 100644 (file)
 
 
 #include "inspircd.h"
-#include "hash.h"
-
-/* $ModDesc: Provides masking of user hostnames */
+#include "modules/hash.h"
 
 enum CloakMode
 {
-       /** 1.2-compatible host-based cloak */
-       MODE_COMPAT_HOST,
-       /** 1.2-compatible IP-only cloak */
-       MODE_COMPAT_IPONLY,
        /** 2.0 cloak of "half" of the hostname plus the full IP hash */
        MODE_HALF_CLOAK,
+
        /** 2.0 cloak of IP hash, split at 2 common CIDR range points */
        MODE_OPAQUE
 };
@@ -43,24 +38,59 @@ enum CloakMode
 // lowercase-only encoding similar to base64, used for hash output
 static const char base32[] = "0123456789abcdefghijklmnopqrstuv";
 
+// The minimum length of a cloak key.
+static const size_t minkeylen = 30;
+
+struct CloakInfo
+{
+       // The method used for cloaking users.
+       CloakMode mode;
+
+       // The number of parts of the hostname shown when using half cloaking.
+       unsigned int domainparts;
+
+       // The secret used for generating cloaks.
+       std::string key;
+
+       // The prefix for cloaks (e.g. MyNet-).
+       std::string prefix;
+
+       // The suffix for IP cloaks (e.g. .IP).
+       std::string suffix;
+
+       CloakInfo(CloakMode Mode, const std::string& Key, const std::string& Prefix, const std::string& Suffix, unsigned int DomainParts = 0)
+               : mode(Mode)
+               , domainparts(DomainParts)
+               , key(Key)
+               , prefix(Prefix)
+               , suffix(Suffix)
+       {
+       }
+};
+
+typedef std::vector<std::string> CloakList;
+
 /** Handles user mode +x
  */
 class CloakUser : public ModeHandler
 {
  public:
-       LocalStringExt ext;
-
+       bool active;
+       SimpleExtItem<CloakList> ext;
        std::string debounce_uid;
        time_t debounce_ts;
        int debounce_count;
 
        CloakUser(Module* source)
-               : ModeHandler(source, "cloak", 'x', PARAM_NONE, MODETYPE_USER),
-               ext("cloaked_host", source), debounce_ts(0), debounce_count(0)
+               : ModeHandler(source, "cloak", 'x', PARAM_NONE, MODETYPE_USER)
+               , active(false)
+               , ext("cloaked_host", ExtensionItem::EXT_USER, source)
+               , debounce_ts(0)
+               , debounce_count(0)
        {
        }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
        {
                LocalUser* user = IS_LOCAL(dest);
 
@@ -70,7 +100,9 @@ class CloakUser : public ModeHandler
                 */
                if (!user)
                {
-                       dest->SetMode('x',adding);
+                       // Remote setters broadcast mode before host while local setters do the opposite, so this takes that into account
+                       active = IS_LOCAL(source) ? adding : !adding;
+                       dest->SetMode(this, adding);
                        return MODEACTION_ALLOW;
                }
 
@@ -87,7 +119,7 @@ class CloakUser : public ModeHandler
                        debounce_ts = ServerInstance->Time();
                }
 
-               if (adding == user->IsModeSet('x'))
+               if (adding == user->IsModeSet(this))
                        return MODEACTION_DENY;
 
                /* don't allow this user to spam modechanges */
@@ -97,21 +129,22 @@ class CloakUser : public ModeHandler
                if (adding)
                {
                        // assume this is more correct
-                       if (user->registered != REG_ALL && user->host != user->dhost)
+                       if (user->registered != REG_ALL && user->GetRealHost() != user->GetDisplayedHost())
                                return MODEACTION_DENY;
 
-                       std::string* cloak = ext.get(user);
-
-                       if (!cloak)
+                       CloakList* cloaks = ext.get(user);
+                       if (!cloaks)
                        {
                                /* Force creation of missing cloak */
                                creator->OnUserConnect(user);
-                               cloak = ext.get(user);
+                               cloaks = ext.get(user);
                        }
-                       if (cloak)
+
+                       // If we have a cloak then set the hostname.
+                       if (cloaks && !cloaks->empty())
                        {
-                               user->ChangeDisplayedHost(cloak->c_str());
-                               user->SetMode('x',true);
+                               user->ChangeDisplayedHost(cloaks->front());
+                               user->SetMode(this, true);
                                return MODEACTION_ALLOW;
                        }
                        else
@@ -122,12 +155,11 @@ class CloakUser : public ModeHandler
                        /* User is removing the mode, so restore their real host
                         * and make it match the displayed one.
                         */
-                       user->SetMode('x',false);
-                       user->ChangeDisplayedHost(user->host.c_str());
+                       user->SetMode(this, false);
+                       user->ChangeDisplayedHost(user->GetRealHost().c_str());
                        return MODEACTION_ALLOW;
                }
        }
-
 };
 
 class CommandCloak : public Command
@@ -139,67 +171,68 @@ class CommandCloak : public Command
                syntax = "<host>";
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user);
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
 
 class ModuleCloaking : public Module
 {
  public:
        CloakUser cu;
-       CloakMode mode;
        CommandCloak ck;
-       std::string prefix;
-       std::string suffix;
-       std::string key;
-       unsigned int compatkey[4];
-       const char* xtab[4];
+       std::vector<CloakInfo> cloaks;
        dynamic_reference<HashProvider> Hash;
 
-       ModuleCloaking() : cu(this), mode(MODE_OPAQUE), ck(this), Hash(this, "hash/md5")
+       ModuleCloaking()
+               : cu(this)
+               , ck(this)
+               , Hash(this, "hash/md5")
        {
        }
 
-       void init()
-       {
-               OnRehash(NULL);
-
-               ServerInstance->Modules->AddService(cu);
-               ServerInstance->Modules->AddService(ck);
-               ServerInstance->Modules->AddService(cu.ext);
-
-               Implementation eventlist[] = { I_OnRehash, I_OnCheckBan, I_OnUserConnect, I_OnChangeHost };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       /** This function takes a domain name string and returns just the last two domain parts,
-        * or the last domain part if only two are available. Failing that it just returns what it was given.
+       /** Takes a domain name and retrieves the subdomain which should be visible.
+        * This is usually the last \p domainparts labels but if not enough are
+        * present then all but the most specific label are used. If the domain name
+        * consists of one label only then none are used.
+        *
+        * Here are some examples for how domain names will be shortened assuming
+        * \p domainparts is set to the default of 3.
         *
-        * For example, if it is passed "svn.inspircd.org" it will return ".inspircd.org".
-        * If it is passed "brainbox.winbot.co.uk" it will return ".co.uk",
-        * and if it is passed "localhost.localdomain" it will return ".localdomain".
+        *   "this.is.an.example.com"  =>  ".an.example.com"
+        *   "an.example.com"          =>  ".example.com"
+        *   "example.com"             =>  ".com"
+        *   "localhost"               =>  ""
         *
-        * This is used to ensure a significant part of the host is always cloaked (see Bug #216)
+        * @param host The hostname to cloak.
+        * @param domainparts The number of domain labels that should be visible.
+        * @return The visible segment of the hostname.
         */
-       std::string LastTwoDomainParts(const std::string &host)
+       std::string VisibleDomainParts(const std::string& host, unsigned int domainparts)
        {
-               int dots = 0;
-               std::string::size_type splitdot = host.length();
+               // The position at which we found the last dot.
+               std::string::const_reverse_iterator dotpos;
+
+               // The number of dots we have seen so far.
+               unsigned int seendots = 0;
 
-               for (std::string::size_type x = host.length() - 1; x; --x)
+               for (std::string::const_reverse_iterator iter = host.rbegin(); iter != host.rend(); ++iter)
                {
-                       if (host[x] == '.')
-                       {
-                               splitdot = x;
-                               dots++;
-                       }
-                       if (dots >= 3)
+                       if (*iter != '.')
+                               continue;
+
+                       // We have found a dot!
+                       dotpos = iter;
+                       seendots += 1;
+
+                       // Do we have enough segments to stop?
+                       if (seendots >= domainparts)
                                break;
                }
 
-               if (splitdot == host.length())
+               // We only returns a domain part if more than one label is
+               // present. See above for a full explanation.
+               if (!seendots)
                        return "";
-               else
-                       return host.substr(splitdot);
+               return std::string(dotpos.base() - 1, host.end());
        }
 
        /**
@@ -208,17 +241,17 @@ class ModuleCloaking : public Module
         * @param id A unique ID for this type of item (to make it unique if the item matches)
         * @param len The length of the output. Maximum for MD5 is 16 characters.
         */
-       std::string SegmentCloak(const std::string& item, char id, int len)
+       std::string SegmentCloak(const CloakInfo& info, const std::string& item, char id, size_t len)
        {
                std::string input;
-               input.reserve(key.length() + 3 + item.length());
+               input.reserve(info.key.length() + 3 + item.length());
                input.append(1, id);
-               input.append(key);
+               input.append(info.key);
                input.append(1, '\0'); // null does not terminate a C++ string
                input.append(item);
 
-               std::string rv = Hash->sum(input).substr(0,len);
-               for(int i=0; i < len; i++)
+               std::string rv = Hash->GenerateRaw(input).substr(0,len);
+               for(size_t i = 0; i < len; i++)
                {
                        // this discards 3 bits per byte. We have an
                        // overabundance of bits in the hash output, doesn't
@@ -228,70 +261,13 @@ class ModuleCloaking : public Module
                return rv;
        }
 
-       std::string CompatCloak4(const char* ip)
-       {
-               irc::sepstream seps(ip, '.');
-               std::string octet[4];
-               int i[4];
-
-               for (int j = 0; j < 4; j++)
-               {
-                       seps.GetToken(octet[j]);
-                       i[j] = atoi(octet[j].c_str());
-               }
-
-               octet[3] = octet[0] + "." + octet[1] + "." + octet[2] + "." + octet[3];
-               octet[2] = octet[0] + "." + octet[1] + "." + octet[2];
-               octet[1] = octet[0] + "." + octet[1];
-
-               /* Reset the Hash module and send it our IV */
-
-               std::string rv;
-
-               /* Send the Hash module a different hex table for each octet group's Hash sum */
-               for (int k = 0; k < 4; k++)
-               {
-                       rv.append(Hash->sumIV(compatkey, xtab[(compatkey[k]+i[k]) % 4], octet[k]).substr(0,6));
-                       if (k < 3)
-                               rv.append(".");
-               }
-               /* Stick them all together */
-               return rv;
-       }
-
-       std::string CompatCloak6(const char* ip)
-       {
-               std::vector<std::string> hashies;
-               std::string item;
-               int rounds = 0;
-
-               /* Reset the Hash module and send it our IV */
-
-               for (const char* input = ip; *input; input++)
-               {
-                       item += *input;
-                       if (item.length() > 7)
-                       {
-                               hashies.push_back(Hash->sumIV(compatkey, xtab[(compatkey[0]+rounds) % 4], item).substr(0,8));
-                               item.clear();
-                       }
-                       rounds++;
-               }
-               if (!item.empty())
-               {
-                       hashies.push_back(Hash->sumIV(compatkey, xtab[(compatkey[0]+rounds) % 4], item).substr(0,8));
-               }
-               /* Stick them all together */
-               return irc::stringjoiner(":", hashies, 0, hashies.size() - 1).GetJoined();
-       }
-
-       std::string SegmentIP(const irc::sockets::sockaddrs& ip, bool full)
+       std::string SegmentIP(const CloakInfo& info, const irc::sockets::sockaddrs& ip, bool full)
        {
                std::string bindata;
-               int hop1, hop2, hop3;
-               int len1, len2;
+               size_t hop1, hop2, hop3;
+               size_t len1, len2;
                std::string rv;
-               if (ip.sa.sa_family == AF_INET6)
+               if (ip.family() == AF_INET6)
                {
                        bindata = std::string((const char*)ip.in6.sin6_addr.s6_addr, 16);
                        hop1 = 8;
@@ -301,7 +277,7 @@ class ModuleCloaking : public Module
                        len2 = 4;
                        // pfx s1.s2.s3. (xxxx.xxxx or s4) sfx
                        //     6  4  4    9/6
-                       rv.reserve(prefix.length() + 26 + suffix.length());
+                       rv.reserve(info.prefix.length() + 26 + info.suffix.length());
                }
                else
                {
@@ -311,67 +287,74 @@ class ModuleCloaking : public Module
                        hop3 = 2;
                        len1 = len2 = 3;
                        // pfx s1.s2. (xxx.xxx or s3) sfx
-                       rv.reserve(prefix.length() + 15 + suffix.length());
+                       rv.reserve(info.prefix.length() + 15 + info.suffix.length());
                }
 
-               rv.append(prefix);
-               rv.append(SegmentCloak(bindata, 10, len1));
+               rv.append(info.prefix);
+               rv.append(SegmentCloak(info, bindata, 10, len1));
                rv.append(1, '.');
                bindata.erase(hop1);
-               rv.append(SegmentCloak(bindata, 11, len2));
+               rv.append(SegmentCloak(info, bindata, 11, len2));
                if (hop2)
                {
                        rv.append(1, '.');
                        bindata.erase(hop2);
-                       rv.append(SegmentCloak(bindata, 12, len2));
+                       rv.append(SegmentCloak(info, bindata, 12, len2));
                }
 
                if (full)
                {
                        rv.append(1, '.');
                        bindata.erase(hop3);
-                       rv.append(SegmentCloak(bindata, 13, 6));
-                       rv.append(suffix);
+                       rv.append(SegmentCloak(info, bindata, 13, 6));
+                       rv.append(info.suffix);
                }
                else
                {
-                       char buf[50];
-                       if (ip.sa.sa_family == AF_INET6)
+                       if (ip.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], info.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], info.suffix.c_str()));
                        }
-                       rv.append(buf);
                }
                return rv;
        }
 
-       ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask)
+       ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask) CXX11_OVERRIDE
        {
                LocalUser* lu = IS_LOCAL(user);
                if (!lu)
                        return MOD_RES_PASSTHRU;
 
+               // Force the creation of cloaks if not already set.
                OnUserConnect(lu);
-               std::string* cloak = cu.ext.get(user);
-               /* Check if they have a cloaked host, but are not using it */
-               if (cloak && *cloak != user->dhost)
+
+               // If the user has no cloaks (i.e. UNIX socket) then we do nothing here.
+               CloakList* cloaklist = cu.ext.get(user);
+               if (!cloaklist || cloaklist->empty())
+                       return MOD_RES_PASSTHRU;
+
+               // Check if they have a cloaked host but are not using it.
+               for (CloakList::const_iterator iter = cloaklist->begin(); iter != cloaklist->end(); ++iter)
                {
-                       char cmask[MAXBUF];
-                       snprintf(cmask, MAXBUF, "%s!%s@%s", user->nick.c_str(), user->ident.c_str(), cloak->c_str());
-                       if (InspIRCd::Match(cmask,mask))
-                               return MOD_RES_DENY;
+                       const std::string& cloak = *iter;
+                       if (cloak != user->GetDisplayedHost())
+                       {
+                               const std::string cloakMask = user->nick + "!" + user->ident + "@" + cloak;
+                               if (InspIRCd::Match(cloakMask, mask))
+                                       return MOD_RES_DENY;
+                       }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                /* Needs to be after m_banexception etc. */
                ServerInstance->Modules->SetPriority(this, I_OnCheckBan, PRIORITY_LAST);
@@ -379,190 +362,158 @@ class ModuleCloaking : public Module
 
        // this unsets umode +x on every host change. If we are actually doing a +x
        // mode change, we will call SetMode back to true AFTER the host change is done.
-       void OnChangeHost(User* u, const std::string& host)
+       void OnChangeHost(User* u, const std::string& host) CXX11_OVERRIDE
        {
-               if(u->IsModeSet('x'))
+               if (u->IsModeSet(cu) && !cu.active)
                {
-                       u->SetMode('x', false);
-                       u->WriteServ("MODE %s -x", u->nick.c_str());
+                       u->SetMode(cu, false);
+
+                       if (!IS_LOCAL(u))
+                               return;
+                       Modes::ChangeList modechangelist;
+                       modechangelist.push_remove(&cu);
+                       ClientProtocol::Events::Mode modeevent(ServerInstance->FakeClient, NULL, u, modechangelist);
+                       static_cast<LocalUser*>(u)->Send(modeevent);
                }
+               cu.active = false;
        }
 
-       ~ModuleCloaking()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                std::string testcloak = "broken";
-               if (Hash)
+               if (Hash && !cloaks.empty())
                {
-                       switch (mode)
+                       const CloakInfo& info = cloaks.front();
+                       switch (info.mode)
                        {
-                               case MODE_COMPAT_HOST:
-                                       testcloak = prefix + "-" + Hash->sumIV(compatkey, xtab[0], "*").substr(0,10);
-                                       break;
-                               case MODE_COMPAT_IPONLY:
-                                       testcloak = Hash->sumIV(compatkey, xtab[0], "*").substr(0,10);
-                                       break;
                                case MODE_HALF_CLOAK:
-                                       testcloak = prefix + SegmentCloak("*", 3, 8) + suffix;
+                                       // Use old cloaking verification to stay compatible with 2.0
+                                       // But verify domainparts when use 3.0-only features
+                                       if (info.domainparts == 3)
+                                               testcloak = info.prefix + SegmentCloak(info, "*", 3, 8) + info.suffix;
+                                       else
+                                       {
+                                               irc::sockets::sockaddrs sa;
+                                               testcloak = GenCloak(info, sa, "", testcloak + ConvToStr(info.domainparts));
+                                       }
                                        break;
                                case MODE_OPAQUE:
-                                       testcloak = prefix + SegmentCloak("*", 4, 8) + suffix;
+                                       testcloak = info.prefix + SegmentCloak(info, "*", 4, 8) + info.suffix;
                        }
                }
                return Version("Provides masking of user hostnames", VF_COMMON|VF_VENDOR, testcloak);
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               ConfigTag* tag = ServerInstance->Config->ConfValue("cloak");
-               prefix = tag->getString("prefix");
-               suffix = tag->getString("suffix", ".IP");
-
-               std::string modestr = tag->getString("mode");
-               if (modestr == "compat-host")
-                       mode = MODE_COMPAT_HOST;
-               else if (modestr == "compat-ip")
-                       mode = MODE_COMPAT_IPONLY;
-               else if (modestr == "half")
-                       mode = MODE_HALF_CLOAK;
-               else if (modestr == "full")
-                       mode = MODE_OPAQUE;
-               else
-                       throw ModuleException("Bad value for <cloak:mode>; must be one of compat-host, compat-ip, half, full");
+               ConfigTagList tags = ServerInstance->Config->ConfTags("cloak");
+               if (tags.first == tags.second)
+                       throw ModuleException("You have loaded the cloaking module but not configured any <cloak> tags!");
 
-               if (mode == MODE_COMPAT_HOST || mode == MODE_COMPAT_IPONLY)
+               std::vector<CloakInfo> newcloaks;
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
                {
-                       bool lowercase = tag->getBool("lowercase");
-
-                       /* These are *not* using the need_positive parameter of ReadInteger -
-                        * that will limit the valid values to only the positive values in a
-                        * signed int. Instead, accept any value that fits into an int and
-                        * cast it to an unsigned int. That will, a bit oddly, give us the full
-                        * spectrum of an unsigned integer. - Special
-                        *
-                        * We must limit the keys or else we get different results on
-                        * amd64/x86 boxes. - psychon */
-                       const unsigned int limit = 0x80000000;
-                       compatkey[0] = (unsigned int) tag->getInt("key1");
-                       compatkey[1] = (unsigned int) tag->getInt("key2");
-                       compatkey[2] = (unsigned int) tag->getInt("key3");
-                       compatkey[3] = (unsigned int) tag->getInt("key4");
-
-                       if (!lowercase)
-                       {
-                               xtab[0] = "F92E45D871BCA630";
-                               xtab[1] = "A1B9D80C72E653F4";
-                               xtab[2] = "1ABC078934DEF562";
-                               xtab[3] = "ABCDEF5678901234";
-                       }
-                       else
-                       {
-                               xtab[0] = "f92e45d871bca630";
-                               xtab[1] = "a1b9d80c72e653f4";
-                               xtab[2] = "1abc078934def562";
-                               xtab[3] = "abcdef5678901234";
-                       }
+                       ConfigTag* tag = i->second;
+
+                       // Ensure that we have the <cloak:key> parameter.
+                       const std::string key = tag->getString("key");
+                       if (key.empty())
+                               throw ModuleException("You have not defined a cloaking key. Define <cloak:key> as a " + ConvToStr(minkeylen) + "+ character network-wide secret, at " + tag->getTagLocation());
 
-                       if (prefix.empty())
-                               prefix = ServerInstance->Config->Network;
+                       // If we are the first cloak method then mandate a strong key.
+                       if (i == tags.first && key.length() < minkeylen)
+                               throw ModuleException("Your cloaking key is not secure. It should be at least " + ConvToStr(minkeylen) + " characters long, at " + tag->getTagLocation());
 
-                       if (!compatkey[0] || !compatkey[1] || !compatkey[2] || !compatkey[3] ||
-                               compatkey[0] >= limit || compatkey[1] >= limit || compatkey[2] >= limit || compatkey[3] >= limit)
+                       const std::string mode = tag->getString("mode");
+                       const std::string prefix = tag->getString("prefix");
+                       const std::string suffix = tag->getString("suffix", ".IP");
+                       if (stdalgo::string::equalsci(mode, "half"))
                        {
-                               std::string detail;
-                               if (!compatkey[0] || compatkey[0] >= limit)
-                                       detail = "<cloak:key1> is not valid, it may be set to a too high/low value, or it may not exist.";
-                               else if (!compatkey[1] || compatkey[1] >= limit)
-                                       detail = "<cloak:key2> is not valid, it may be set to a too high/low value, or it may not exist.";
-                               else if (!compatkey[2] || compatkey[2] >= limit)
-                                       detail = "<cloak:key3> is not valid, it may be set to a too high/low value, or it may not exist.";
-                               else if (!compatkey[3] || compatkey[3] >= limit)
-                                       detail = "<cloak:key4> is not valid, it may be set to a too high/low value, or it may not exist.";
-
-                               throw ModuleException("You have not defined cloak keys for m_cloaking!!! THIS IS INSECURE AND SHOULD BE CHECKED! - " + detail);
+                               unsigned int domainparts = tag->getUInt("domainparts", 3, 1, 10);
+                               newcloaks.push_back(CloakInfo(MODE_HALF_CLOAK, key, prefix, suffix, domainparts));
                        }
+                       else if (stdalgo::string::equalsci(mode, "full"))
+                               newcloaks.push_back(CloakInfo(MODE_OPAQUE, key, prefix, suffix));
+                       else
+                               throw ModuleException(mode + " is an invalid value for <cloak:mode>; acceptable values are 'half' and 'full', at " + tag->getTagLocation());
                }
-               else
-               {
-                       key = tag->getString("key");
-                       if (key.empty() || key == "secret")
-                               throw ModuleException("You have not defined cloak keys for m_cloaking. Define <cloak:key> as a network-wide secret.");
-               }
+
+               // The cloak configuration was valid so we can apply it.
+               cloaks.swap(newcloaks);
        }
 
-       std::string GenCloak(const irc::sockets::sockaddrs& ip, const std::string& ipstr, const std::string& host)
+       std::string GenCloak(const CloakInfo& info, const irc::sockets::sockaddrs& ip, const std::string& ipstr, const std::string& host)
        {
                std::string chost;
 
                irc::sockets::sockaddrs hostip;
                bool host_is_ip = irc::sockets::aptosa(host, ip.port(), hostip) && hostip == ip;
 
-               switch (mode)
+               switch (info.mode)
                {
-                       case MODE_COMPAT_HOST:
-                       {
-                               if (!host_is_ip)
-                               {
-                                       std::string tail = LastTwoDomainParts(host);
-
-                                       // xtab is not used here due to a bug in 1.2 cloaking
-                                       chost = prefix + "-" + Hash->sumIV(compatkey, "0123456789abcdef", host).substr(0,8) + tail;
-
-                                       /* Fix by brain - if the cloaked host is > the max length of a host (64 bytes
-                                        * according to the DNS RFC) then they get cloaked as an IP.
-                                        */
-                                       if (chost.length() <= 64)
-                                               break;
-                               }
-                               // fall through to IP cloak
-                       }
-                       case MODE_COMPAT_IPONLY:
-                               if (ip.sa.sa_family == AF_INET6)
-                                       chost = CompatCloak6(ipstr.c_str());
-                               else
-                                       chost = CompatCloak4(ipstr.c_str());
-                               break;
                        case MODE_HALF_CLOAK:
                        {
                                if (!host_is_ip)
-                                       chost = prefix + SegmentCloak(host, 1, 6) + LastTwoDomainParts(host);
+                                       chost = info.prefix + SegmentCloak(info, host, 1, 6) + VisibleDomainParts(host, info.domainparts);
                                if (chost.empty() || chost.length() > 50)
-                                       chost = SegmentIP(ip, false);
+                                       chost = SegmentIP(info, ip, false);
                                break;
                        }
                        case MODE_OPAQUE:
                        default:
-                               chost = SegmentIP(ip, true);
+                               chost = SegmentIP(info, ip, true);
                }
                return chost;
        }
 
-       void OnUserConnect(LocalUser* dest)
+       void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
+       {
+               // Connecting users are handled in OnUserConnect not here.
+               if (user->registered != REG_ALL)
+                       return;
+
+               // Remove the cloaks and generate new ones.
+               cu.ext.unset(user);
+               OnUserConnect(user);
+
+               // If a user is using a cloak then update it.
+               if (user->IsModeSet(cu))
+               {
+                       CloakList* cloaklist = cu.ext.get(user);
+                       user->ChangeDisplayedHost(cloaklist->front());
+               }
+       }
+
+       void OnUserConnect(LocalUser* dest) CXX11_OVERRIDE
        {
-               std::string* cloak = cu.ext.get(dest);
-               if (cloak)
+               if (cu.ext.get(dest))
                        return;
 
-               cu.ext.set(dest, GenCloak(dest->client_sa, dest->GetIPString(), dest->host));
+               // TODO: decide how we are going to cloak AF_UNIX hostnames.
+               if (dest->client_sa.family() != AF_INET && dest->client_sa.family() != AF_INET6)
+                       return;
+
+               CloakList cloaklist;
+               for (std::vector<CloakInfo>::const_iterator iter = cloaks.begin(); iter != cloaks.end(); ++iter)
+                       cloaklist.push_back(GenCloak(*iter, dest->client_sa, dest->GetIPString(), dest->GetRealHost()));
+               cu.ext.set(dest, cloaklist);
        }
 };
 
-CmdResult CommandCloak::Handle(const std::vector<std::string> &parameters, User *user)
+CmdResult CommandCloak::Handle(User* user, const Params& parameters)
 {
        ModuleCloaking* mod = (ModuleCloaking*)(Module*)creator;
-       irc::sockets::sockaddrs sa;
-       std::string cloak;
-
-       if (irc::sockets::aptosa(parameters[0], 0, sa))
-               cloak = mod->GenCloak(sa, parameters[0], parameters[0]);
-       else
-               cloak = mod->GenCloak(sa, "", parameters[0]);
 
-       user->WriteServ("NOTICE %s :*** Cloak for %s is %s", user->nick.c_str(), parameters[0].c_str(), cloak.c_str());
+       // If we're cloaking an IP address we pass it in the IP field too.
+       irc::sockets::sockaddrs sa;
+       const char* ipaddr = irc::sockets::aptosa(parameters[0], 0, sa) ? parameters[0].c_str() : "";
 
+       unsigned int id = 0;
+       for (std::vector<CloakInfo>::const_iterator iter = mod->cloaks.begin(); iter != mod->cloaks.end(); ++iter)
+       {
+               const std::string cloak = mod->GenCloak(*iter, sa, ipaddr, parameters[0]);
+               user->WriteNotice(InspIRCd::Format("*** Cloak #%u for %s is %s", ++id, parameters[0].c_str(), cloak.c_str()));
+       }
        return CMD_SUCCESS;
 }
 
index 92b1bda78fb8a8d4d186a072405c1a56340294ed..1a3a5fd7bb04498401bfb966149e0c53e7a3d99d 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/ircv3_batch.h"
 
-/* $ModDesc: Provides the /CLONES command to retrieve information on clones. */
+enum
+{
+       // InspIRCd-specific.
+       RPL_CLONES = 399
+};
 
-/** Handle /CLONES
- */
-class CommandClones : public Command
+class CommandClones : public SplitCommand
 {
+ private:
+       IRCv3::Batch::API batchmanager;
+       IRCv3::Batch::Batch batch;
+
  public:
-       CommandClones(Module* Creator) : Command(Creator,"CLONES", 1)
+       CommandClones(Module* Creator)
+               : SplitCommand(Creator,"CLONES", 1)
+               , batchmanager(Creator)
+               , batch("inspircd.org/clones")
        {
-               flags_needed = 'o'; syntax = "<limit>";
+               flags_needed = 'o';
+               syntax = "<limit>";
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
        {
+               unsigned int limit = ConvToNum<unsigned int>(parameters[0]);
 
-               std::string clonesstr = "304 " + user->nick + " :CLONES";
-
-               unsigned long limit = atoi(parameters[0].c_str());
+               // Syntax of a CLONES reply:
+               // :irc.example.com BATCH +<id> inspircd.org/clones :<min-count>
+               // @batch=<id> :irc.example.com 399 <client> <local-count> <remote-count> <cidr-mask>
+               /// :irc.example.com BATCH :-<id>
 
-               /*
-                * Syntax of a /clones reply:
-                *  :server.name 304 target :CLONES START
-                *  :server.name 304 target :CLONES <count> <ip>
-                *  :server.name 304 target :CLONES END
-                */
-
-               user->WriteServ(clonesstr + " START");
+               if (batchmanager)
+               {
+                       batchmanager->Start(batch);
+                       batch.GetBatchStartMessage().PushParam(ConvToStr(limit));
+               }
 
-               /* hostname or other */
-               // XXX I really don't like marking global_clones public for this. at all. -- w00t
-               for (clonemap::iterator x = ServerInstance->Users->global_clones.begin(); x != ServerInstance->Users->global_clones.end(); x++)
+               const UserManager::CloneMap& clonemap = ServerInstance->Users->GetCloneMap();
+               for (UserManager::CloneMap::const_iterator i = clonemap.begin(); i != clonemap.end(); ++i)
                {
-                       if (x->second >= limit)
-                               user->WriteServ(clonesstr + " "+ ConvToStr(x->second) + " " + x->first.str());
+                       const UserManager::CloneCounts& counts = i->second;
+                       if (counts.global < limit)
+                               continue;
+
+                       Numeric::Numeric numeric(RPL_CLONES);
+                       numeric.push(counts.local);
+                       numeric.push(counts.global);
+                       numeric.push(i->first.str());
+
+                       ClientProtocol::Messages::Numeric numericmsg(numeric, user);
+                       batch.AddToBatch(numericmsg);
+                       user->Send(ServerInstance->GetRFCEvents().numeric, numericmsg);
                }
 
-               user->WriteServ(clonesstr + " END");
+               if (batchmanager)
+                       batchmanager->End(batch);
 
                return CMD_SUCCESS;
        }
 };
 
-
 class ModuleClones : public Module
 {
- private:
-       CommandClones cmd;
  public:
-       ModuleClones() : cmd(this)
-       {
-       }
-
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
+       CommandClones cmd;
 
-       virtual ~ModuleClones()
+ public:
+       ModuleClones()
+               : cmd(this)
        {
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides the /CLONES command to retrieve information on clones.", VF_VENDOR);
+               return Version("Provides the CLONES command to retrieve information on clones", VF_VENDOR);
        }
-
-
 };
 
 MODULE_INIT(ModuleClones)
diff --git a/src/modules/m_close.cpp b/src/modules/m_close.cpp
deleted file mode 100644 (file)
index 8b0ea34..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@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"
-
-/* $ModDesc: Provides /CLOSE functionality */
-
-/** Handle /CLOSE
- */
-class CommandClose : public Command
-{
- public:
-       /* Command 'close', needs operator */
-       CommandClose(Module* Creator) : Command(Creator,"CLOSE", 0)
-       {
-       flags_needed = 'o'; }
-
-       CmdResult Handle (const std::vector<std::string> &parameters, User *src)
-       {
-               std::map<std::string,int> closed;
-
-               for (LocalUserList::const_iterator u = ServerInstance->Users->local_users.begin(); u != ServerInstance->Users->local_users.end(); ++u)
-               {
-                       LocalUser* user = *u;
-                       if (user->registered != REG_ALL)
-                       {
-                               ServerInstance->Users->QuitUser(user, "Closing all unknown connections per request");
-                               std::string key = ConvToStr(user->GetIPString())+"."+ConvToStr(user->GetServerPort());
-                               closed[key]++;
-                       }
-               }
-
-               int total = 0;
-               for (std::map<std::string,int>::iterator ci = closed.begin(); ci != closed.end(); ci++)
-               {
-                       src->WriteServ("NOTICE %s :*** Closed %d unknown connection%s from [%s]",src->nick.c_str(),(*ci).second,((*ci).second>1)?"s":"",(*ci).first.c_str());
-                       total += (*ci).second;
-               }
-               if (total)
-                       src->WriteServ("NOTICE %s :*** %i unknown connection%s closed",src->nick.c_str(),total,(total>1)?"s":"");
-               else
-                       src->WriteServ("NOTICE %s :*** No unknown connections found",src->nick.c_str());
-
-               return CMD_SUCCESS;
-       }
-};
-
-class ModuleClose : public Module
-{
-       CommandClose cmd;
- public:
-       ModuleClose()
-               : cmd(this)
-       {
-       }
-
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleClose()
-       {
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides /CLOSE functionality", VF_VENDOR);
-       }
-};
-
-MODULE_INIT(ModuleClose)
index afa17add4512d437f41d58ecdad182d40c9b52ad..f3c206a4492bf17c4b6015f990820e59d91b04ef 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2019 Peter Powell <petpow@saberuk.com>
  *   Copyright (C) 2007 Craig Edwards <craigedwards@brainbox.cc>
  *
  * This file is part of InspIRCd.  InspIRCd is free software: you can
 
 
 #include "inspircd.h"
+#include "modules/ctctags.h"
 
-/* $ModDesc: Adds user mode +c, which if set, users must be on a common channel with you to private message you */
-
-/** Handles user mode +c
- */
-class PrivacyMode : public SimpleUserModeHandler
+class ModuleCommonChans
+       : public CTCTags::EventListener
+       , public Module
 {
- public:
-       PrivacyMode(Module* Creator) : SimpleUserModeHandler(Creator, "deaf_commonchan", 'c') { }
-};
+ private:
+       SimpleUserModeHandler mode;
 
-class ModulePrivacyMode : public Module
-{
-       PrivacyMode pm;
- public:
-       ModulePrivacyMode() : pm(this)
+       ModResult HandleMessage(User* user, const MessageTarget& target)
        {
-       }
+               if (target.type != MessageTarget::TYPE_USER)
+                       return MOD_RES_PASSTHRU;
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(pm);
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               User* targuser = target.Get<User>();
+               if (!targuser->IsModeSet(mode) || !user->SharesChannelWith(targuser))
+                       return MOD_RES_PASSTHRU;
+
+               if (user->HasPrivPermission("users/ignore-commonchans") || user->server->IsULine())
+                       return MOD_RES_PASSTHRU;
+
+               user->WriteNumeric(ERR_CANTSENDTOUSER, targuser->nick, "You are not permitted to send private messages to this user (+c is set)");
+               return MOD_RES_DENY;
        }
 
-       virtual ~ModulePrivacyMode()
+ public:
+       ModuleCommonChans()
+               : CTCTags::EventListener(this)
+               , mode(this, "deaf_commonchan", 'c')
        {
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Adds user mode +c, which if set, users must be on a common channel with you to private message you", VF_VENDOR);
+               return Version("Provides user mode +c, requires users to share a common channel with you to private message you", VF_VENDOR);
        }
 
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               if (target_type == TYPE_USER)
-               {
-                       User* t = (User*)dest;
-                       if (!IS_OPER(user) && (t->IsModeSet('c')) && (!ServerInstance->ULine(user->server)) && !user->SharesChannelWith(t))
-                       {
-                               user->WriteNumeric(ERR_CANTSENDTOUSER, "%s %s :You are not permitted to send private messages to this user (+c set)", user->nick.c_str(), t->nick.c_str());
-                               return MOD_RES_DENY;
-                       }
-               }
-               return MOD_RES_PASSTHRU;
+               return HandleMessage(user, target);
        }
 
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
        {
-               return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
+               return HandleMessage(user, target);
        }
 };
 
-
-MODULE_INIT(ModulePrivacyMode)
+MODULE_INIT(ModuleCommonChans)
index 6b13ab1aab8da7556d193cd8249b4e394c9ef391..e5df97d9018bfac11dcb2ff057fd7d8b9e12a0e5 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Forces users to join the specified channel(s) on connect */
+static void JoinChannels(LocalUser* u, const std::string& chanlist)
+{
+       irc::commasepstream chans(chanlist);
+       std::string chan;
+
+       while (chans.GetToken(chan))
+       {
+               if (ServerInstance->IsChannel(chan))
+                       Channel::JoinUser(u, chan);
+       }
+}
+
+class JoinTimer : public Timer
+{
+ private:
+       LocalUser* const user;
+       const std::string channels;
+       SimpleExtItem<JoinTimer>& ext;
+
+ public:
+       JoinTimer(LocalUser* u, SimpleExtItem<JoinTimer>& ex, const std::string& chans, unsigned int delay)
+               : Timer(delay, false)
+               , user(u), channels(chans), ext(ex)
+       {
+               ServerInstance->Timers.AddTimer(this);
+       }
+
+       bool Tick(time_t time) CXX11_OVERRIDE
+       {
+               if (user->chans.empty())
+                       JoinChannels(user, channels);
+
+               ext.unset(user);
+               return false;
+       }
+};
 
 class ModuleConnJoin : public Module
 {
-       public:
-               void init()
-               {
-                       Implementation eventlist[] = { I_OnPostConnect };
-                       ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               }
+       SimpleExtItem<JoinTimer> ext;
+       std::string defchans;
+       unsigned int defdelay;
 
-               void Prioritize()
-               {
-                       ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIORITY_LAST);
-               }
+ public:
+       ModuleConnJoin()
+               : ext("join_timer", ExtensionItem::EXT_USER, this)
+       {
+       }
 
-               Version GetVersion()
-               {
-                       return Version("Forces users to join the specified channel(s) on connect", VF_VENDOR);
-               }
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("autojoin");
+               defchans = tag->getString("channel");
+               defdelay = tag->getDuration("delay", 0, 0, 60*15);
+       }
 
-               void OnPostConnect(User* user)
-               {
-                       if (!IS_LOCAL(user))
-                               return;
+       void Prioritize() CXX11_OVERRIDE
+       {
+               ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIORITY_LAST);
+       }
 
-                       std::string chanlist = ServerInstance->Config->ConfValue("autojoin")->getString("channel");
-                       chanlist = user->GetClass()->config->getString("autojoin", chanlist);
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Forces users to join the specified channel(s) on connect", VF_VENDOR);
+       }
 
-                       irc::commasepstream chans(chanlist);
-                       std::string chan;
+       void OnPostConnect(User* user) CXX11_OVERRIDE
+       {
+               LocalUser* localuser = IS_LOCAL(user);
+               if (!localuser)
+                       return;
 
-                       while (chans.GetToken(chan))
-                       {
-                               if (ServerInstance->IsChannel(chan.c_str(), ServerInstance->Config->Limits.ChanMax))
-                                       Channel::JoinUser(user, chan.c_str(), false, "", false, ServerInstance->Time());
-                       }
+               std::string chanlist = localuser->GetClass()->config->getString("autojoin");
+               unsigned int chandelay = localuser->GetClass()->config->getDuration("autojoindelay", 0, 0, 60*15);
+
+               if (chanlist.empty())
+               {
+                       if (defchans.empty())
+                               return;
+                       chanlist = defchans;
+                       chandelay = defdelay;
                }
-};
 
+               if (!chandelay)
+                       JoinChannels(localuser, chanlist);
+               else
+                       ext.set(localuser, new JoinTimer(localuser, ext, chanlist, chandelay));
+       }
+};
 
 MODULE_INIT(ModuleConnJoin)
index a21462ddf39a79b33dc91e537f43ee2cd802d8c0..3132aed4016685a9cc97948ecb1f3f5139acbbc9 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Sets (and unsets) modes on users when they connect */
-
 class ModuleModesOnConnect : public Module
 {
  public:
-       void init()
-       {
-               ServerInstance->Modules->Attach(I_OnUserConnect, this);
-       }
-
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                // for things like +x on connect, important, otherwise we have to resort to config order (bleh) -- w00t
                ServerInstance->Modules->SetPriority(this, I_OnUserConnect, PRIORITY_FIRST);
        }
 
-       virtual ~ModuleModesOnConnect()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Sets (and unsets) modes on users when they connect", VF_VENDOR);
        }
 
-       virtual void OnUserConnect(LocalUser* user)
+       void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
        {
-               // Backup and zero out the disabled usermodes, so that we can override them here.
-               char save[64];
-               memcpy(save, ServerInstance->Config->DisabledUModes,
-                               sizeof(ServerInstance->Config->DisabledUModes));
-               memset(ServerInstance->Config->DisabledUModes, 0, 64);
-
                ConfigTag* tag = user->MyClass->config;
                std::string ThisModes = tag->getString("modes");
                if (!ThisModes.empty())
                {
                        std::string buf;
-                       std::stringstream ss(ThisModes);
-
-                       std::vector<std::string> tokens;
-
-                       // split ThisUserModes into modes and mode params
-                       while (ss >> buf)
-                               tokens.push_back(buf);
+                       irc::spacesepstream ss(ThisModes);
 
-                       std::vector<std::string> modes;
+                       CommandBase::Params modes;
                        modes.push_back(user->nick);
-                       modes.push_back(tokens[0]);
 
-                       if (tokens.size() > 1)
-                       {
-                               // process mode params
-                               for (unsigned int k = 1; k < tokens.size(); k++)
-                               {
-                                       modes.push_back(tokens[k]);
-                               }
-                       }
+                       // split ThisUserModes into modes and mode params
+                       while (ss.GetToken(buf))
+                               modes.push_back(buf);
 
-                       ServerInstance->Parser->CallHandler("MODE", modes, user);
+                       ServerInstance->Parser.CallHandler("MODE", modes, user);
                }
-
-               memcpy(ServerInstance->Config->DisabledUModes, save, 64);
        }
 };
 
index 1d48220a6b096330cbafecd9e077216149042f35..f2e9590c81ece0598613f0c73e28360d5cf0d2df 100644 (file)
@@ -24,8 +24,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Forces connecting clients to send a PONG message back to the server before they can complete their connection */
-
 class ModuleWaitPong : public Module
 {
        bool sendsnotice;
@@ -34,39 +32,33 @@ class ModuleWaitPong : public Module
 
  public:
        ModuleWaitPong()
-        : ext("waitpong_pingstr", this)
-       {
-       }
-
-       void init()
+               : ext("waitpong_pingstr", ExtensionItem::EXT_USER, this)
        {
-               ServerInstance->Modules->AddService(ext);
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnUserRegister, I_OnCheckReady, I_OnPreCommand, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("waitpong");
                sendsnotice = tag->getBool("sendsnotice", true);
                killonbadreply = tag->getBool("killonbadreply", true);
        }
 
-       ModResult OnUserRegister(LocalUser* user)
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
        {
                std::string pingrpl = ServerInstance->GenRandomStr(10);
-
-               user->Write("PING :%s", pingrpl.c_str());
+               {
+                       ClientProtocol::Messages::Ping pingmsg(pingrpl);
+                       user->Send(ServerInstance->GetRFCEvents().ping, pingmsg);
+               }
 
                if(sendsnotice)
-                       user->WriteServ("NOTICE %s :*** If you are having problems connecting due to ping timeouts, please type /quote PONG %s or /raw PONG %s now.", user->nick.c_str(), pingrpl.c_str(), pingrpl.c_str());
+                       user->WriteNotice("*** If you are having problems connecting due to ping timeouts, please type /quote PONG " + pingrpl + " or /raw PONG " + pingrpl + " now.");
 
                ext.set(user, pingrpl);
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser* user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
        {
                if (command == "PONG")
                {
@@ -90,20 +82,15 @@ class ModuleWaitPong : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnCheckReady(LocalUser* user)
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
                return ext.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
        }
 
-       ~ModuleWaitPong()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Require pong prior to registration", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleWaitPong)
index 26120add984381529a736bcd3af0f8b67b4e0d61..9ce5830637842c9d62ae9f683119f40976540557 100644 (file)
 #include "inspircd.h"
 #include "xline.h"
 
-/* $ModDesc: Throttles the connections of IP ranges who try to connect flood. */
-
 class ModuleConnectBan : public Module
 {
- private:
-       clonemap connects;
+       typedef std::map<irc::sockets::cidr_mask, unsigned int> ConnectMap;
+       ConnectMap connects;
        unsigned int threshold;
        unsigned int banduration;
        unsigned int ipv4_cidr;
        unsigned int ipv6_cidr;
- public:
-       void init()
-       {
-               Implementation eventlist[] = { I_OnSetUserIP, I_OnGarbageCollect, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
-       }
+       std::string banmessage;
 
-       virtual ~ModuleConnectBan()
-       {
-       }
-
-       virtual Version GetVersion()
+ public:
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Throttles the connections of IP ranges who try to connect flood.", VF_VENDOR);
+               return Version("Throttles the connections of IP ranges who try to connect flood", VF_VENDOR);
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("connectban");
 
-               ipv4_cidr = tag->getInt("ipv4cidr", 32);
-               if (ipv4_cidr == 0)
-                       ipv4_cidr = 32;
-
-               ipv6_cidr = tag->getInt("ipv6cidr", 128);
-               if (ipv6_cidr == 0)
-                       ipv6_cidr = 128;
-
-               threshold = tag->getInt("threshold", 10);
-               if (threshold == 0)
-                       threshold = 10;
-
-               banduration = ServerInstance->Duration(tag->getString("duration", "10m"));
-               if (banduration == 0)
-                       banduration = 10*60;
+               ipv4_cidr = tag->getUInt("ipv4cidr", 32, 1, 32);
+               ipv6_cidr = tag->getUInt("ipv6cidr", 128, 1, 128);
+               threshold = tag->getUInt("threshold", 10, 1);
+               banduration = tag->getDuration("duration", 10*60, 1);
+               banmessage = tag->getString("banmessage", "Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.");
        }
 
-       virtual void OnSetUserIP(LocalUser* u)
+       void OnSetUserIP(LocalUser* u) CXX11_OVERRIDE
        {
                if (u->exempt)
                        return;
 
-               int range = 32;
-               clonemap::iterator i;
+               unsigned char range = 32;
 
-               switch (u->client_sa.sa.sa_family)
+               switch (u->client_sa.family())
                {
                        case AF_INET6:
                                range = ipv6_cidr;
@@ -87,7 +65,7 @@ class ModuleConnectBan : public Module
                }
 
                irc::sockets::cidr_mask mask(u->client_sa, range);
-               i = connects.find(mask);
+               ConnectMap::iterator i = connects.find(mask);
 
                if (i != connects.end())
                {
@@ -95,8 +73,8 @@ class ModuleConnectBan : public Module
 
                        if (i->second >= threshold)
                        {
-                               // Create zline for set duration.
-                               ZLine* zl = new ZLine(ServerInstance->Time(), banduration, ServerInstance->Config->ServerName, "Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.", mask.str());
+                               // Create Z-line for set duration.
+                               ZLine* zl = new ZLine(ServerInstance->Time(), banduration, ServerInstance->Config->ServerName, banmessage, mask.str());
                                if (!ServerInstance->XLines->AddLine(zl, NULL))
                                {
                                        delete zl;
@@ -104,9 +82,8 @@ class ModuleConnectBan : public Module
                                }
                                ServerInstance->XLines->ApplyLines();
                                std::string maskstr = mask.str();
-                               std::string timestr = ServerInstance->TimeString(zl->expiry);
-                               ServerInstance->SNO->WriteGlobalSno('x',"Module m_connectban added Z:line on *@%s to expire on %s: Connect flooding",
-                                       maskstr.c_str(), timestr.c_str());
+                               ServerInstance->SNO->WriteGlobalSno('x', "Z-line added by module m_connectban on %s to expire in %s (on %s): Connect flooding",
+                                       maskstr.c_str(), InspIRCd::DurationString(zl->duration).c_str(), InspIRCd::TimeString(zl->expiry).c_str());
                                ServerInstance->SNO->WriteGlobalSno('a', "Connect flooding from IP range %s (%d)", maskstr.c_str(), threshold);
                                connects.erase(i);
                        }
@@ -117,9 +94,9 @@ class ModuleConnectBan : public Module
                }
        }
 
-       virtual void OnGarbageCollect()
+       void OnGarbageCollect() CXX11_OVERRIDE
        {
-               ServerInstance->Logs->Log("m_connectban",DEBUG, "Clearing map.");
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Clearing map.");
                connects.clear();
        }
 };
index f77691e3276a8efe2f6649b4da04d31a588b79fc..5070dd3a76b26e6e629f71b4415451e104c43ca3 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Connection throttle */
-
 class ModuleConnFlood : public Module
 {
-private:
-       int seconds, timeout, boot_wait;
+       unsigned int seconds;
+       unsigned int timeout;
+       unsigned int boot_wait;
        unsigned int conns;
        unsigned int maxconns;
        bool throttled;
@@ -39,35 +38,28 @@ public:
        {
        }
 
-       void init()
-       {
-               InitConf();
-               Implementation eventlist[] = { I_OnRehash, I_OnUserRegister };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Connection throttle", VF_VENDOR);
        }
 
-       void InitConf()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                /* read configuration variables */
                ConfigTag* tag = ServerInstance->Config->ConfValue("connflood");
                /* throttle configuration */
-               seconds = tag->getInt("seconds");
-               maxconns = tag->getInt("maxconns");
-               timeout = tag->getInt("timeout");
+               seconds = tag->getDuration("period", tag->getDuration("seconds", 30));
+               maxconns = tag->getUInt("maxconns", 3);
+               timeout = tag->getDuration("timeout", 30);
                quitmsg = tag->getString("quitmsg");
 
                /* seconds to wait when the server just booted */
-               boot_wait = tag->getInt("bootwait");
+               boot_wait = tag->getDuration("bootwait", 10);
 
                first = ServerInstance->Time();
        }
 
-       virtual ModResult OnUserRegister(LocalUser* user)
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
        {
                if (user->exempt)
                        return MOD_RES_PASSTHRU;
@@ -114,12 +106,6 @@ public:
                }
                return MOD_RES_PASSTHRU;
        }
-
-       virtual void OnRehash(User* user)
-       {
-               InitConf();
-       }
-
 };
 
 MODULE_INIT(ModuleConnFlood)
index dfc60e082560190d62736bc02c3bcd006d41da24..831aa908db92b0bbb3a797c40d55d085bed96097 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Allows custom prefix modes to be created. */
-
-class CustomPrefixMode : public ModeHandler
+class CustomPrefixMode : public PrefixMode
 {
  public:
        reference<ConfigTag> tag;
-       int rank;
-       bool depriv;
-       CustomPrefixMode(Module* parent, ConfigTag* Tag)
-               : ModeHandler(parent, Tag->getString("name"), 0, PARAM_ALWAYS, MODETYPE_CHANNEL), tag(Tag)
-       {
-               list = true;
-               m_paramtype = TR_NICK;
-               std::string v = tag->getString("prefix");
-               prefix = v.c_str()[0];
-               v = tag->getString("letter");
-               mode = v.c_str()[0];
-               rank = tag->getInt("rank");
-               levelrequired = tag->getInt("ranktoset", rank);
-               depriv = tag->getBool("depriv", true);
-       }
-
-       unsigned int GetPrefixRank()
-       {
-               return rank;
-       }
-
-       ModResult AccessCheck(User* src, Channel*, std::string& value, bool adding)
-       {
-               if (!adding && src->nick == value && depriv)
-                       return MOD_RES_ALLOW;
-               return MOD_RES_PASSTHRU;
-       }
-
-       void RemoveMode(Channel* channel, irc::modestacker* stack)
-       {
-               const UserMembList* cl = channel->GetUsers();
-               std::vector<std::string> mode_junk;
-               mode_junk.push_back(channel->name);
-               irc::modestacker modestack(false);
-               std::deque<std::string> stackresult;
-
-               for (UserMembCIter i = cl->begin(); i != cl->end(); i++)
-               {
-                       if (i->second->hasMode(mode))
-                       {
-                               if (stack)
-                                       stack->Push(this->GetModeChar(), i->first->nick);
-                               else
-                                       modestack.Push(this->GetModeChar(), i->first->nick);
-                       }
-               }
-
-               if (stack)
-                       return;
 
-               while (modestack.GetStackedLine(stackresult))
-               {
-                       mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
-                       ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
-                       mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
-               }
-       }
-
-       void RemoveMode(User* user, irc::modestacker* stack)
+       CustomPrefixMode(Module* parent, const std::string& Name, char Letter, char Prefix, ConfigTag* Tag)
+               : PrefixMode(parent, Name, Letter, 0, Prefix)
+               , tag(Tag)
        {
-       }
-
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
-       {
-               return MODEACTION_ALLOW;
+               unsigned long rank = tag->getUInt("rank", 0, 0, UINT_MAX);
+               unsigned long setrank = tag->getUInt("ranktoset", prefixrank, rank, UINT_MAX);
+               unsigned long unsetrank = tag->getUInt("ranktounset", setrank, setrank, UINT_MAX);
+               bool depriv = tag->getBool("depriv", true);
+               this->Update(rank, setrank, unsetrank, depriv);
+
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Created the %s prefix: letter=%c prefix=%c rank=%u ranktoset=%u ranktounset=%i depriv=%d",
+                       name.c_str(), GetModeChar(), GetPrefix(), GetPrefixRank(), GetLevelRequired(true), GetLevelRequired(false), CanSelfRemove());
        }
 };
 
@@ -97,41 +43,65 @@ class ModuleCustomPrefix : public Module
 {
        std::vector<CustomPrefixMode*> modes;
  public:
-       ModuleCustomPrefix()
-       {
-       }
-
-       void init()
+       void init() CXX11_OVERRIDE
        {
                ConfigTagList tags = ServerInstance->Config->ConfTags("customprefix");
-               while (tags.first != tags.second)
+               for (ConfigIter iter = tags.first; iter != tags.second; ++iter)
                {
-                       ConfigTag* tag = tags.first->second;
-                       tags.first++;
-                       CustomPrefixMode* mh = new CustomPrefixMode(this, tag);
-                       modes.push_back(mh);
-                       if (mh->rank <= 0)
-                               throw ModuleException("Rank must be specified for prefix at " + tag->getTagLocation());
-                       if (!isalpha(mh->GetModeChar()))
-                               throw ModuleException("Mode must be a letter for prefix at " + tag->getTagLocation());
+                       ConfigTag* tag = iter->second;
+
+                       const std::string name = tag->getString("name");
+                       if (name.empty())
+                               throw ModuleException("<customprefix:name> must be specified at " + tag->getTagLocation());
+
+                       if (tag->getBool("change"))
+                       {
+                               ModeHandler* mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL);
+                               if (!mh)
+                                       throw ModuleException("<customprefix:change> specified for a nonexistent mode at " + tag->getTagLocation());
+
+                               PrefixMode* pm = mh->IsPrefixMode();
+                               if (!pm)
+                                       throw ModuleException("<customprefix:change> specified for a non-prefix mode at " + tag->getTagLocation());
+
+                               unsigned long rank = tag->getUInt("rank", pm->GetPrefixRank(), 0, UINT_MAX);
+                               unsigned long setrank = tag->getUInt("ranktoset", pm->GetLevelRequired(true), rank, UINT_MAX);
+                               unsigned long unsetrank = tag->getUInt("ranktounset", pm->GetLevelRequired(false), setrank, UINT_MAX);
+                               bool depriv = tag->getBool("depriv", pm->CanSelfRemove());
+                               pm->Update(rank, setrank, unsetrank, depriv);
+
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changed the %s prefix: depriv=%u rank=%u ranktoset=%u ranktounset=%u",
+                                       pm->name.c_str(), pm->CanSelfRemove(), pm->GetPrefixRank(), pm->GetLevelRequired(true), pm->GetLevelRequired(false));
+                               continue;
+                       }
+
+                       const std::string letter = tag->getString("letter");
+                       if (letter.length() != 1)
+                               throw ModuleException("<customprefix:letter> must be set to a mode character at " + tag->getTagLocation());
+
+                       const std::string prefix = tag->getString("prefix");
+                       if (prefix.length() != 1)
+                               throw ModuleException("<customprefix:prefix> must be set to a mode prefix at " + tag->getTagLocation());
+
                        try
                        {
+                               CustomPrefixMode* mh = new CustomPrefixMode(this, name, letter[0], prefix[0], tag);
+                               modes.push_back(mh);
                                ServerInstance->Modules->AddService(*mh);
                        }
                        catch (ModuleException& e)
                        {
-                               throw ModuleException(e.err + " (while creating mode from " + tag->getTagLocation() + ")");
+                               throw ModuleException(e.GetReason() + " (while creating mode from " + tag->getTagLocation() + ")");
                        }
                }
        }
 
        ~ModuleCustomPrefix()
        {
-               for (std::vector<CustomPrefixMode*>::iterator i = modes.begin(); i != modes.end(); i++)
-                       delete *i;
+               stdalgo::delete_all(modes);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides custom prefix channel modes", VF_VENDOR);
        }
index c65645bc9209426d0382f3e81cfdc2725daca441..3ffc9f682aaef26fd0193954313eaa8ae3f23338 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/whois.h"
 
-/* $ModDesc: Provides the TITLE command which allows setting of CUSTOM WHOIS TITLE line */
+enum
+{
+       // From UnrealIRCd.
+       RPL_WHOISSPECIAL = 320
+};
+
+struct CustomTitle
+{
+       const std::string name;
+       const std::string password;
+       const std::string hash;
+       const std::string host;
+       const std::string title;
+       const std::string vhost;
+
+       CustomTitle(const std::string& n, const std::string& p, const std::string& h, const std::string& hst, const std::string& t, const std::string& v)
+               : name(n)
+               , password(p)
+               , hash(h)
+               , host(hst)
+               , title(t)
+               , vhost(v)
+       {
+       }
+
+       bool MatchUser(User* user) const
+       {
+               const std::string userHost = user->ident + "@" + user->GetRealHost();
+               const std::string userIP = user->ident + "@" + user->GetIPString();
+               return InspIRCd::MatchMask(host, userHost, userIP);
+       }
+
+       bool CheckPass(User* user, const std::string& pass) const
+       {
+               return ServerInstance->PassCompare(user, password, pass, hash);
+       }
+};
+
+typedef std::multimap<std::string, CustomTitle> CustomVhostMap;
+typedef std::pair<CustomVhostMap::iterator, CustomVhostMap::iterator> MatchingConfigs;
 
 /** Handle /TITLE
  */
@@ -29,105 +69,98 @@ class CommandTitle : public Command
 {
  public:
        StringExtItem ctitle;
-       CommandTitle(Module* Creator) : Command(Creator,"TITLE", 2),
-               ctitle("ctitle", Creator)
-       {
-               syntax = "<user> <password>";
-       }
+       CustomVhostMap configs;
 
-       bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
+       CommandTitle(Module* Creator) : Command(Creator,"TITLE", 2),
+               ctitle("ctitle", ExtensionItem::EXT_USER, Creator)
        {
-               std::stringstream hl(hostlist);
-               std::string xhost;
-               while (hl >> xhost)
-               {
-                       if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
-                       {
-                               return true;
-                       }
-               }
-               return false;
+               syntax = "<username> <password>";
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User* user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               char TheHost[MAXBUF];
-               char TheIP[MAXBUF];
+               MatchingConfigs matching = configs.equal_range(parameters[0]);
 
-               snprintf(TheHost,MAXBUF,"%s@%s",user->ident.c_str(), user->host.c_str());
-               snprintf(TheIP, MAXBUF,"%s@%s",user->ident.c_str(), user->GetIPString());
-
-               ConfigTagList tags = ServerInstance->Config->ConfTags("title");
-               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               for (MatchingConfigs::first_type i = matching.first; i != matching.second; ++i)
                {
-                       std::string Name = i->second->getString("name");
-                       std::string pass = i->second->getString("password");
-                       std::string hash = i->second->getString("hash");
-                       std::string host = i->second->getString("host", "*@*");
-                       std::string title = i->second->getString("title");
-                       std::string vhost = i->second->getString("vhost");
-
-                       if (Name == parameters[0] && !ServerInstance->PassCompare(user, pass, parameters[1], hash) && OneOfMatches(TheHost,TheIP,host.c_str()) && !title.empty())
+                       CustomTitle config = i->second;
+                       if (config.MatchUser(user) && config.CheckPass(user, parameters[1]))
                        {
-                               ctitle.set(user, title);
+                               ctitle.set(user, config.title);
 
-                               ServerInstance->PI->SendMetaData(user, "ctitle", title);
+                               ServerInstance->PI->SendMetaData(user, "ctitle", config.title);
 
-                               if (!vhost.empty())
-                                       user->ChangeDisplayedHost(vhost.c_str());
+                               if (!config.vhost.empty())
+                                       user->ChangeDisplayedHost(config.vhost);
 
-                               user->WriteServ("NOTICE %s :Custom title set to '%s'",user->nick.c_str(), title.c_str());
+                               user->WriteNotice("Custom title set to '" + config.title + "'");
 
                                return CMD_SUCCESS;
                        }
                }
 
-               user->WriteServ("NOTICE %s :Invalid title credentials",user->nick.c_str());
+               user->WriteNotice("Invalid title credentials");
                return CMD_SUCCESS;
        }
 
 };
 
-class ModuleCustomTitle : public Module
+class ModuleCustomTitle : public Module, public Whois::LineEventListener
 {
        CommandTitle cmd;
 
  public:
-       ModuleCustomTitle() : cmd(this)
+       ModuleCustomTitle()
+               : Whois::LineEventListener(this)
+               , cmd(this)
        {
        }
 
-       void init()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd);
-               ServerInstance->Modules->AddService(cmd.ctitle);
-               ServerInstance->Modules->Attach(I_OnWhoisLine, this);
+               ConfigTagList tags = ServerInstance->Config->ConfTags("title");
+               CustomVhostMap newtitles;
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       reference<ConfigTag> tag = i->second;
+                       std::string name = tag->getString("name", "", 1);
+                       if (name.empty())
+                               throw ModuleException("<title:name> is empty at " + tag->getTagLocation());
+
+                       std::string pass = tag->getString("password");
+                       if (pass.empty())
+                               throw ModuleException("<title:password> is empty at " + tag->getTagLocation());
+
+                       std::string hash = tag->getString("hash");
+                       std::string host = tag->getString("host", "*@*");
+                       std::string title = tag->getString("title");
+                       std::string vhost = tag->getString("vhost");
+                       CustomTitle config(name, pass, hash, host, title, vhost);
+                       newtitles.insert(std::make_pair(name, config));
+               }
+               cmd.configs.swap(newtitles);
        }
 
        // :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games.
-       ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+       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 :%s",user->nick.c_str(), dest->nick.c_str(), ctitle->c_str());
+                               whois.SendLine(RPL_WHOISSPECIAL, ctitle);
                        }
                }
                /* Don't block anything */
                return MOD_RES_PASSTHRU;
        }
 
-       ~ModuleCustomTitle()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Custom Title for users", VF_OPTCOMMON | VF_VENDOR);
+               return Version("Provides the TITLE command, custom titles for users", VF_OPTCOMMON | VF_VENDOR);
        }
 };
 
index 383e7b5a2d538bf2ea0a48298788543652522109..5f3dddc6f714262e0d042c8ffcf36b50d5c6bef1 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides command CYCLE, acts as a server-side HOP command to part and rejoin a channel. */
-
 /** Handle /CYCLE
  */
-class CommandCycle : public Command
+class CommandCycle : public SplitCommand
 {
  public:
-       CommandCycle(Module* Creator) : Command(Creator,"CYCLE", 1)
+       CommandCycle(Module* Creator)
+               : SplitCommand(Creator, "CYCLE", 1)
        {
-               Penalty = 3; syntax = "<channel> :[reason]";
-               TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
+               Penalty = 3; syntax = "<channel> [:<reason>]";
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
        {
                Channel* channel = ServerInstance->FindChan(parameters[0]);
-               std::string reason = ConvToStr("Cycling");
+               std::string reason = "Cycling";
 
                if (parameters.size() > 1)
                {
@@ -46,34 +44,27 @@ class CommandCycle : public Command
 
                if (!channel)
                {
-                       user->WriteNumeric(403, "%s %s :No such channel", user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
                        return CMD_FAILURE;
                }
 
                if (channel->HasUser(user))
                {
-                       /*
-                        * technically, this is only ever sent locally, but pays to be safe ;p
-                        */
-                       if (IS_LOCAL(user))
+                       if (channel->GetPrefixValue(user) < VOICE_VALUE && channel->IsBanned(user))
                        {
-                               if (channel->GetPrefixValue(user) < VOICE_VALUE && channel->IsBanned(user))
-                               {
-                                       /* banned, boned. drop the message. */
-                                       user->WriteServ("NOTICE "+user->nick+" :*** You may not cycle, as you are banned on channel " + channel->name);
-                                       return CMD_FAILURE;
-                               }
-
-                               channel->PartUser(user, reason);
-
-                               Channel::JoinUser(user, parameters[0].c_str(), true, "", false, ServerInstance->Time());
+                               // User is banned, send an error and don't cycle them
+                               user->WriteNotice("*** You may not cycle, as you are banned on channel " + channel->name);
+                               return CMD_FAILURE;
                        }
 
+                       channel->PartUser(user, reason);
+                       Channel::JoinUser(user, parameters[0], true);
+
                        return CMD_SUCCESS;
                }
                else
                {
-                       user->WriteNumeric(442, "%s %s :You're not on that channel", user->nick.c_str(), channel->name.c_str());
+                       user->WriteNumeric(ERR_NOTONCHANNEL, channel->name, "You're not on that channel");
                }
 
                return CMD_FAILURE;
@@ -84,26 +75,17 @@ class CommandCycle : public Command
 class ModuleCycle : public Module
 {
        CommandCycle cmd;
+
  public:
        ModuleCycle()
                : cmd(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleCycle()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides command CYCLE, acts as a server-side HOP command to part and rejoin a channel.", VF_VENDOR);
+               return Version("Provides the CYCLE command, acts as a server-side HOP command to part and rejoin a channel", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleCycle)
index 05fff89377f5c09f6d2faf1a097eaf70c0c2040c..e0ea4c7aeed89e7d20ab38cdc05bb0320e4e8ff5 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the /DCCALLOW command */
+enum
+{
+       // From ircd-ratbox.
+       RPL_HELPSTART = 704,
+       RPL_HELPTXT = 705,
+       RPL_ENDOFHELP = 706,
+
+       // InspIRCd-specific?
+       RPL_DCCALLOWSTART = 990,
+       RPL_DCCALLOWLIST = 991,
+       RPL_DCCALLOWEND = 992,
+       RPL_DCCALLOWTIMED = 993,
+       RPL_DCCALLOWPERMANENT = 994,
+       RPL_DCCALLOWREMOVED = 995,
+       ERR_DCCALLOWINVALID = 996,
+       RPL_DCCALLOWEXPIRED = 997,
+       ERR_UNKNOWNDCCALLOWCMD = 998
+};
+
+static const char* const helptext[] =
+{
+       "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. 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
 {
@@ -40,11 +79,17 @@ class DCCAllow
        std::string nickname;
        std::string hostmask;
        time_t set_on;
-       long length;
+       unsigned long length;
 
        DCCAllow() { }
 
-       DCCAllow(const std::string &nick, const std::string &hm, const time_t so, const long ln) : nickname(nick), hostmask(hm), set_on(so), length(ln) { }
+       DCCAllow(const std::string& nick, const std::string& hm, time_t so, unsigned long ln)
+               : nickname(nick)
+               , hostmask(hm)
+               , set_on(so)
+               , length(ln)
+       {
+       }
 };
 
 typedef std::vector<User *> userlist;
@@ -53,21 +98,26 @@ typedef std::vector<DCCAllow> dccallowlist;
 dccallowlist* dl;
 typedef std::vector<BannedFileList> bannedfilelist;
 bannedfilelist bfl;
-SimpleExtItem<dccallowlist>* ext;
+typedef SimpleExtItem<dccallowlist> DCCAllowExt;
 
 class CommandDccallow : public Command
 {
+       DCCAllowExt& ext;
+
  public:
        unsigned int maxentries;
-       CommandDccallow(Module* parent) : Command(parent, "DCCALLOW", 0)
+       unsigned long defaultlength;
+       CommandDccallow(Module* parent, DCCAllowExt& Ext)
+               : Command(parent, "DCCALLOW", 0)
+               , ext(Ext)
        {
                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 */
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               /* syntax: DCCALLOW [+|-]<nick> (<time>) */
+               /* syntax: DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP] */
                if (!parameters.size())
                {
                        // display current DCCALLOW list
@@ -95,21 +145,21 @@ class CommandDccallow : public Command
                                }
                                else
                                {
-                                       user->WriteNumeric(998, "%s :DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP", user->nick.c_str());
+                                       user->WriteNumeric(ERR_UNKNOWNDCCALLOWCMD, "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 == '-')
                                {
                                        // check if it contains any entries
-                                       dl = ext->get(user);
+                                       dl = ext.get(user);
                                        if (dl)
                                        {
                                                for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
@@ -118,7 +168,7 @@ class CommandDccallow : public Command
                                                        if (i->nickname == target->nick)
                                                        {
                                                                dl->erase(i);
-                                                               user->WriteNumeric(995, "%s %s :Removed %s from your DCCALLOW list", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
+                                                               user->WriteNumeric(RPL_DCCALLOWREMOVED, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
                                                                break;
                                                        }
                                                }
@@ -128,22 +178,22 @@ class CommandDccallow : public Command
                                {
                                        if (target == user)
                                        {
-                                               user->WriteNumeric(996, "%s %s :You cannot add yourself to your own DCCALLOW list!", user->nick.c_str(), user->nick.c_str());
+                                               user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "You cannot add yourself to your own DCCALLOW list!");
                                                return CMD_FAILURE;
                                        }
 
-                                       dl = ext->get(user);
+                                       dl = ext.get(user);
                                        if (!dl)
                                        {
                                                dl = new dccallowlist;
-                                               ext->set(user, dl);
+                                               ext.set(user, dl);
                                                // add this user to the userlist
                                                ul.push_back(user);
                                        }
 
                                        if (dl->size() >= maxentries)
                                        {
-                                               user->WriteNumeric(996, "%s %s :Too many nicks on DCCALLOW list", user->nick.c_str(), user->nick.c_str());
+                                               user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "Too many nicks on DCCALLOW list");
                                                return CMD_FAILURE;
                                        }
 
@@ -151,29 +201,32 @@ class CommandDccallow : public Command
                                        {
                                                if (k->nickname == target->nick)
                                                {
-                                                       user->WriteNumeric(996, "%s %s :%s is already on your DCCALLOW list", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
+                                                       user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is already on your DCCALLOW list", target->nick.c_str()));
                                                        return CMD_FAILURE;
                                                }
                                        }
 
-                                       std::string mask = target->nick+"!"+target->ident+"@"+target->dhost;
-                                       std::string default_length = ServerInstance->Config->ConfValue("dccallow")->getString("length");
-
-                                       long length;
+                                       std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
+                                       unsigned long length;
                                        if (parameters.size() < 2)
                                        {
-                                               length = ServerInstance->Duration(default_length);
+                                               length = defaultlength;
                                        }
-                                       else if (!atoi(parameters[1].c_str()))
+                                       else if (!InspIRCd::IsValidDuration(parameters[1]))
                                        {
-                                               length = 0;
+                                               user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is not a valid DCCALLOW duration", parameters[1].c_str()));
+                                               return CMD_FAILURE;
                                        }
                                        else
                                        {
-                                               length = ServerInstance->Duration(parameters[1]);
+                                               if (!InspIRCd::Duration(parameters[1], length))
+                                               {
+                                                       user->WriteNotice("*** Invalid duration for DCC allow");
+                                                       return CMD_FAILURE;
+                                               }
                                        }
 
-                                       if (!ServerInstance->IsValidMask(mask))
+                                       if (!InspIRCd::IsValidMask(mask))
                                        {
                                                return CMD_FAILURE;
                                        }
@@ -182,11 +235,11 @@ class CommandDccallow : public Command
 
                                        if (length > 0)
                                        {
-                                               user->WriteNumeric(993, "%s %s :Added %s to DCCALLOW list for %ld seconds", user->nick.c_str(), user->nick.c_str(), target->nick.c_str(), length);
+                                               user->WriteNumeric(RPL_DCCALLOWTIMED, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %s", target->nick.c_str(), InspIRCd::DurationString(length).c_str()));
                                        }
                                        else
                                        {
-                                               user->WriteNumeric(994, "%s %s :Added %s to DCCALLOW list for this session", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
+                                               user->WriteNumeric(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
                                        }
 
                                        /* route it. */
@@ -196,40 +249,24 @@ class CommandDccallow : public Command
                        else
                        {
                                // nick doesn't exist
-                               user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), nick.c_str());
+                               user->WriteNumeric(Numerics::NoSuchNick(nick));
                                return CMD_FAILURE;
                        }
                }
                return CMD_FAILURE;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_BROADCAST;
        }
 
        void DisplayHelp(User* user)
        {
-               user->WriteNumeric(998, "%s :DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]", user->nick.c_str());
-               user->WriteNumeric(998, "%s :You may allow DCCs from specific users by specifying a", user->nick.c_str());
-               user->WriteNumeric(998, "%s :DCC allow for the user you want to receive DCCs from.", user->nick.c_str());
-               user->WriteNumeric(998, "%s :For example, to allow the user Brain to send you inspircd.exe", user->nick.c_str());
-               user->WriteNumeric(998, "%s :you would type:", user->nick.c_str());
-               user->WriteNumeric(998, "%s :/DCCALLOW +Brain", user->nick.c_str());
-               user->WriteNumeric(998, "%s :Brain would then be able to send you files. They would have to", user->nick.c_str());
-               user->WriteNumeric(998, "%s :resend the file again if the server gave them an error message", user->nick.c_str());
-               user->WriteNumeric(998, "%s :before you added them to your DCCALLOW list.", user->nick.c_str());
-               user->WriteNumeric(998, "%s :DCCALLOW entries will be temporary by default, if you want to add", user->nick.c_str());
-               user->WriteNumeric(998, "%s :them to your DCCALLOW list until you leave IRC, type:", user->nick.c_str());
-               user->WriteNumeric(998, "%s :/DCCALLOW +Brain 0", user->nick.c_str());
-               user->WriteNumeric(998, "%s :To remove the user from your DCCALLOW list, type:", user->nick.c_str());
-               user->WriteNumeric(998, "%s :/DCCALLOW -Brain", user->nick.c_str());
-               user->WriteNumeric(998, "%s :To see the users in your DCCALLOW list, type:", user->nick.c_str());
-               user->WriteNumeric(998, "%s :/DCCALLOW LIST", user->nick.c_str());
-               user->WriteNumeric(998, "%s :NOTE: If the user leaves IRC or changes their nickname", user->nick.c_str());
-               user->WriteNumeric(998, "%s :  they will be removed from your DCCALLOW list.", user->nick.c_str());
-               user->WriteNumeric(998, "%s :  your DCCALLOW list will be deleted when you leave IRC.", user->nick.c_str());
-               user->WriteNumeric(999, "%s :End of DCCALLOW HELP", user->nick.c_str());
+               user->WriteNumeric(RPL_HELPSTART, "*", "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
+               for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
+                       user->WriteNumeric(RPL_HELPTXT, "*", helptext[i]);
+               user->WriteNumeric(RPL_ENDOFHELP, "*", "End of DCCALLOW HELP");
 
                LocalUser* localuser = IS_LOCAL(user);
                if (localuser)
@@ -239,100 +276,78 @@ class CommandDccallow : public Command
        void DisplayDCCAllowList(User* user)
        {
                 // display current DCCALLOW list
-               user->WriteNumeric(990, "%s :Users on your DCCALLOW list:", user->nick.c_str());
+               user->WriteNumeric(RPL_DCCALLOWSTART, "Users on your DCCALLOW list:");
 
-               dl = ext->get(user);
+               dl = ext.get(user);
                if (dl)
                {
                        for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
                        {
-                               user->WriteNumeric(991, "%s %s :%s (%s)", user->nick.c_str(), user->nick.c_str(), c->nickname.c_str(), c->hostmask.c_str());
+                               user->WriteNumeric(RPL_DCCALLOWLIST, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
                        }
                }
 
-               user->WriteNumeric(992, "%s :End of DCCALLOW list", user->nick.c_str());
+               user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
        }
 
 };
 
 class ModuleDCCAllow : public Module
 {
+       DCCAllowExt ext;
        CommandDccallow cmd;
- public:
+       bool blockchat;
+       std::string defaultaction;
 
+ public:
        ModuleDCCAllow()
-               : cmd(this)
+               : ext("dccallow", ExtensionItem::EXT_USER, this)
+               , cmd(this, ext)
+               , blockchat(false)
        {
-               ext = NULL;
        }
 
-       void init()
+       void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
        {
-               ext = new SimpleExtItem<dccallowlist>("dccallow", this);
-               ServerInstance->Modules->AddService(*ext);
-               ServerInstance->Modules->AddService(cmd);
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserQuit, I_OnUserPostNick, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual void OnRehash(User* user)
-       {
-               ReadFileConf();
-               ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
-               cmd.maxentries = tag->getInt("maxentries", 20);
-       }
-
-       virtual void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
-       {
-               dccallowlist* udl = ext->get(user);
+               dccallowlist* udl = ext.get(user);
 
                // 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
                RemoveNick(user);
        }
 
-       virtual void OnUserPostNick(User* user, const std::string &oldnick)
+       void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
        {
                RemoveNick(user);
        }
 
-       virtual ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               return OnUserPreNotice(user, dest, target_type, text, status, exempt_list);
-       }
-
-       virtual ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return MOD_RES_PASSTHRU;
 
-               if (target_type == TYPE_USER)
+               if (target.type == MessageTarget::TYPE_USER)
                {
-                       User* u = (User*)dest;
+                       User* u = target.Get<User>();
 
                        /* Always allow a user to dcc themselves (although... why?) */
                        if (user == u)
                                return MOD_RES_PASSTHRU;
 
-                       if ((text.length()) && (text[0] == '\1'))
+                       if ((details.text.length()) && (details.text[0] == '\1'))
                        {
                                Expire();
 
                                // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
                                // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
 
-                               if (strncmp(text.c_str(), "\1DCC ", 5) == 0)
+                               if (strncmp(details.text.c_str(), "\1DCC ", 5) == 0)
                                {
-                                       dl = ext->get(u);
+                                       dl = ext.get(u);
                                        if (dl && dl->size())
                                        {
                                                for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
@@ -340,17 +355,14 @@ class ModuleDCCAllow : public Module
                                                                return MOD_RES_PASSTHRU;
                                        }
 
-                                       std::string buf = text.substr(5);
+                                       std::string buf = details.text.substr(5);
                                        size_t s = buf.find(' ');
                                        if (s == std::string::npos)
                                                return MOD_RES_PASSTHRU;
 
-                                       irc::string type = assign(buf.substr(0, s));
-
-                                       ConfigTag* conftag = ServerInstance->Config->ConfValue("dccallow");
-                                       bool blockchat = conftag->getBool("blockchat");
+                                       const std::string type = buf.substr(0, s);
 
-                                       if (type == "SEND")
+                                       if (stdalgo::string::equalsci(type, "SEND"))
                                        {
                                                size_t first;
 
@@ -375,7 +387,6 @@ class ModuleDCCAllow : public Module
                                                if (s == std::string::npos)
                                                        return MOD_RES_PASSTHRU;
 
-                                               std::string defaultaction = conftag->getString("action");
                                                std::string filename = buf.substr(first, s);
 
                                                bool found = false;
@@ -384,7 +395,7 @@ class ModuleDCCAllow : public Module
                                                        if (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
                                                        {
                                                                /* We have a matching badfile entry, override whatever the default action is */
-                                                               if (bfl[i].action == "allow")
+                                                               if (stdalgo::string::equalsci(bfl[i].action, "allow"))
                                                                        return MOD_RES_PASSTHRU;
                                                                else
                                                                {
@@ -398,16 +409,16 @@ class ModuleDCCAllow : public Module
                                                if ((!found) && (defaultaction == "allow"))
                                                        return MOD_RES_PASSTHRU;
 
-                                               user->WriteServ("NOTICE %s :The user %s is not accepting DCC SENDs from you. Your file %s was not sent.", user->nick.c_str(), u->nick.c_str(), filename.c_str());
-                                               u->WriteServ("NOTICE %s :%s (%s@%s) attempted to send you a file named %s, which was blocked.", u->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), filename.c_str());
-                                               u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick.c_str(), user->nick.c_str());
+                                               user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
+                                               u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to send you a file named " + filename + ", which was blocked.");
+                                               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->WriteServ("NOTICE %s :The user %s is not accepting DCC CHAT requests from you.", user->nick.c_str(), u->nick.c_str());
-                                               u->WriteServ("NOTICE %s :%s (%s@%s) attempted to initiate a DCC CHAT session, which was blocked.", u->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str());
-                                               u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick.c_str(), user->nick.c_str());
+                                               user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
+                                               u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to initiate a DCC CHAT session, which was blocked.");
+                                               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;
                                        }
                                }
@@ -421,7 +432,7 @@ class ModuleDCCAllow : public Module
                for (userlist::iterator iter = ul.begin(); iter != ul.end();)
                {
                        User* u = (User*)(*iter);
-                       dl = ext->get(u);
+                       dl = ext.get(u);
                        if (dl)
                        {
                                if (dl->size())
@@ -429,9 +440,10 @@ class ModuleDCCAllow : public Module
                                        dccallowlist::iterator iter2 = dl->begin();
                                        while (iter2 != dl->end())
                                        {
-                                               if (iter2->length != 0 && (iter2->set_on + iter2->length) <= ServerInstance->Time())
+                                               time_t expires = iter2->set_on + iter2->length;
+                                               if (iter2->length != 0 && expires <= ServerInstance->Time())
                                                {
-                                                       u->WriteNumeric(997, "%s %s :DCCALLOW entry for %s has expired", u->nick.c_str(), u->nick.c_str(), iter2->nickname.c_str());
+                                                       u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
                                                        iter2 = dl->erase(iter2);
                                                }
                                                else
@@ -455,7 +467,7 @@ class ModuleDCCAllow : public Module
                for (userlist::iterator iter = ul.begin(); iter != ul.end();)
                {
                        User *u = (User*)(*iter);
-                       dl = ext->get(u);
+                       dl = ext.get(u);
                        if (dl)
                        {
                                if (dl->size())
@@ -465,8 +477,8 @@ class ModuleDCCAllow : public Module
                                                if (i->nickname == user->nick)
                                                {
 
-                                                       u->WriteServ("NOTICE %s :%s left the network or changed their nickname and has been removed from your DCCALLOW list", u->nick.c_str(), i->nickname.c_str());
-                                                       u->WriteNumeric(995, "%s %s :Removed %s from your DCCALLOW list", u->nick.c_str(), u->nick.c_str(), i->nickname.c_str());
+                                                       u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
+                                                       u->WriteNumeric(RPL_DCCALLOWREMOVED, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
                                                        dl->erase(i);
                                                        break;
                                                }
@@ -495,27 +507,29 @@ class ModuleDCCAllow : public Module
                }
        }
 
-       void ReadFileConf()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               bfl.clear();
+               bannedfilelist newbfl;
                ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
                for (ConfigIter i = tags.first; i != tags.second; ++i)
                {
                        BannedFileList bf;
                        bf.filemask = i->second->getString("pattern");
                        bf.action = i->second->getString("action");
-                       bfl.push_back(bf);
+                       newbfl.push_back(bf);
                }
-       }
+               bfl.swap(newbfl);
 
-       virtual ~ModuleDCCAllow()
-       {
-               delete ext;
+               ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
+               cmd.maxentries = tag->getUInt("maxentries", 20);
+               cmd.defaultlength = tag->getDuration("length", 0);
+               blockchat = tag->getBool("blockchat");
+               defaultaction = tag->getString("action");
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for the /DCCALLOW command", VF_COMMON | VF_VENDOR);
+               return Version("Provides the DCCALLOW command", VF_COMMON | VF_VENDOR);
        }
 };
 
index 43b24cfae9c6dba39b0f6ce6b0a64c40164c792b..90412c5c1201d67e0256836e542ed0010f6504bf 100644 (file)
@@ -4,6 +4,7 @@
  *   Copyright (C) 2006, 2008 Craig Edwards <craigedwards@brainbox.cc>
  *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
  *   Copyright (C) 2006-2007 Dennis Friis <peavey@inspircd.org>
+ *   Copyright (C) 2012 satmd <satmd@satmd.dyndns.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
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides usermode +d to block channel messages and channel notices */
+// User mode +d - filter out channel messages and channel notices
+class DeafMode : public ModeHandler
+{
+ public:
+       DeafMode(Module* Creator) : ModeHandler(Creator, "deaf", 'd', PARAM_NONE, MODETYPE_USER) { }
 
-/** User mode +d - filter out channel messages and channel notices
- */
-class User_d : public ModeHandler
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
+       {
+               if (adding == dest->IsModeSet(this))
+                       return MODEACTION_DENY;
+
+               if (adding)
+                       dest->WriteNotice("*** You have enabled user mode +d, deaf mode. This mode means you WILL NOT receive any messages from any channels you are in. If you did NOT mean to do this, use /mode " + dest->nick + " -d.");
+
+               dest->SetMode(this, adding);
+               return MODEACTION_ALLOW;
+       }
+};
+
+// User mode +D - filter out user messages and user notices
+class PrivDeafMode : public ModeHandler
 {
  public:
-       User_d(Module* Creator) : ModeHandler(Creator, "deaf", 'd', PARAM_NONE, MODETYPE_USER) { }
+       PrivDeafMode(Module* Creator) : ModeHandler(Creator, "privdeaf", 'D', PARAM_NONE, MODETYPE_USER)
+       {
+               if (!ServerInstance->Config->ConfValue("deaf")->getBool("enableprivdeaf"))
+                       DisableAutoRegister();
+       }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
        {
+               if (adding == dest->IsModeSet(this))
+                       return MODEACTION_DENY;
+
                if (adding)
-               {
-                       if (!dest->IsModeSet('d'))
-                       {
-                               dest->WriteServ("NOTICE %s :*** You have enabled usermode +d, deaf mode. This mode means you WILL NOT receive any messages from any channels you are in. If you did NOT mean to do this, use /mode %s -d.", dest->nick.c_str(), dest->nick.c_str());
-                               dest->SetMode('d',true);
-                               return MODEACTION_ALLOW;
-                       }
-               }
-               else
-               {
-                       if (dest->IsModeSet('d'))
-                       {
-                               dest->SetMode('d',false);
-                               return MODEACTION_ALLOW;
-                       }
-               }
-               return MODEACTION_DENY;
+                       dest->WriteNotice("*** You have enabled user mode +D, private deaf mode. This mode means you WILL NOT receive any messages and notices from any nicks. If you did NOT mean to do this, use /mode " + dest->nick + " -D.");
+
+               dest->SetMode(this, adding);
+               return MODEACTION_ALLOW;
        }
 };
 
 class ModuleDeaf : public Module
 {
-       User_d m1;
-
+       DeafMode deafmode;
+       PrivDeafMode privdeafmode;
        std::string deaf_bypasschars;
        std::string deaf_bypasschars_uline;
+       bool privdeafuline;
 
  public:
        ModuleDeaf()
-               : m1(this)
-       {
-       }
-
-       void init()
+               : deafmode(this)
+               , privdeafmode(this)
        {
-               ServerInstance->Modules->AddService(m1);
-
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("deaf");
                deaf_bypasschars = tag->getString("bypasschars");
                deaf_bypasschars_uline = tag->getString("bypasscharsuline");
+               privdeafuline = tag->getBool("privdeafuline", true);
        }
 
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               if (target_type == TYPE_CHANNEL)
+               if (target.type == MessageTarget::TYPE_CHANNEL)
                {
-                       Channel* chan = (Channel*)dest;
-                       if (chan)
-                               this->BuildDeafList(MSG_NOTICE, chan, user, status, text, exempt_list);
-               }
-
-               return MOD_RES_PASSTHRU;
-       }
-
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               if (target_type == TYPE_CHANNEL)
-               {
-                       Channel* chan = (Channel*)dest;
-                       if (chan)
-                               this->BuildDeafList(MSG_PRIVMSG, chan, user, status, text, exempt_list);
-               }
-
-               return MOD_RES_PASSTHRU;
-       }
-
-       virtual void BuildDeafList(MessageType message_type, Channel* chan, User* sender, char status, const std::string &text, CUList &exempt_list)
-       {
-               const UserMembList *ulist = chan->GetUsers();
-               bool is_a_uline;
-               bool is_bypasschar, is_bypasschar_avail;
-               bool is_bypasschar_uline, is_bypasschar_uline_avail;
+                       Channel* chan = target.Get<Channel>();
+                       bool is_bypasschar = (deaf_bypasschars.find(details.text[0]) != std::string::npos);
+                       bool is_bypasschar_uline = (deaf_bypasschars_uline.find(details.text[0]) != std::string::npos);
+
+                       // If we have no bypasschars_uline in config, and this is a bypasschar (regular)
+                       // Then it is obviously going to get through +d, no exemption list required
+                       if (deaf_bypasschars_uline.empty() && is_bypasschar)
+                               return MOD_RES_PASSTHRU;
+                       // If it matches both bypasschar and bypasschar_uline, it will get through.
+                       if (is_bypasschar && is_bypasschar_uline)
+                               return MOD_RES_PASSTHRU;
 
-               is_bypasschar = is_bypasschar_avail = is_bypasschar_uline = is_bypasschar_uline_avail = 0;
-               if (!deaf_bypasschars.empty())
-               {
-                       is_bypasschar_avail = 1;
-                       if (deaf_bypasschars.find(text[0], 0) != std::string::npos)
-                               is_bypasschar = 1;
+                       const Channel::MemberMap& ulist = chan->GetUsers();
+                       for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
+                       {
+                               // not +d
+                               if (!i->first->IsModeSet(deafmode))
+                                       continue;
+
+                               bool is_a_uline = i->first->server->IsULine();
+                               // matched a U-line only bypass
+                               if (is_bypasschar_uline && is_a_uline)
+                                       continue;
+                               // matched a regular bypass
+                               if (is_bypasschar && !is_a_uline)
+                                       continue;
+
+                               // don't deliver message!
+                               details.exemptions.insert(i->first);
+                       }
                }
-               if (!deaf_bypasschars_uline.empty())
+               else if (target.type == MessageTarget::TYPE_USER)
                {
-                       is_bypasschar_uline_avail = 1;
-                       if (deaf_bypasschars_uline.find(text[0], 0) != std::string::npos)
-                               is_bypasschar_uline = 1;
-               }
+                       User* targ = target.Get<User>();
+                       if (!targ->IsModeSet(privdeafmode))
+                               return MOD_RES_PASSTHRU;
 
-               /*
-                * 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 (!is_bypasschar_uline_avail && is_bypasschar)
-                       return;
-
-               for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
-               {
-                       /* not +d ? */
-                       if (!i->first->IsModeSet('d'))
-                               continue; /* deliver message */
-                       /* matched both U-line only and regular bypasses */
-                       if (is_bypasschar && is_bypasschar_uline)
-                               continue; /* deliver message */
+                       if (!privdeafuline && user->server->IsULine())
+                               return MOD_RES_DENY;
 
-                       is_a_uline = ServerInstance->ULine(i->first->server);
-                       /* matched a U-line only bypass */
-                       if (is_bypasschar_uline && is_a_uline)
-                               continue; /* deliver message */
-                       /* matched a regular bypass */
-                       if (is_bypasschar && !is_a_uline)
-                               continue; /* deliver message */
-
-                       if (status && !strchr(chan->GetAllPrefixChars(i->first), status))
-                               continue;
-
-                       /* don't deliver message! */
-                       exempt_list.insert(i->first);
+                       if (!user->HasPrivPermission("users/ignore-privdeaf"))
+                               return MOD_RES_DENY;
                }
-       }
 
-       virtual ~ModuleDeaf()
-       {
+               return MOD_RES_PASSTHRU;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides usermode +d to block channel messages and channel notices", VF_VENDOR);
+               return Version("Provides user modes +d and +D to block channel and user messages/notices", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleDeaf)
index 20d4c8e8faeeed905350aac88dfc2eb79c90b511..acfbfce26d8b6a0fcfe6b8c72262383d9a5f1480 100644 (file)
  */
 
 
-/* $ModDesc: Allows for delay-join channels (+D) where users don't appear to join until they speak */
-
 #include "inspircd.h"
-#include <stdarg.h>
+#include "modules/ctctags.h"
+#include "modules/names.h"
 
 class DelayJoinMode : public ModeHandler
 {
  private:
-       CUList empty;
+       LocalIntExt& unjoined;
+
  public:
-       DelayJoinMode(Module* Parent) : ModeHandler(Parent, "delayjoin", 'D', PARAM_NONE, MODETYPE_CHANNEL)
+       DelayJoinMode(Module* Parent, LocalIntExt& ext)
+               : ModeHandler(Parent, "delayjoin", 'D', PARAM_NONE, MODETYPE_CHANNEL)
+               , unjoined(ext)
        {
-               levelrequired = OP_VALUE;
+               ranktoset = ranktounset = OP_VALUE;
        }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE;
+       void RevealUser(User* user, Channel* chan);
 };
 
-class ModuleDelayJoin : public Module
+
+namespace
 {
-       DelayJoinMode djm;
+
+/** Hook handler for join client protocol events.
+ * This allows us to block join protocol events completely, including all associated messages (e.g. MODE, away-notify AWAY).
+ * This is not the same as OnUserJoin() because that runs only when a real join happens but this runs also when a module
+ * such as hostcycle generates a join.
+ */
+class JoinHook : public ClientProtocol::EventHook
+{
+       const LocalIntExt& unjoined;
+
  public:
-       LocalIntExt unjoined;
-       ModuleDelayJoin() : djm(this), unjoined("delayjoin", this)
+       JoinHook(Module* mod, const LocalIntExt& unjoinedref)
+               : ClientProtocol::EventHook(mod, "JOIN", 10)
+               , unjoined(unjoinedref)
        {
        }
 
-       void init()
+       ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(djm);
-               ServerInstance->Modules->AddService(unjoined);
-               Implementation eventlist[] = { I_OnUserJoin, I_OnUserPart, I_OnUserKick, I_OnBuildNeighborList, I_OnNamesListItem, I_OnText, I_OnRawMode };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+               const Membership* const memb = join.GetMember();
+               const User* const u = memb->user;
+               if ((unjoined.get(memb)) && (u != user))
+                       return MOD_RES_DENY;
+               return MOD_RES_PASSTHRU;
        }
-       ~ModuleDelayJoin();
-       Version GetVersion();
-       void OnNamesListItem(User* issuer, Membership*, std::string &prefixes, std::string &nick);
-       void OnUserJoin(Membership*, bool, bool, CUList&);
+};
+
+}
+
+class ModuleDelayJoin 
+       : public Module
+       , public CTCTags::EventListener
+       , public Names::EventListener
+{
+ public:
+       LocalIntExt unjoined;
+       JoinHook joinhook;
+       DelayJoinMode djm;
+
+       ModuleDelayJoin()
+               : CTCTags::EventListener(this)
+               , Names::EventListener(this)
+               , unjoined("delayjoin", ExtensionItem::EXT_MEMBERSHIP, this)
+               , joinhook(this, unjoined)
+               , djm(this, unjoined)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE;
+       ModResult OnNamesListItem(LocalUser* issuer, Membership*, std::string& prefixes, std::string& nick) CXX11_OVERRIDE;
+       void OnUserJoin(Membership*, bool, bool, CUList&) CXX11_OVERRIDE;
        void CleanUser(User* user);
-       void OnUserPart(Membership*, std::string &partmessage, CUList&);
-       void OnUserKick(User* source, Membership*, const std::string &reason, CUList&);
-       void OnBuildNeighborList(User* source, UserChanList &include, std::map<User*,bool> &exception);
-       void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list);
-       ModResult OnRawMode(User* user, Channel* channel, const char mode, const std::string &param, bool adding, int pcnt);
+       void OnUserPart(Membership*, std::string &partmessage, CUList&) CXX11_OVERRIDE;
+       void OnUserKick(User* source, Membership*, const std::string &reason, CUList&) CXX11_OVERRIDE;
+       void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE;
+       void OnUserMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE;
+       void OnUserTagMessage(User* user, const MessageTarget& target, const CTCTags::TagMessageDetails& details) CXX11_OVERRIDE;
+       ModResult OnRawMode(User* user, Channel* channel, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE;
 };
 
 ModeAction DelayJoinMode::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
 {
        /* no change */
-       if (channel->IsModeSet('D') == adding)
+       if (channel->IsModeSet(this) == adding)
                return MODEACTION_DENY;
 
        if (!adding)
@@ -78,38 +117,36 @@ 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)
-                       creator->OnText(n->first, channel, TYPE_CHANNEL, "", 0, empty);
+               const Channel::MemberMap& users = channel->GetUsers();
+               for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
+                       RevealUser(n->first, channel);
        }
-       channel->SetMode('D', adding);
+       channel->SetMode(this, adding);
        return MODEACTION_ALLOW;
 }
 
-ModuleDelayJoin::~ModuleDelayJoin()
-{
-}
-
 Version ModuleDelayJoin::GetVersion()
 {
-       return Version("Allows for delay-join channels (+D) where users don't appear to join until they speak", VF_VENDOR);
+       return Version("Provides channel mode +D, delay-join, users don't appear as joined to others until they speak", VF_VENDOR);
 }
 
-void ModuleDelayJoin::OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+ModResult ModuleDelayJoin::OnNamesListItem(LocalUser* issuer, Membership* memb, std::string& prefixes, std::string& nick)
 {
        /* don't prevent the user from seeing themself */
        if (issuer == memb->user)
-               return;
+               return MOD_RES_PASSTHRU;
 
        /* If the user is hidden by delayed join, hide them from the NAMES list */
        if (unjoined.get(memb))
-               nick.clear();
+               return MOD_RES_DENY;
+
+       return MOD_RES_PASSTHRU;
 }
 
 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;
@@ -119,11 +156,8 @@ static void populate(CUList& except, Membership* memb)
 
 void ModuleDelayJoin::OnUserJoin(Membership* memb, bool sync, bool created, CUList& except)
 {
-       if (memb->chan->IsModeSet('D'))
-       {
+       if (memb->chan->IsModeSet(djm))
                unjoined.set(memb, 1);
-               populate(except, memb);
-       }
 }
 
 void ModuleDelayJoin::OnUserPart(Membership* memb, std::string &partmessage, CUList& except)
@@ -138,53 +172,57 @@ void ModuleDelayJoin::OnUserKick(User* source, Membership* memb, const std::stri
                populate(except, memb);
 }
 
-void ModuleDelayJoin::OnBuildNeighborList(User* source, UserChanList &include, std::map<User*,bool> &exception)
+void ModuleDelayJoin::OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception)
 {
-       UCListIter i = include.begin();
-       while (i != include.end())
+       for (IncludeChanList::iterator i = include.begin(); i != include.end(); )
        {
-               Channel* c = *i++;
-               Membership* memb = c->GetUser(source);
-               if (memb && unjoined.get(memb))
-                       include.erase(c);
+               Membership* memb = *i;
+               if (unjoined.get(memb))
+                       i = include.erase(i);
+               else
+                       ++i;
        }
 }
 
-void ModuleDelayJoin::OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list)
+void ModuleDelayJoin::OnUserTagMessage(User* user, const MessageTarget& target, const CTCTags::TagMessageDetails& details)
 {
-       /* Server origin */
-       if (!user)
+       if (target.type != MessageTarget::TYPE_CHANNEL)
                return;
 
-       if (target_type != TYPE_CHANNEL)
+       Channel* channel = target.Get<Channel>();
+       djm.RevealUser(user, channel);
+}
+
+void ModuleDelayJoin::OnUserMessage(User* user, const MessageTarget& target, const MessageDetails& details)
+{
+       if (target.type != MessageTarget::TYPE_CHANNEL)
                return;
 
-       Channel* channel = static_cast<Channel*>(dest);
+       Channel* channel = target.Get<Channel>();
+       djm.RevealUser(user, channel);
+}
 
-       Membership* memb = channel->GetUser(user);
+void DelayJoinMode::RevealUser(User* user, Channel* chan)
+{
+       Membership* memb = chan->GetUser(user);
        if (!memb || !unjoined.set(memb, 0))
                return;
 
        /* Display the join to everyone else (the user who joined got it earlier) */
-       channel->WriteAllExceptSender(user, false, 0, "JOIN %s", channel->name.c_str());
-
-       std::string ms = memb->modes;
-       for(unsigned int i=0; i < memb->modes.length(); i++)
-               ms.append(" ").append(user->nick);
-
-       if (ms.length() > 0)
-               channel->WriteAllExceptSender(user, false, 0, "MODE %s +%s", channel->name.c_str(), ms.c_str());
+       CUList except_list;
+       except_list.insert(user);
+       ClientProtocol::Events::Join joinevent(memb);
+       chan->Write(joinevent, 0, except_list);
 }
 
 /* make the user visible if he receives any mode change */
-ModResult ModuleDelayJoin::OnRawMode(User* user, Channel* channel, const char mode, const std::string &param, bool adding, int pcnt)
+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;
 
-       ModeHandler* mh = ServerInstance->Modes->FindMode(mode, MODETYPE_CHANNEL);
        // If not a prefix mode then we got nothing to do here
-       if (!mh || !mh->GetPrefixRank())
+       if (!mh->IsPrefixMode())
                return MOD_RES_PASSTHRU;
 
        User* dest;
@@ -196,9 +234,7 @@ ModResult ModuleDelayJoin::OnRawMode(User* user, Channel* channel, const char mo
        if (!dest)
                return MOD_RES_PASSTHRU;
 
-       Membership* memb = channel->GetUser(dest);
-       if (memb && unjoined.set(memb, 0))
-               channel->WriteAllExceptSender(dest, false, 0, "JOIN %s", channel->name.c_str());
+       djm.RevealUser(dest, channel);
        return MOD_RES_PASSTHRU;
 }
 
index 978ab55d2191c94b31bb74862bf454f4f01ce72d..6acaa9a2f2947b7f51f7bacf92813a7120e1faf7 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/ctctags.h"
 
-/* $ModDesc: Provides channelmode +d <int>, to deny messages to a channel until <int> seconds. */
-
-class DelayMsgMode : public ModeHandler
+class DelayMsgMode : public ParamMode<DelayMsgMode, LocalIntExt>
 {
  public:
        LocalIntExt jointime;
-       DelayMsgMode(Module* Parent) : ModeHandler(Parent, "delaymsg", 'd', PARAM_SETONLY, MODETYPE_CHANNEL)
-               , jointime("delaymsg", Parent)
+       DelayMsgMode(Module* Parent)
+               : ParamMode<DelayMsgMode, LocalIntExt>(Parent, "delaymsg", 'd')
+               , jointime("delaymsg", ExtensionItem::EXT_MEMBERSHIP, Parent)
        {
-               levelrequired = OP_VALUE;
+               ranktoset = ranktounset = OP_VALUE;
        }
 
-       bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel*)
+       bool ResolveModeConflict(std::string& their_param, const std::string& our_param, Channel*) CXX11_OVERRIDE
        {
                return (atoi(their_param.c_str()) < atoi(our_param.c_str()));
        }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
+       ModeAction OnSet(User* source, Channel* chan, std::string& parameter) CXX11_OVERRIDE;
+       void OnUnset(User* source, Channel* chan) CXX11_OVERRIDE;
+
+       void SerializeParam(Channel* chan, intptr_t n, std::string& out)
+       {
+               out += ConvToStr(n);
+       }
 };
 
-class ModuleDelayMsg : public Module
+class ModuleDelayMsg
+       : public Module
+       , public CTCTags::EventListener
 {
  private:
        DelayMsgMode djm;
+       bool allownotice;
+       ModResult HandleMessage(User* user, const MessageTarget& target, bool notice);
+
  public:
-       ModuleDelayMsg() : djm(this)
+       ModuleDelayMsg()
+               : CTCTags::EventListener(this)
+               , djm(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(djm);
-               ServerInstance->Modules->AddService(djm.jointime);
-               Implementation eventlist[] = { I_OnUserJoin, I_OnUserPreMessage, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
-       }
-       Version GetVersion();
-       void OnUserJoin(Membership* memb, bool sync, bool created, CUList&);
-       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list);
-       ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list);
-       void OnRehash(User* user);
+       Version GetVersion() CXX11_OVERRIDE;
+       void OnUserJoin(Membership* memb, bool sync, bool created, CUList&) CXX11_OVERRIDE;
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE;
+       ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE;
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
 };
 
-ModeAction DelayMsgMode::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+ModeAction DelayMsgMode::OnSet(User* source, Channel* chan, std::string& parameter)
 {
-       if (adding)
-       {
-               if ((channel->IsModeSet('d')) && (channel->GetModeParameter('d') == parameter))
-                       return MODEACTION_DENY;
-
-               /* Setting a new limit, sanity check */
-               long limit = atoi(parameter.c_str());
+       // Setting a new limit, sanity check
+       intptr_t limit = ConvToNum<intptr_t>(parameter);
+       if (limit <= 0)
+               limit = 1;
 
-               /* Wrap low values at 32768 */
-               if (limit < 0)
-                       limit = 0x7FFF;
-
-               parameter = ConvToStr(limit);
-       }
-       else
-       {
-               if (!channel->IsModeSet('d'))
-                       return MODEACTION_DENY;
-
-               /*
-                * Clean up metadata
-                */
-               const UserMembList* names = channel->GetUsers();
-               for (UserMembCIter n = names->begin(); n != names->end(); ++n)
-                       jointime.set(n->second, 0);
-       }
-       channel->SetModeParam('d', adding ? parameter : "");
+       ext.set(chan, limit);
        return MODEACTION_ALLOW;
 }
 
+void DelayMsgMode::OnUnset(User* source, Channel* chan)
+{
+       /*
+        * Clean up metadata
+        */
+       const Channel::MemberMap& users = chan->GetUsers();
+       for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
+               jointime.set(n->second, 0);
+}
+
 Version ModuleDelayMsg::GetVersion()
 {
-       return Version("Provides channelmode +d <int>, to deny messages to a channel until <int> seconds.", VF_VENDOR);
+       return Version("Provides channel mode +d <int>, to deny messages to a channel until <int> seconds have passed", VF_VENDOR);
 }
 
 void ModuleDelayMsg::OnUserJoin(Membership* memb, bool sync, bool created, CUList&)
 {
-       if ((IS_LOCAL(memb->user)) && (memb->chan->IsModeSet('d')))
+       if ((IS_LOCAL(memb->user)) && (memb->chan->IsModeSet(djm)))
        {
                djm.jointime.set(memb, ServerInstance->Time());
        }
 }
 
-ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
+ModResult ModuleDelayMsg::OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details)
+{
+       return HandleMessage(user, target, details.type == MSG_NOTICE);
+}
+
+ModResult ModuleDelayMsg::OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details)
+{
+       return HandleMessage(user, target, false);
+}
+
+ModResult ModuleDelayMsg::HandleMessage(User* user, const MessageTarget& target, bool notice)
 {
-       /* Server origin */
-       if ((!user) || (!IS_LOCAL(user)))
+       if (!IS_LOCAL(user))
                return MOD_RES_PASSTHRU;
 
-       if (target_type != TYPE_CHANNEL)
+       if ((target.type != MessageTarget::TYPE_CHANNEL) || ((!allownotice) && (notice)))
                return MOD_RES_PASSTHRU;
 
-       Channel* channel = (Channel*) dest;
+       Channel* channel = target.Get<Channel>();
        Membership* memb = channel->GetUser(user);
 
        if (!memb)
@@ -128,14 +131,13 @@ ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_ty
        if (ts == 0)
                return MOD_RES_PASSTHRU;
 
-       std::string len = channel->GetModeParameter('d');
+       int len = djm.ext.get(channel);
 
-       if (ts + atoi(len.c_str()) > ServerInstance->Time())
+       if ((ts + len) > ServerInstance->Time())
        {
                if (channel->GetPrefixValue(user) < VOICE_VALUE)
                {
-                       user->WriteNumeric(404, "%s %s :You must wait %s seconds after joining to send to channel (+d)",
-                               user->nick.c_str(), channel->name.c_str(), len.c_str());
+                       user->WriteNumeric(ERR_CANNOTSENDTOCHAN, channel->name, InspIRCd::Format("You must wait %d seconds after joining to send to the channel (+d is set)", len));
                        return MOD_RES_DENY;
                }
        }
@@ -147,19 +149,10 @@ ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_ty
        return MOD_RES_PASSTHRU;
 }
 
-ModResult ModuleDelayMsg::OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list)
-{
-       return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
-}
-
-void ModuleDelayMsg::OnRehash(User* user)
+void ModuleDelayMsg::ReadConfig(ConfigStatus& status)
 {
        ConfigTag* tag = ServerInstance->Config->ConfValue("delaymsg");
-       if (tag->getBool("allownotice", true))
-               ServerInstance->Modules->Detach(I_OnUserPreNotice, this);
-       else
-               ServerInstance->Modules->Attach(I_OnUserPreNotice, this);
+       allownotice = tag->getBool("allownotice", true);
 }
 
 MODULE_INIT(ModuleDelayMsg)
-
index 39d9e0d34024103fc7caad894f8d295a59a592e7..cc4172529c53e18ca29908fdbc86911858065344 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *.  Copyright (C) 2018 Peter Powell <petpow@saberuk.com>
  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
  *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements config tags which allow blocking of joins to channels */
+enum
+{
+       // InspIRCd-specific.
+       ERR_BADCHANNEL = 926
+};
+
+struct BadChannel
+{
+       bool allowopers;
+       std::string name;
+       std::string reason;
+       std::string redirect;
+
+       BadChannel(const std::string& Name, const std::string& Redirect, const std::string& Reason, bool AllowOpers)
+               : allowopers(AllowOpers)
+               , name(Name)
+               , reason(Reason)
+               , redirect(Redirect)
+       {
+       }
+};
+
+typedef std::vector<BadChannel> BadChannels;
+typedef std::vector<std::string> GoodChannels;
 
 class ModuleDenyChannels : public Module
 {
+ private:
+       BadChannels badchannels;
+       GoodChannels goodchannels;
+       UserModeReference antiredirectmode;
+       ChanModeReference redirectmode;
+
  public:
-       void init()
+       ModuleDenyChannels()
+               : antiredirectmode(this, "antiredirect")
+               , redirectmode(this, "redirect")
        {
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               /* check for redirect validity and loops/chains */
-               ConfigTagList tags = ServerInstance->Config->ConfTags("badchan");
+               GoodChannels goodchans;
+               ConfigTagList tags = ServerInstance->Config->ConfTags("goodchan");
+               for (ConfigIter iter = tags.first; iter != tags.second; ++iter)
+               {
+                       ConfigTag* tag = iter->second;
+
+                       // Ensure that we have the <goodchan:name> parameter.
+                       const std::string name = tag->getString("name");
+                       if (name.empty())
+                               throw ModuleException("<goodchan:name> is a mandatory field, at " + tag->getTagLocation());
+
+                       goodchans.push_back(name);
+               }
+
+               BadChannels badchans;
+               tags = ServerInstance->Config->ConfTags("badchan");
                for (ConfigIter i = tags.first; i != tags.second; ++i)
                {
-                       std::string name = i->second->getString("name");
-                       std::string redirect = i->second->getString("redirect");
+                       ConfigTag* tag = i->second;
+
+                       // Ensure that we have the <badchan:name> parameter.
+                       const std::string name = tag->getString("name");
+                       if (name.empty())
+                               throw ModuleException("<badchan:name> is a mandatory field, at " + tag->getTagLocation());
+
+                       // Ensure that we have the <badchan:reason> parameter.
+                       const std::string reason = tag->getString("reason");
+                       if (reason.empty())
+                               throw ModuleException("<badchan:reason> is a mandatory field, at " + tag->getTagLocation());
 
+                       const std::string redirect = tag->getString("redirect");
                        if (!redirect.empty())
                        {
+                               // Ensure that <badchan:redirect> contains a channel name.
+                               if (!ServerInstance->IsChannel(redirect))
+                                       throw ModuleException("<badchan:redirect> is not a valid channel name, at " + tag->getTagLocation());
 
-                               if (!ServerInstance->IsChannel(redirect.c_str(), ServerInstance->Config->Limits.ChanMax))
-                               {
-                                       if (user)
-                                               user->WriteServ("NOTICE %s :Invalid badchan redirect '%s'", user->nick.c_str(), redirect.c_str());
-                                       throw ModuleException("Invalid badchan redirect, not a channel");
-                               }
-
-                               for (ConfigIter j = tags.first; j != tags.second; ++j)
-                               {
-                                       if (InspIRCd::Match(redirect, j->second->getString("name")))
-                                       {
-                                               bool goodchan = false;
-                                               ConfigTagList goodchans = ServerInstance->Config->ConfTags("goodchan");
-                                               for (ConfigIter k = goodchans.first; k != goodchans.second; ++k)
-                                               {
-                                                       if (InspIRCd::Match(redirect, k->second->getString("name")))
-                                                               goodchan = true;
-                                               }
-
-                                               if (!goodchan)
-                                               {
-                                                       /* <badchan:redirect> is a badchan */
-                                                       if (user)
-                                                               user->WriteServ("NOTICE %s :Badchan %s redirects to badchan %s", user->nick.c_str(), name.c_str(), redirect.c_str());
-                                                       throw ModuleException("Badchan redirect loop");
-                                               }
-                                       }
-                               }
+                               // We defer the rest of the validation of the redirect channel until we have
+                               // finished parsing all of the badchans.
                        }
+
+                       badchans.push_back(BadChannel(name, redirect, reason, tag->getBool("allowopers")));
                }
-       }
 
-       virtual ~ModuleDenyChannels()
-       {
+               // Now we have all of the badchan information recorded we can check that all redirect
+               // channels can actually be redirected to.
+               for (BadChannels::const_iterator i = badchans.begin(); i != badchans.end(); ++i)
+               {
+                       const BadChannel& badchan = *i;
+
+                       // If there is no redirect channel we have nothing to do.
+                       if (badchan.redirect.empty())
+                               continue;
+
+                       // If the redirect channel is whitelisted then it is okay.
+                       for (GoodChannels::const_iterator j = goodchans.begin(); j != goodchans.end(); ++j)
+                               if (InspIRCd::Match(badchan.redirect, *j))
+                                       continue;
+
+                       // If the redirect channel is not blacklisted then it is okay.
+                       for (BadChannels::const_iterator j = badchans.begin(); j != badchans.end(); ++j)
+                               if (InspIRCd::Match(badchan.redirect, j->name))
+                                       throw ModuleException("<badchan:redirect> cannot be a blacklisted channel name");
+               }
+
+               // The config file contained no errors so we can apply the new configuration.
+               badchannels.swap(badchans);
+               goodchannels.swap(goodchans);
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Implements config tags which allow blocking of joins to channels", VF_VENDOR);
        }
 
 
-       virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
-               ConfigTagList tags = ServerInstance->Config->ConfTags("badchan");
-               for (ConfigIter j = tags.first; j != tags.second; ++j)
+               for (BadChannels::const_iterator j = badchannels.begin(); j != badchannels.end(); ++j)
                {
-                       if (InspIRCd::Match(cname, j->second->getString("name")))
-                       {
-                               if (IS_OPER(user) && j->second->getBool("allowopers"))
-                               {
+                       const BadChannel& badchan = *j;
+
+                       // If the channel does not match the current entry we have nothing else to do.
+                       if (!InspIRCd::Match(cname, badchan.name))
+                               continue;
+
+                       // If the user is an oper and opers are allowed to enter this blacklisted channel
+                       // then allow the join.
+                       if (user->IsOper() && badchan.allowopers)
+                               return MOD_RES_PASSTHRU;
+
+                       // If the channel matches a whitelist then allow the join.
+                       for (GoodChannels::const_iterator i = goodchannels.begin(); i != goodchannels.end(); ++i)
+                               if (InspIRCd::Match(cname, *i))
                                        return MOD_RES_PASSTHRU;
-                               }
-                               else
-                               {
-                                       std::string reason = j->second->getString("reason");
-                                       std::string redirect = j->second->getString("redirect");
-
-                                       ConfigTagList goodchans = ServerInstance->Config->ConfTags("goodchan");
-                                       for (ConfigIter i = goodchans.first; i != goodchans.second; ++i)
-                                       {
-                                               if (InspIRCd::Match(cname, i->second->getString("name")))
-                                               {
-                                                       return MOD_RES_PASSTHRU;
-                                               }
-                                       }
-
-                                       if (ServerInstance->IsChannel(redirect.c_str(), ServerInstance->Config->Limits.ChanMax))
-                                       {
-                                               /* simple way to avoid potential loops: don't redirect to +L channels */
-                                               Channel *newchan = ServerInstance->FindChan(redirect);
-                                               if ((!newchan) || (!(newchan->IsModeSet('L'))))
-                                               {
-                                                       user->WriteNumeric(926, "%s %s :Channel %s is forbidden, redirecting to %s: %s",user->nick.c_str(),cname,cname,redirect.c_str(), reason.c_str());
-                                                       Channel::JoinUser(user,redirect.c_str(),false,"",false,ServerInstance->Time());
-                                                       return MOD_RES_DENY;
-                                               }
-                                       }
-
-                                       user->WriteNumeric(926, "%s %s :Channel %s is forbidden: %s",user->nick.c_str(),cname,cname,reason.c_str());
-                                       return MOD_RES_DENY;
-                               }
+
+                       // If there is no redirect chan, the user has enabled the antiredirect mode, or
+                       // the target channel redirects elsewhere we just tell the user and deny the join.
+                       Channel* target = NULL;
+                       if (badchan.redirect.empty() || user->IsModeSet(antiredirectmode)
+                               || ((target = ServerInstance->FindChan(badchan.redirect)) && target->IsModeSet(redirectmode)))
+                       {
+                               user->WriteNumeric(ERR_BADCHANNEL, cname, InspIRCd::Format("Channel %s is forbidden: %s",
+                                       cname.c_str(), badchan.reason.c_str()));
+                               return MOD_RES_DENY;
                        }
+
+                       // Redirect the user to the target channel.
+                       user->WriteNumeric(ERR_BADCHANNEL, cname, InspIRCd::Format("Channel %s is forbidden, redirecting to %s: %s",
+                               cname.c_str(), badchan.redirect.c_str(), badchan.reason.c_str()));
+                       Channel::JoinUser(user, badchan.redirect);
+                       return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
        }
diff --git a/src/modules/m_devoice.cpp b/src/modules/m_devoice.cpp
deleted file mode 100644 (file)
index 2b5de2b..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   Copyright (C) 2005, 2007 Robin Burchell <robin+git@viroteck.net>
- *   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/>.
- */
-
-
-/*
- * DEVOICE module for InspIRCd
- *  Syntax: /DEVOICE <#chan>
- */
-
-/* $ModDesc: Provides voiced users with the ability to devoice themselves. */
-
-#include "inspircd.h"
-
-/** Handle /DEVOICE
- */
-class CommandDevoice : public Command
-{
- public:
-       CommandDevoice(Module* Creator) : Command(Creator,"DEVOICE", 1)
-       {
-               syntax = "<channel>";
-               TRANSLATE2(TR_TEXT, TR_END);
-       }
-
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
-       {
-               Channel* c = ServerInstance->FindChan(parameters[0]);
-               if (c && c->HasUser(user))
-               {
-                       std::vector<std::string> modes;
-                       modes.push_back(parameters[0]);
-                       modes.push_back("-v");
-                       modes.push_back(user->nick);
-
-                       ServerInstance->SendGlobalMode(modes, ServerInstance->FakeClient);
-                       return CMD_SUCCESS;
-               }
-
-               return CMD_FAILURE;
-       }
-};
-
-class ModuleDeVoice : public Module
-{
-       CommandDevoice cmd;
- public:
-       ModuleDeVoice() : cmd(this)
-       {
-       }
-
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleDeVoice()
-       {
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides voiced users with the ability to devoice themselves.", VF_VENDOR);
-       }
-};
-
-MODULE_INIT(ModuleDeVoice)
diff --git a/src/modules/m_disable.cpp b/src/modules/m_disable.cpp
new file mode 100644 (file)
index 0000000..97013a2
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2019 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"
+
+enum
+{
+       // From ircu.
+       ERR_DISABLED = 517
+};
+
+// Holds a list of disabled commands.
+typedef std::vector<std::string> CommandList;
+
+// Holds whether modes are disabled or not.
+typedef std::bitset<64> ModeStatus;
+
+class ModuleDisable : public Module
+{
+ private:
+       CommandList commands;
+       ModeStatus chanmodes;
+       bool fakenonexistent;
+       bool notifyopers;
+       ModeStatus usermodes;
+
+       void ReadModes(ConfigTag* tag, const std::string& field, ModeType type, ModeStatus& status)
+       {
+               const std::string modes = tag->getString(field);
+               for (std::string::const_iterator iter = modes.begin(); iter != modes.end(); ++iter)
+               {
+                       const char& chr = *iter;
+
+                       // Check that the character is a valid mode letter.
+                       if (!ModeParser::IsModeChar(chr))
+                               throw ModuleException(InspIRCd::Format("Invalid mode '%c' was specified in <disabled:%s> at %s",
+                                       chr, field.c_str(), tag->getTagLocation().c_str()));
+
+                       // Check that the mode actually exists.
+                       ModeHandler* mh = ServerInstance->Modes->FindMode(chr, type);
+                       if (!chr)
+                               throw ModuleException(InspIRCd::Format("Nonexistent mode '%c' was specified in <disabled:%s> at %s",
+                                       chr, field.c_str(), tag->getTagLocation().c_str()));
+
+                       // Disable the mode.
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "The %c (%s) %s mode has been disabled",
+                               mh->GetModeChar(), mh->name.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user");
+                       status.set(chr - 'A');
+               }
+       }
+
+       void WriteLog(const char* message, ...) CUSTOM_PRINTF(2, 3)
+       {
+               std::string buffer;
+               VAFORMAT(buffer, message, message);
+
+               if (notifyopers)
+                       ServerInstance->SNO->WriteToSnoMask('a', buffer);
+               else
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, buffer);
+       }
+
+ public:
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("disabled");
+
+               // Parse the disabled commands.
+               CommandList newcommands;
+               irc::spacesepstream commandlist(tag->getString("commands"));
+               for (std::string command; commandlist.GetToken(command); )
+               {
+                       // Check that the command actually exists.
+                       Command* handler = ServerInstance->Parser.GetHandler(command);
+                       if (!handler)
+                               throw ModuleException(InspIRCd::Format("Nonexistent command '%s' was specified in <disabled:commands> at %s",
+                                       command.c_str(), tag->getTagLocation().c_str()));
+
+                       // Prevent admins from disabling COMMANDS and MODULES for transparency reasons.
+                       if (handler->name == "COMMANDS" || handler->name == "MODULES")
+                               continue;
+
+                       // Disable the command.
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "The %s command has been disabled", handler->name.c_str());
+                       newcommands.push_back(handler->name);
+               }
+
+               // Parse the disabled channel modes.
+               ModeStatus newchanmodes;
+               ReadModes(tag, "chanmodes", MODETYPE_CHANNEL, newchanmodes);
+
+               // Parse the disabled user modes.
+               ModeStatus newusermodes;
+               ReadModes(tag, "usermodes", MODETYPE_USER, newusermodes);
+
+               // The server config was valid so we can use these now.
+               chanmodes = newchanmodes;
+               usermodes = newusermodes;
+               commands.swap(newcommands);
+
+               // Whether we should fake the non-existence of disabled things.
+               fakenonexistent = tag->getBool("fakenonexistent", tag->getBool("fakenonexistant"));
+
+               // Whether to notify server operators via snomask `a` about the attempted use of disabled commands/modes.
+               notifyopers = tag->getBool("notifyopers");
+       }
+
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
+       {
+               // If a command is unvalidated or the source is not registered we do nothing.
+               if (!validated || user->registered != REG_ALL)
+                       return MOD_RES_PASSTHRU;
+
+               // If the command is not disabled or the user has the servers/use-disabled-commands priv we do nothing.
+               if (!stdalgo::isin(commands, command) || user->HasPrivPermission("servers/use-disabled-commands"))
+                       return MOD_RES_PASSTHRU;
+
+               // The user has tried to execute a disabled command!
+               user->CommandFloodPenalty += 2000;
+               WriteLog("%s was blocked from executing the disabled %s command", user->GetFullRealHost().c_str(), command.c_str());
+
+               if (fakenonexistent)
+               {
+                       // The server administrator has specified that disabled commands should be
+                       // treated as if they do not exist.
+                       user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "Unknown command");
+                       ServerInstance->stats.Unknown++;
+                       return MOD_RES_DENY;
+               }
+
+               // Inform the user that the command they executed has been disabled.
+               user->WriteNumeric(ERR_DISABLED, command, "Command disabled");
+               return MOD_RES_DENY;
+       }
+
+       ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE
+       {
+               // If a mode change is remote or the source is not registered we do nothing.
+               if (!IS_LOCAL(user) || user->registered != REG_ALL)
+                       return MOD_RES_PASSTHRU;
+
+               // If the mode is not disabled or the user has the servers/use-disabled-modes priv we do nothing.
+               const std::bitset<64>& disabled = (mh->GetModeType() == MODETYPE_CHANNEL) ? chanmodes : usermodes;
+               if (!disabled.test(mh->GetModeChar() - 'A') || user->HasPrivPermission("servers/use-disabled-modes"))
+                       return MOD_RES_PASSTHRU;
+
+               // The user has tried to change a disabled mode!
+               const char* what = mh->GetModeType() == MODETYPE_CHANNEL ? "channel" : "user";
+               WriteLog("%s was blocked from executing the disabled %s mode %c (%s)",
+                       user->GetFullRealHost().c_str(), what, mh->GetModeChar(), mh->name.c_str());
+
+               if (fakenonexistent)
+               {
+                       // The server administrator has specified that disabled modes should be
+                       // treated as if they do not exist.
+                       user->WriteNumeric(mh->GetModeType() == MODETYPE_CHANNEL ? ERR_UNKNOWNMODE : ERR_UNKNOWNSNOMASK,
+                               mh->GetModeChar(), "is an unknown mode character");
+                       return MOD_RES_DENY;
+               }
+
+               // Inform the user that the mode they changed has been disabled.
+               user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - %s mode %c (%s) is disabled",
+                       what, mh->GetModeChar(), mh->name.c_str()));
+               return MOD_RES_DENY;
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides support for disabling commands and modes", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleDisable)
index 3dea080cee59dd99474510ae5fe5567f4eda3b44..91777637c152b8a8342d825a03667049056e9471 100644 (file)
@@ -23,8 +23,8 @@
 
 #include "inspircd.h"
 #include "xline.h"
-
-/* $ModDesc: Provides handling of DNS blacklists */
+#include "modules/dns.h"
+#include "modules/stats.h"
 
 /* Class holding data for a single entry */
 class DNSBLConfEntry : public refcountbase
@@ -35,18 +35,17 @@ class DNSBLConfEntry : public refcountbase
                std::string name, ident, host, domain, reason;
                EnumBanaction banaction;
                EnumType type;
-               long duration;
-               int bitmask;
+               unsigned long duration;
+               unsigned int bitmask;
                unsigned char records[256];
                unsigned long stats_hits, stats_misses;
                DNSBLConfEntry(): type(A_BITMASK),duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {}
-               ~DNSBLConfEntry() { }
 };
 
 
-/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
+/** Resolver for CGI:IRC hostnames encoded in ident/real name
  */
-class DNSBLResolver : public Resolver
+class DNSBLResolver : public DNS::Request
 {
        std::string theiruid;
        LocalStringExt& nameExt;
@@ -55,175 +54,186 @@ class DNSBLResolver : public Resolver
 
  public:
 
-       DNSBLResolver(Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, reference<DNSBLConfEntry> conf, bool &cached)
-               : Resolver(hostname, DNS_QUERY_A, cached, me), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf)
+       DNSBLResolver(DNS::Manager *mgr, Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, reference<DNSBLConfEntry> conf)
+               : DNS::Request(mgr, me, hostname, DNS::QUERY_A, true), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf)
        {
        }
 
        /* Note: This may be called multiple times for multiple A record results */
-       virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+       void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE
        {
                /* Check the user still exists */
                LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid);
-               if (them)
+               if (!them)
+                       return;
+
+               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)
                {
-                       int i = countExt.get(them);
-                       if (i)
-                               countExt.set(them, i - 1);
-                       // All replies should be in 127.0.0.0/8
-                       if (result.compare(0, 4, "127.") == 0)
+                       ServerInstance->SNO->WriteGlobalSno('d', "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)
+                       countExt.set(them, i - 1);
+
+               // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
+
+               unsigned int bitmask = 0, record = 0;
+               bool match = false;
+               in_addr resultip;
+
+               inet_pton(AF_INET, ans_record->rdata.c_str(), &resultip);
+
+               switch (ConfEntry->type)
+               {
+                       case DNSBLConfEntry::A_BITMASK:
+                               bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
+                               bitmask &= ConfEntry->bitmask;
+                               match = (bitmask != 0);
+                       break;
+                       case DNSBLConfEntry::A_RECORD:
+                               record = resultip.s_addr >> 24; /* Last octet */
+                               match = (ConfEntry->records[record] == 1);
+                       break;
+               }
+
+               if (match)
+               {
+                       std::string reason = ConfEntry->reason;
+                       std::string::size_type x = reason.find("%ip%");
+                       while (x != std::string::npos)
                        {
-                               unsigned int bitmask = 0, record = 0;
-                               bool match = false;
-                               in_addr resultip;
+                               reason.erase(x, 4);
+                               reason.insert(x, them->GetIPString());
+                               x = reason.find("%ip%");
+                       }
 
-                               inet_aton(result.c_str(), &resultip);
+                       ConfEntry->stats_hits++;
 
-                               switch (ConfEntry->type)
+                       switch (ConfEntry->banaction)
+                       {
+                               case DNSBLConfEntry::I_KILL:
                                {
-                                       case DNSBLConfEntry::A_BITMASK:
-                                               // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
-                                               bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
-                                               bitmask &= ConfEntry->bitmask;
-                                               match = (bitmask != 0);
-                                       break;
-                                       case DNSBLConfEntry::A_RECORD:
-                                               record = resultip.s_addr >> 24; /* Last octet */
-                                               match = (ConfEntry->records[record] == 1);
+                                       ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")");
                                        break;
                                }
-
-                               if (match)
+                               case DNSBLConfEntry::I_MARK:
                                {
-                                       std::string reason = ConfEntry->reason;
-                                       std::string::size_type x = reason.find("%ip%");
-                                       while (x != std::string::npos)
+                                       if (!ConfEntry->ident.empty())
                                        {
-                                               reason.erase(x, 4);
-                                               reason.insert(x, them->GetIPString());
-                                               x = reason.find("%ip%");
+                                               them->WriteNotice("Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
+                                               them->ChangeIdent(ConfEntry->ident);
                                        }
 
-                                       ConfEntry->stats_hits++;
-
-                                       switch (ConfEntry->banaction)
+                                       if (!ConfEntry->host.empty())
                                        {
-                                               case DNSBLConfEntry::I_KILL:
-                                               {
-                                                       ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")");
-                                                       break;
-                                               }
-                                               case DNSBLConfEntry::I_MARK:
-                                               {
-                                                       if (!ConfEntry->ident.empty())
-                                                       {
-                                                               them->WriteServ("304 " + them->nick + " :Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
-                                                               them->ChangeIdent(ConfEntry->ident.c_str());
-                                                       }
-
-                                                       if (!ConfEntry->host.empty())
-                                                       {
-                                                               them->WriteServ("304 " + them->nick + " :Your host has been set to " + ConfEntry->host + " because you matched " + reason);
-                                                               them->ChangeDisplayedHost(ConfEntry->host.c_str());
-                                                       }
-
-                                                       nameExt.set(them, ConfEntry->name);
-                                                       break;
-                                               }
-                                               case DNSBLConfEntry::I_KLINE:
-                                               {
-                                                       KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
-                                                                       "*", them->GetIPString());
-                                                       if (ServerInstance->XLines->AddLine(kl,NULL))
-                                                       {
-                                                               std::string timestr = ServerInstance->TimeString(kl->expiry);
-                                                               ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s",
-                                                                       them->GetIPString(), timestr.c_str(), reason.c_str());
-                                                               ServerInstance->XLines->ApplyLines();
-                                                       }
-                                                       else
-                                                       {
-                                                               delete kl;
-                                                               return;
-                                                       }
-                                                       break;
-                                               }
-                                               case DNSBLConfEntry::I_GLINE:
-                                               {
-                                                       GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
-                                                                       "*", them->GetIPString());
-                                                       if (ServerInstance->XLines->AddLine(gl,NULL))
-                                                       {
-                                                               std::string timestr = ServerInstance->TimeString(gl->expiry);
-                                                               ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s",
-                                                                       them->GetIPString(), timestr.c_str(), reason.c_str());
-                                                               ServerInstance->XLines->ApplyLines();
-                                                       }
-                                                       else
-                                                       {
-                                                               delete gl;
-                                                               return;
-                                                       }
-                                                       break;
-                                               }
-                                               case DNSBLConfEntry::I_ZLINE:
-                                               {
-                                                       ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
-                                                                       them->GetIPString());
-                                                       if (ServerInstance->XLines->AddLine(zl,NULL))
-                                                       {
-                                                               std::string timestr = ServerInstance->TimeString(zl->expiry);
-                                                               ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on *@%s to expire on %s: %s",
-                                                                       them->GetIPString(), timestr.c_str(), reason.c_str());
-                                                               ServerInstance->XLines->ApplyLines();
-                                                       }
-                                                       else
-                                                       {
-                                                               delete zl;
-                                                               return;
-                                                       }
-                                                       break;
-                                               }
-                                               case DNSBLConfEntry::I_UNKNOWN:
-                                               {
-                                                       break;
-                                               }
-                                               break;
+                                               them->WriteNotice("Your host has been set to " + ConfEntry->host + " because you matched " + reason);
+                                               them->ChangeDisplayedHost(ConfEntry->host);
                                        }
 
-                                       ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record);
+                                       nameExt.set(them, ConfEntry->name);
+                                       break;
                                }
-                               else
-                                       ConfEntry->stats_misses++;
-                       }
-                       else
-                       {
-                               if (!result.empty())
-                                       ServerInstance->SNO->WriteGlobalSno('a', "DNSBL: %s returned address outside of acceptable subnet 127.0.0.0/8: %s", ConfEntry->domain.c_str(), result.c_str());
-                               ConfEntry->stats_misses++;
+                               case DNSBLConfEntry::I_KLINE:
+                               {
+                                       KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+                                                       "*", them->GetIPString());
+                                       if (ServerInstance->XLines->AddLine(kl,NULL))
+                                       {
+                                               ServerInstance->SNO->WriteGlobalSno('x', "K-line added due to DNSBL match on *@%s to expire in %s (on %s): %s",
+                                                       them->GetIPString().c_str(), InspIRCd::DurationString(kl->duration).c_str(),
+                                                       InspIRCd::TimeString(kl->expiry).c_str(), reason.c_str());
+                                               ServerInstance->XLines->ApplyLines();
+                                       }
+                                       else
+                                       {
+                                               delete kl;
+                                               return;
+                                       }
+                                       break;
+                               }
+                               case DNSBLConfEntry::I_GLINE:
+                               {
+                                       GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+                                                       "*", them->GetIPString());
+                                       if (ServerInstance->XLines->AddLine(gl,NULL))
+                                       {
+                                               ServerInstance->SNO->WriteGlobalSno('x', "G-line added due to DNSBL match on *@%s to expire in %s (on %s): %s",
+                                                       them->GetIPString().c_str(), InspIRCd::DurationString(gl->duration).c_str(),
+                                                       InspIRCd::TimeString(gl->expiry).c_str(), reason.c_str());
+                                               ServerInstance->XLines->ApplyLines();
+                                       }
+                                       else
+                                       {
+                                               delete gl;
+                                               return;
+                                       }
+                                       break;
+                               }
+                               case DNSBLConfEntry::I_ZLINE:
+                               {
+                                       ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+                                                       them->GetIPString());
+                                       if (ServerInstance->XLines->AddLine(zl,NULL))
+                                       {
+                                               ServerInstance->SNO->WriteGlobalSno('x', "Z-line added due to DNSBL match on %s to expire in %s (on %s): %s",
+                                                       them->GetIPString().c_str(), InspIRCd::DurationString(zl->duration).c_str(),
+                                                       InspIRCd::TimeString(zl->expiry).c_str(), reason.c_str());
+                                               ServerInstance->XLines->ApplyLines();
+                                       }
+                                       else
+                                       {
+                                               delete zl;
+                                               return;
+                                       }
+                                       break;
+                               }
+                               case DNSBLConfEntry::I_UNKNOWN:
+                               default:
+                                       break;
                        }
+
+                       ServerInstance->SNO->WriteGlobalSno('d', "Connecting user %s (%s) detected as being on the '%s' DNS blacklist with result %d",
+                               them->GetFullRealHost().c_str(), them->GetIPString().c_str(), ConfEntry->name.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record);
                }
+               else
+                       ConfEntry->stats_misses++;
        }
 
-       virtual void OnError(ResolverError e, const std::string &errormessage)
+       void OnError(const DNS::Query *q) CXX11_OVERRIDE
        {
                LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid);
-               if (them)
+               if (!them)
+                       return;
+
+               int i = countExt.get(them);
+               if (i)
+                       countExt.set(them, i - 1);
+
+               if (q->error == DNS::ERROR_NO_RECORDS || q->error == DNS::ERROR_DOMAIN_NOT_FOUND)
                {
-                       int i = countExt.get(them);
-                       if (i)
-                               countExt.set(them, i - 1);
+                       ConfEntry->stats_misses++;
+                       return;
                }
-       }
 
-       virtual ~DNSBLResolver()
-       {
+               ServerInstance->SNO->WriteGlobalSno('d', "An error occurred whilst checking whether %s (%s) is on the '%s' DNS blacklist: %s",
+                       them->GetFullRealHost().c_str(), them->GetIPString().c_str(), ConfEntry->name.c_str(), this->manager->GetErrorStr(q->error).c_str());
        }
 };
 
-class ModuleDNSBL : public Module
+typedef std::vector<reference<DNSBLConfEntry> > DNSBLConfList;
+
+class ModuleDNSBL : public Module, public Stats::EventListener
 {
-       std::vector<reference<DNSBLConfEntry> > DNSBLConfEntries;
+       DNSBLConfList DNSBLConfEntries;
+       dynamic_reference<DNS::Manager> DNS;
        LocalStringExt nameExt;
        LocalIntExt countExt;
 
@@ -246,27 +256,29 @@ class ModuleDNSBL : public Module
                return DNSBLConfEntry::I_UNKNOWN;
        }
  public:
-       ModuleDNSBL() : nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
+       ModuleDNSBL()
+               : Stats::EventListener(this)
+               , DNS(this, "DNS")
+               , nameExt("dnsbl_match", ExtensionItem::EXT_USER, this)
+               , countExt("dnsbl_pending", ExtensionItem::EXT_USER, this)
+       {
+       }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
-               ReadConf();
-               ServerInstance->Modules->AddService(nameExt);
-               ServerInstance->Modules->AddService(countExt);
-               Implementation eventlist[] = { I_OnRehash, I_OnSetUserIP, I_OnStats, I_OnSetConnectClass, I_OnCheckReady };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               ServerInstance->SNO->EnableSnomask('d', "DNSBL");
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides handling of DNS blacklists", VF_VENDOR);
        }
 
        /** Fill our conf vector with data
         */
-       void ReadConf()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               DNSBLConfEntries.clear();
+               DNSBLConfList newentries;
 
                ConfigTagList dnsbls = ServerInstance->Config->ConfTags("dnsbl");
                for(ConfigIter i = dnsbls.first; i != dnsbls.second; ++i)
@@ -280,10 +292,10 @@ class ModuleDNSBL : public Module
                        e->reason = tag->getString("reason");
                        e->domain = tag->getString("domain");
 
-                       if (tag->getString("type") == "bitmask")
+                       if (stdalgo::string::equalsci(tag->getString("type"), "bitmask"))
                        {
                                e->type = DNSBLConfEntry::A_BITMASK;
-                               e->bitmask = tag->getInt("bitmask");
+                               e->bitmask = tag->getUInt("bitmask", 0, 0, UINT_MAX);
                        }
                        else
                        {
@@ -296,59 +308,51 @@ class ModuleDNSBL : public Module
                        }
 
                        e->banaction = str2banaction(tag->getString("action"));
-                       e->duration = ServerInstance->Duration(tag->getString("duration", "60"));
+                       e->duration = tag->getDuration("duration", 60, 1);
 
                        /* Use portparser for record replies */
 
                        /* yeah, logic here is a little messy */
                        if ((e->bitmask <= 0) && (DNSBLConfEntry::A_BITMASK == e->type))
                        {
-                               std::string location = tag->getTagLocation();
-                               ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): invalid bitmask", location.c_str());
+                               throw ModuleException("Invalid <dnsbl:bitmask> at " + tag->getTagLocation());
                        }
                        else if (e->name.empty())
                        {
-                               std::string location = tag->getTagLocation();
-                               ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid name", location.c_str());
+                               throw ModuleException("Empty <dnsbl:name> at " + tag->getTagLocation());
                        }
                        else if (e->domain.empty())
                        {
-                               std::string location = tag->getTagLocation();
-                               ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid domain", location.c_str());
+                               throw ModuleException("Empty <dnsbl:domain> at " + tag->getTagLocation());
                        }
                        else if (e->banaction == DNSBLConfEntry::I_UNKNOWN)
                        {
-                               std::string location = tag->getTagLocation();
-                               ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid banaction", location.c_str());
-                       }
-                       else if (e->duration <= 0)
-                       {
-                               std::string location = tag->getTagLocation();
-                               ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid duration", location.c_str());
+                               throw ModuleException("Unknown <dnsbl:action> at " + tag->getTagLocation());
                        }
                        else
                        {
                                if (e->reason.empty())
                                {
                                        std::string location = tag->getTagLocation();
-                                       ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): empty reason, using defaults", location.c_str());
+                                       ServerInstance->SNO->WriteGlobalSno('d', "DNSBL(%s): empty reason, using defaults", location.c_str());
                                        e->reason = "Your IP has been blacklisted.";
                                }
 
                                /* add it, all is ok */
-                               DNSBLConfEntries.push_back(e);
+                               newentries.push_back(e);
                        }
                }
-       }
 
-       void OnRehash(User* user)
-       {
-               ReadConf();
+               DNSBLConfEntries.swap(newentries);
        }
 
-       void OnSetUserIP(LocalUser* user)
+       void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
        {
-               if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET))
+               if ((user->exempt) || !DNS)
+                       return;
+
+               // Clients can't be in a DNSBL if they aren't connected via IPv4 or IPv6.
+               if (user->client_sa.family() != AF_INET && user->client_sa.family() != AF_INET6)
                        return;
 
                if (user->MyClass)
@@ -357,61 +361,86 @@ class ModuleDNSBL : public Module
                                return;
                }
                else
-                       ServerInstance->Logs->Log("m_dnsbl", DEBUG, "User has no connect class in OnSetUserIP");
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User has no connect class in OnSetUserIP");
 
-               unsigned char a, b, c, d;
-               char reversedipbuf[128];
                std::string reversedip;
+               if (user->client_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.family() == AF_INET6)
+               {
+                       const unsigned char* ip = user->client_sa.in6.sin6_addr.s6_addr;
 
-               d = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
-               c = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
-               b = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
-               a = (unsigned char) user->client_sa.in4.sin_addr.s_addr & 0xFF;
+                       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;
 
-               snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a);
-               reversedip = std::string(reversedipbuf);
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Reversed IP %s -> %s", user->GetIPString().c_str(), reversedip.c_str());
 
                countExt.set(user, DNSBLConfEntries.size());
 
                // For each DNSBL, we will run through this lookup
-               unsigned int i = 0;
-               while (i < DNSBLConfEntries.size())
+               for (unsigned i = 0; i < DNSBLConfEntries.size(); ++i)
                {
                        // Fill hostname with a dnsbl style host (d.c.b.a.domain.tld)
                        std::string hostname = reversedip + "." + DNSBLConfEntries[i]->domain;
 
                        /* now we'd need to fire off lookups for `hostname'. */
-                       bool cached;
-                       DNSBLResolver *r = new DNSBLResolver(this, nameExt, countExt, hostname, user, DNSBLConfEntries[i], cached);
-                       ServerInstance->AddResolver(r, cached);
+                       DNSBLResolver *r = new DNSBLResolver(*this->DNS, this, nameExt, countExt, hostname, user, DNSBLConfEntries[i]);
+                       try
+                       {
+                               this->DNS->Process(r);
+                       }
+                       catch (DNS::Exception &ex)
+                       {
+                               delete r;
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, ex.GetReason());
+                       }
+
                        if (user->quitting)
                                break;
-                       i++;
                }
        }
 
-       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
        {
                std::string dnsbl;
                if (!myclass->config->readString("dnsbl", dnsbl))
                        return MOD_RES_PASSTHRU;
+
                std::string* match = nameExt.get(user);
-               std::string myname = match ? *match : "";
-               if (dnsbl == myname)
+               if (!match)
+                       return MOD_RES_PASSTHRU;
+
+               if (InspIRCd::Match(*match, dnsbl))
                        return MOD_RES_PASSTHRU;
+
                return MOD_RES_DENY;
        }
-       
-       ModResult OnCheckReady(LocalUser *user)
+
+       ModResult OnCheckReady(LocalUser *user) CXX11_OVERRIDE
        {
                if (countExt.get(user))
                        return MOD_RES_DENY;
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnStats(char symbol, User* user, string_list &results)
+       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;
@@ -421,12 +450,12 @@ class ModuleDNSBL : public Module
                        total_hits += (*i)->stats_hits;
                        total_misses += (*i)->stats_misses;
 
-                       results.push_back(ServerInstance->Config->ServerName + " 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(ServerInstance->Config->ServerName + " 304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits));
-               results.push_back(ServerInstance->Config->ServerName + " 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;
        }
index 8d6d65af2329c050dff0fa4e6c319909da0731b7..61b20fc9bf8740d07970a870c639039b3e691f62 100644 (file)
 
 
 #include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides the ability to allow channel operators to be exempt from certain modes. */
+#include "listmode.h"
+#include "modules/exemption.h"
 
 /** Handles channel mode +X
  */
 class ExemptChanOps : public ListModeBase
 {
  public:
-       ExemptChanOps(Module* Creator) : ListModeBase(Creator, "exemptchanops", 'X', "End of channel exemptchanops list", 954, 953, false, "exemptchanops") { }
+       ExemptChanOps(Module* Creator)
+               : ListModeBase(Creator, "exemptchanops", 'X', "End of channel exemptchanops list", 954, 953, false)
+       {
+       }
 
-       bool ValidateParam(User* user, Channel* chan, std::string &word)
+       bool ValidateParam(User* user, Channel* chan, std::string& word) CXX11_OVERRIDE
        {
-               // TODO actually make sure there's a prop for this
-               if ((word.length() > 35) || (word.empty()))
+               std::string::size_type p = word.find(':');
+               if (p == std::string::npos)
                {
-                       user->WriteNumeric(955, "%s %s %s :word is too %s for exemptchanops list",user->nick.c_str(), chan->name.c_str(), word.c_str(), (word.empty() ? "short" : "long"));
+                       user->WriteNumeric(Numerics::InvalidModeParameter(chan, this, word, "Invalid exemptchanops entry, format is <restriction>:<prefix>"));
                        return false;
                }
 
-               return true;
-       }
-
-       bool TellListTooLong(User* user, Channel* chan, std::string &word)
-       {
-               user->WriteNumeric(959, "%s %s %s :Channel exemptchanops list is full", user->nick.c_str(), chan->name.c_str(), word.c_str());
-               return true;
-       }
+               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);
 
-       void TellAlreadyOnList(User* user, Channel* chan, std::string &word)
-       {
-               user->WriteNumeric(957, "%s %s :The word %s is already on the exemptchanops list",user->nick.c_str(), chan->name.c_str(), word.c_str());
-       }
+               if (!ServerInstance->Modes->FindMode(restriction, MODETYPE_CHANNEL))
+               {
+                       user->WriteNumeric(Numerics::InvalidModeParameter(chan, this, word, "Unknown restriction"));
+                       return false;
+               }
 
-       void TellNotSet(User* user, Channel* chan, std::string &word)
-       {
-               user->WriteNumeric(958, "%s %s :No such exemptchanops word is set",user->nick.c_str(), chan->name.c_str());
+               return true;
        }
 };
 
-class ExemptHandler : public HandlerBase3<ModResult, User*, Channel*, const std::string&>
+class ExemptHandler : public CheckExemption::EventListener
 {
  public:
        ExemptChanOps ec;
-       ExemptHandler(Module* me) : ec(me) {}
-       
-       ModeHandler* FindMode(const std::string& mid)
+       ExemptHandler(Module* me)
+               : CheckExemption::EventListener(me)
+               , ec(me)
+       {
+       }
+
+       PrefixMode* FindMode(const std::string& mid)
        {
                if (mid.length() == 1)
-                       return ServerInstance->Modes->FindMode(mid[0], MODETYPE_CHANNEL);
-               for(char c='A'; c <= 'z'; c++)
-               {
-                       ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
-                       if (mh && mh->name == mid)
-                               return mh;
-               }
-               return NULL;
+                       return ServerInstance->Modes->FindPrefixMode(mid[0]);
+
+               ModeHandler* mh = ServerInstance->Modes->FindMode(mid, MODETYPE_CHANNEL);
+               return mh ? mh->IsPrefixMode() : NULL;
        }
 
-       ModResult Call(User* user, Channel* chan, const std::string& restriction)
+       ModResult OnCheckExemption(User* user, Channel* chan, const std::string& restriction) CXX11_OVERRIDE
        {
                unsigned int mypfx = chan->GetPrefixValue(user);
                std::string minmode;
 
-               modelist* list = ec.extItem.get(chan);
+               ListModeBase::ModeList* list = ec.GetList(chan);
 
                if (list)
                {
-                       for (modelist::iterator i = list->begin(); i != list->end(); ++i)
+                       for (ListModeBase::ModeList::iterator i = list->begin(); i != list->end(); ++i)
                        {
                                std::string::size_type pos = (*i).mask.find(':');
                                if (pos == std::string::npos)
                                        continue;
-                               if ((*i).mask.substr(0,pos) == restriction)
-                                       minmode = (*i).mask.substr(pos + 1);
+                               if (!i->mask.compare(0, pos, restriction))
+                                       minmode.assign(i->mask, pos + 1, std::string::npos);
                        }
                }
 
-               ModeHandler* mh = FindMode(minmode);
+               PrefixMode* mh = FindMode(minmode);
                if (mh && mypfx >= mh->GetPrefixRank())
                        return MOD_RES_ALLOW;
                if (mh || minmode == "*")
                        return MOD_RES_DENY;
 
-               return ServerInstance->HandleOnCheckExemption.Call(user, chan, restriction);
+               return MOD_RES_PASSTHRU;
        }
 };
 
 class ModuleExemptChanOps : public Module
 {
-       std::string defaults;
        ExemptHandler eh;
 
  public:
-
        ModuleExemptChanOps() : eh(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(eh.ec);
-               Implementation eventlist[] = { I_OnRehash, I_OnSyncChannel };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               ServerInstance->OnCheckExemption = &eh;
-
-               OnRehash(NULL);
-       }
-
-       ~ModuleExemptChanOps()
-       {
-               ServerInstance->OnCheckExemption = &ServerInstance->HandleOnCheckExemption;
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides the ability to allow channel operators to be exempt from certain modes.",VF_VENDOR);
+               return Version("Provides the ability to allow channel operators to be exempt from certain modes", VF_VENDOR);
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                eh.ec.DoRehash();
        }
-
-       void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
-       {
-               eh.ec.DoSyncChannel(chan, proto, opaque);
-       }
 };
 
 MODULE_INIT(ModuleExemptChanOps)
index 4090f5600b4d1a9381cfd0b67c6a9106a1b7b8fd..b2febf810a5ea65a54d80c7cde92fe6d57ca41f5 100644 (file)
 
 #include "inspircd.h"
 #include "xline.h"
-#include "m_regex.h"
-
-/* $ModDesc: Text (spam) filtering */
-
-class ModuleFilter;
+#include "modules/regex.h"
+#include "modules/server.h"
+#include "modules/shun.h"
+#include "modules/stats.h"
 
 enum FilterFlags
 {
@@ -39,19 +38,24 @@ enum FilterFlags
 enum FilterAction
 {
        FA_GLINE,
+       FA_ZLINE,
+       FA_WARN,
        FA_BLOCK,
        FA_SILENT,
        FA_KILL,
+       FA_SHUN,
        FA_NONE
 };
 
 class FilterResult
 {
  public:
+       Regex* regex;
        std::string freeform;
        std::string reason;
        FilterAction action;
-       long gline_time;
+       unsigned long duration;
+       bool from_config;
 
        bool flag_no_opers;
        bool flag_part_message;
@@ -60,9 +64,16 @@ class FilterResult
        bool flag_notice;
        bool flag_strip_color;
 
-       FilterResult(const std::string& free, const std::string& rea, FilterAction act, long gt, const std::string& fla) :
-                       freeform(free), reason(rea), action(act), gline_time(gt)
+       FilterResult(dynamic_reference<RegexFactory>& RegexEngine, const std::string& free, const std::string& rea, FilterAction act, unsigned long gt, const std::string& fla, bool cfg)
+               : freeform(free)
+               , reason(rea)
+               , action(act)
+               , duration(gt)
+               , from_config(cfg)
        {
+               if (!RegexEngine)
+                       throw ModuleException("Regex module implementing '"+RegexEngine.GetProvider()+"' is not loaded!");
+               regex = RegexEngine->Create(free);
                this->FillFlags(fla);
        }
 
@@ -144,28 +155,22 @@ class CommandFilter : public Command
                : Command(f, "FILTER", 1, 5)
        {
                flags_needed = 'o';
-               this->syntax = "<filter-definition> <action> <flags> [<gline-duration>] :<reason>";
+               this->syntax = "<pattern> [<action> <flags> [<duration>] :<reason>]";
        }
-       CmdResult Handle(const std::vector<std::string>&, User*);
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_BROADCAST;
        }
 };
 
-class ImplFilter : public FilterResult
+class ModuleFilter : public Module, public ServerEventListener, public Stats::EventListener
 {
- public:
-       Regex* regex;
+       typedef insp::flat_set<std::string, irc::insensitive_swo> ExemptTargetSet;
 
-       ImplFilter(ModuleFilter* mymodule, const std::string &rea, FilterAction act, long glinetime, const std::string &pat, const std::string &flgs);
-};
-
-
-class ModuleFilter : public Module
-{
        bool initing;
+       bool notifyuser;
        RegexFactory* factory;
        void FreeFilters();
 
@@ -173,49 +178,55 @@ class ModuleFilter : public Module
        CommandFilter filtcommand;
        dynamic_reference<RegexFactory> RegexEngine;
 
-       std::vector<ImplFilter> filters;
+       std::vector<FilterResult> filters;
        int flags;
 
-       std::set<std::string> exemptfromfilter; // List of channel names excluded from filtering.
+       // List of channel names excluded from filtering.
+       ExemptTargetSet exemptedchans;
+
+       // List of target nicknames excluded from filtering.
+       ExemptTargetSet exemptednicks;
 
        ModuleFilter();
-       void init();
-       CullResult cull();
-       ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
+       void init() CXX11_OVERRIDE;
+       CullResult cull() CXX11_OVERRIDE;
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE;
        FilterResult* FilterMatch(User* user, const std::string &text, int flags);
-       bool DeleteFilter(const std::string &freeform);
-       std::pair<bool, std::string> AddFilter(const std::string &freeform, FilterAction type, const std::string &reason, long duration, const std::string &flags);
-       ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
-       void OnRehash(User* user);
-       Version GetVersion();
+       bool DeleteFilter(const std::string& freeform, std::string& reason);
+       std::pair<bool, std::string> AddFilter(const std::string& freeform, FilterAction type, const std::string& reason, unsigned long duration, const std::string& flags, bool config = false);
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
+       Version GetVersion() CXX11_OVERRIDE;
        std::string EncodeFilter(FilterResult* filter);
        FilterResult DecodeFilter(const std::string &data);
-       void OnSyncNetwork(Module* proto, void* opaque);
-       void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata);
-       ModResult OnStats(char symbol, User* user, string_list &results);
-       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line);
-       void OnUnloadModule(Module* mod);
+       void OnSyncNetwork(ProtocolInterface::Server& server) CXX11_OVERRIDE;
+       void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata) CXX11_OVERRIDE;
+       ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE;
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE;
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
        bool AppliesToMe(User* user, FilterResult* filter, int flags);
        void ReadFilters();
        static bool StringToFilterAction(const std::string& str, FilterAction& fa);
        static std::string FilterActionToString(FilterAction fa);
 };
 
-CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User *user)
+CmdResult CommandFilter::Handle(User* user, const Params& parameters)
 {
        if (parameters.size() == 1)
        {
                /* Deleting a filter */
-               Module *me = creator;
-               if (static_cast<ModuleFilter *>(me)->DeleteFilter(parameters[0]))
+               Module* me = creator;
+               std::string reason;
+
+               if (static_cast<ModuleFilter*>(me)->DeleteFilter(parameters[0], reason))
                {
-                       user->WriteServ("NOTICE %s :*** Removed filter '%s'", user->nick.c_str(), parameters[0].c_str());
-                       ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'a' : 'A', "FILTER: "+user->nick+" removed filter '"+parameters[0]+"'");
+                       user->WriteNotice("*** Removed filter '" + parameters[0] + "': " + reason);
+                       ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'f' : 'F', "%s removed filter '%s': %s",
+                               user->nick.c_str(), parameters[0].c_str(), reason.c_str());
                        return CMD_SUCCESS;
                }
                else
                {
-                       user->WriteServ("NOTICE %s :*** Filter '%s' not found in list, try /stats s.", user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNotice("*** Filter '" + parameters[0] + "' not found on the list.");
                        return CMD_FAILURE;
                }
        }
@@ -228,24 +239,31 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
                        FilterAction type;
                        const std::string& flags = parameters[2];
                        unsigned int reasonindex;
-                       long duration = 0;
+                       unsigned long duration = 0;
 
                        if (!ModuleFilter::StringToFilterAction(parameters[1], type))
                        {
-                               user->WriteServ("NOTICE %s :*** Invalid filter type '%s'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.", user->nick.c_str(), parameters[1].c_str());
+                               if (ServerInstance->XLines->GetFactory("SHUN"))
+                                       user->WriteNotice("*** Invalid filter type '" + parameters[1] + "'. Supported types are 'gline', 'zline', 'none', 'warn', 'block', 'silent', 'kill', and 'shun'.");
+                               else
+                                       user->WriteNotice("*** Invalid filter type '" + parameters[1] + "'. Supported types are 'gline', 'zline', 'none', 'warn', 'block', 'silent', and 'kill'.");
                                return CMD_FAILURE;
                        }
 
-                       if (type == FA_GLINE)
+                       if (type == FA_GLINE || type == FA_ZLINE || type == FA_SHUN)
                        {
                                if (parameters.size() >= 5)
                                {
-                                       duration = ServerInstance->Duration(parameters[3]);
+                                       if (!InspIRCd::Duration(parameters[3], duration))
+                                       {
+                                               user->WriteNotice("*** Invalid duration for filter");
+                                               return CMD_FAILURE;
+                                       }
                                        reasonindex = 4;
                                }
                                else
                                {
-                                       user->WriteServ("NOTICE %s :*** Not enough parameters: When setting a gline type filter, a gline duration must be specified as the third parameter.", user->nick.c_str());
+                                       user->WriteNotice("*** Not enough parameters: When setting a '" + parameters[1] + "' type filter, a duration must be specified as the third parameter.");
                                        return CMD_FAILURE;
                                }
                        }
@@ -254,27 +272,31 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
                                reasonindex = 3;
                        }
 
-                       Module *me = creator;
-                       std::pair<bool, std::string> result = static_cast<ModuleFilter *>(me)->AddFilter(freeform, type, parameters[reasonindex], duration, flags);
+                       Moduleme = creator;
+                       std::pair<bool, std::string> result = static_cast<ModuleFilter*>(me)->AddFilter(freeform, type, parameters[reasonindex], duration, flags);
                        if (result.first)
                        {
-                               user->WriteServ("NOTICE %s :*** Added filter '%s', type '%s'%s%s, flags '%s', reason: '%s'", user->nick.c_str(), freeform.c_str(),
-                                               parameters[1].c_str(), (duration ? ", duration " : ""), (duration ? parameters[3].c_str() : ""),
-                                               flags.c_str(), parameters[reasonindex].c_str());
+                               const std::string message = InspIRCd::Format("'%s', type '%s'%s, flags '%s', reason: %s",
+                                       freeform.c_str(), parameters[1].c_str(),
+                                       (duration ? InspIRCd::Format(", duration '%s'",
+                                               InspIRCd::DurationString(duration).c_str()).c_str()
+                                       : ""), flags.c_str(), parameters[reasonindex].c_str());
 
-                               ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'a' : 'A', "FILTER: "+user->nick+" added filter '"+freeform+"', type '"+parameters[1]+"', "+(duration ? "duration "+parameters[3]+", " : "")+"flags '"+flags+"', reason: "+parameters[reasonindex]);
+                               user->WriteNotice("*** Added filter " + message);
+                               ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'f' : 'F',
+                                       "%s added filter %s", user->nick.c_str(), message.c_str());
 
                                return CMD_SUCCESS;
                        }
                        else
                        {
-                               user->WriteServ("NOTICE %s :*** Filter '%s' could not be added: %s", user->nick.c_str(), freeform.c_str(), result.second.c_str());
+                               user->WriteNotice("*** Filter '" + freeform + "' could not be added: " + result.second);
                                return CMD_FAILURE;
                        }
                }
                else
                {
-                       user->WriteServ("NOTICE %s :*** Not enough parameters.", user->nick.c_str());
+                       user->WriteNotice("*** Not enough parameters.");
                        return CMD_FAILURE;
                }
 
@@ -283,7 +305,7 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> &parameters, User
 
 bool ModuleFilter::AppliesToMe(User* user, FilterResult* filter, int iflags)
 {
-       if ((filter->flag_no_opers) && IS_OPER(user))
+       if ((filter->flag_no_opers) && user->IsOper())
                return false;
        if ((iflags & FLAG_PRIVMSG) && (!filter->flag_privmsg))
                return false;
@@ -297,16 +319,17 @@ bool ModuleFilter::AppliesToMe(User* user, FilterResult* filter, int iflags)
 }
 
 ModuleFilter::ModuleFilter()
-       : initing(true), filtcommand(this), RegexEngine(this, "regex")
+       : ServerEventListener(this)
+       , Stats::EventListener(this)
+       , initing(true)
+       , filtcommand(this)
+       , RegexEngine(this, "regex")
 {
 }
 
 void ModuleFilter::init()
 {
-       ServerInstance->Modules->AddService(filtcommand);
-       Implementation eventlist[] = { I_OnPreCommand, I_OnStats, I_OnSyncNetwork, I_OnDecodeMetaData, I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash, I_OnUnloadModule };
-       ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       OnRehash(NULL);
+       ServerInstance->SNO->EnableSnomask('f', "FILTER");
 }
 
 CullResult ModuleFilter::cull()
@@ -317,69 +340,100 @@ CullResult ModuleFilter::cull()
 
 void ModuleFilter::FreeFilters()
 {
-       for (std::vector<ImplFilter>::const_iterator i = filters.begin(); i != filters.end(); ++i)
+       for (std::vector<FilterResult>::const_iterator i = filters.begin(); i != filters.end(); ++i)
                delete i->regex;
 
        filters.clear();
 }
 
-ModResult ModuleFilter::OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ModResult ModuleFilter::OnUserPreMessage(User* user, const MessageTarget& msgtarget, MessageDetails& details)
 {
+       // Leave remote users and servers alone
        if (!IS_LOCAL(user))
                return MOD_RES_PASSTHRU;
 
-       flags = FLAG_PRIVMSG;
-       return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
-}
-
-ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-{
-       /* Leave ulines alone */
-       if ((ServerInstance->ULine(user->server)) || (!IS_LOCAL(user)))
-               return MOD_RES_PASSTHRU;
-
-       if (!flags)
-               flags = FLAG_NOTICE;
+       flags = (details.type == MSG_PRIVMSG) ? FLAG_PRIVMSG : FLAG_NOTICE;
 
-       FilterResult* f = this->FilterMatch(user, text, flags);
+       FilterResult* f = this->FilterMatch(user, details.text, flags);
        if (f)
        {
                std::string target;
-               if (target_type == TYPE_USER)
+               if (msgtarget.type == MessageTarget::TYPE_USER)
                {
-                       User* t = (User*)dest;
+                       User* t = msgtarget.Get<User>();
+                       // Check if the target nick is exempted, if yes, ignore this message
+                       if (exemptednicks.count(t->nick))
+                               return MOD_RES_PASSTHRU;
+
                        target = t->nick;
                }
-               else if (target_type == TYPE_CHANNEL)
+               else if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
                {
-                       Channel* t = (Channel*)dest;
-                       if (exemptfromfilter.find(t->name) != exemptfromfilter.end())
+                       Channel* t = msgtarget.Get<Channel>();
+                       if (exemptedchans.count(t->name))
                                return MOD_RES_PASSTHRU;
 
                        target = t->name;
                }
+               if (f->action == FA_WARN)
+               {
+                       ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("WARNING: %s's message to %s matched %s (%s)",
+                               user->nick.c_str(), target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+                       return MOD_RES_PASSTHRU;
+               }
                if (f->action == FA_BLOCK)
                {
-                       ServerInstance->SNO->WriteGlobalSno('a', "FILTER: "+user->nick+" had their message filtered, target was "+target+": "+f->reason);
-                       if (target_type == TYPE_CHANNEL)
-                               user->WriteNumeric(404, "%s %s :Message to channel blocked and opers notified (%s)",user->nick.c_str(), target.c_str(), f->reason.c_str());
+                       ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s had their message to %s filtered as it matched %s (%s)",
+                               user->nick.c_str(), target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+                       if (notifyuser)
+                       {
+                               if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
+                                       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
-                               user->WriteServ("NOTICE "+user->nick+" :Your message to "+target+" was blocked and opers notified: "+f->reason);
+                               details.echo_original = true;
                }
                else if (f->action == FA_SILENT)
                {
-                       if (target_type == TYPE_CHANNEL)
-                               user->WriteNumeric(404, "%s %s :Message to channel blocked (%s)",user->nick.c_str(), target.c_str(), f->reason.c_str());
+                       if (notifyuser)
+                       {
+                               if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
+                                       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);
+                       }
                        else
-                               user->WriteServ("NOTICE "+user->nick+" :Your message to "+target+" was blocked: "+f->reason);
+                               details.echo_original = true;
                }
                else if (f->action == FA_KILL)
                {
+                       ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s was killed because their message to %s matched %s (%s)",
+                               user->nick.c_str(), target.c_str(), f->freeform.c_str(), f->reason.c_str()));
                        ServerInstance->Users->QuitUser(user, "Filtered: " + f->reason);
                }
+               else if (f->action == FA_SHUN && (ServerInstance->XLines->GetFactory("SHUN")))
+               {
+                       Shun* sh = new Shun(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), user->GetIPString());
+                       ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s (%s) was shunned for %s (expires on %s) because their message to %s matched %s (%s)",
+                               user->nick.c_str(), sh->Displayable().c_str(), InspIRCd::DurationString(f->duration).c_str(),
+                               InspIRCd::TimeString(ServerInstance->Time() + f->duration).c_str(),
+                               target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+                       if (ServerInstance->XLines->AddLine(sh, NULL))
+                       {
+                               ServerInstance->XLines->ApplyLines();
+                       }
+                       else
+                               delete sh;
+               }
                else if (f->action == FA_GLINE)
                {
-                       GLine* gl = new GLine(ServerInstance->Time(), f->gline_time, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+                       GLine* gl = new GLine(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+                       ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s (%s) was G-lined for %s (expires on %s) because their message to %s matched %s (%s)",
+                               user->nick.c_str(), gl->Displayable().c_str(), InspIRCd::DurationString(f->duration).c_str(),
+                               InspIRCd::TimeString(ServerInstance->Time() + f->duration).c_str(),
+                               target.c_str(), f->freeform.c_str(), f->reason.c_str()));
                        if (ServerInstance->XLines->AddLine(gl,NULL))
                        {
                                ServerInstance->XLines->ApplyLines();
@@ -387,16 +441,30 @@ ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, s
                        else
                                delete gl;
                }
+               else if (f->action == FA_ZLINE)
+               {
+                       ZLine* zl = new ZLine(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), user->GetIPString());
+                       ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s (%s) was Z-lined for %s (expires on %s) because their message to %s matched %s (%s)",
+                               user->nick.c_str(), zl->Displayable().c_str(), InspIRCd::DurationString(f->duration).c_str(),
+                               InspIRCd::TimeString(ServerInstance->Time() + f->duration).c_str(),
+                               target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+                       if (ServerInstance->XLines->AddLine(zl,NULL))
+                       {
+                               ServerInstance->XLines->ApplyLines();
+                       }
+                       else
+                               delete zl;
+               }
 
-               ServerInstance->Logs->Log("FILTER",DEFAULT,"FILTER: "+ user->nick + " had their message filtered, target was " + target + ": " + f->reason + " Action: " + ModuleFilter::FilterActionToString(f->action));
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, user->nick + " had their message filtered, target was " + target + ": " + f->reason + " Action: " + ModuleFilter::FilterActionToString(f->action));
                return MOD_RES_DENY;
        }
        return MOD_RES_PASSTHRU;
 }
 
-ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+ModResult ModuleFilter::OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated)
 {
-       if (validated && IS_LOCAL(user))
+       if (validated)
        {
                flags = 0;
                bool parting;
@@ -416,7 +484,7 @@ ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::stri
                        if (parameters.size() < 2)
                                return MOD_RES_PASSTHRU;
 
-                       if (exemptfromfilter.find(parameters[0]) != exemptfromfilter.end())
+                       if (exemptedchans.count(parameters[0]))
                                return MOD_RES_PASSTHRU;
 
                        parting = true;
@@ -434,10 +502,10 @@ ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::stri
                /* We cant block a part or quit, so instead we change the reason to 'Reason filtered' */
                parameters[parting ? 1 : 0] = "Reason filtered";
 
-               /* We're blocking, OR theyre quitting and its a KILL action
+               /* We're warning or blocking, OR theyre quitting and its a KILL action
                 * (we cant kill someone whos already quitting, so filter them anyway)
                 */
-               if ((f->action == FA_BLOCK) || (((!parting) && (f->action == FA_KILL))) || (f->action == FA_SILENT))
+               if ((f->action == FA_WARN) || (f->action == FA_BLOCK) || (((!parting) && (f->action == FA_KILL))) || (f->action == FA_SILENT))
                {
                        return MOD_RES_PASSTHRU;
                }
@@ -446,13 +514,19 @@ ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::stri
                        /* Are they parting, if so, kill is applicable */
                        if ((parting) && (f->action == FA_KILL))
                        {
-                               user->WriteServ("NOTICE %s :*** Your PART message was filtered: %s", user->nick.c_str(), f->reason.c_str());
+                               user->WriteNotice("*** Your PART message was filtered: " + f->reason);
                                ServerInstance->Users->QuitUser(user, "Filtered: " + f->reason);
                        }
                        if (f->action == FA_GLINE)
                        {
-                               /* Note: We gline *@IP so that if their host doesnt resolve the gline still applies. */
-                               GLine* gl = new GLine(ServerInstance->Time(), f->gline_time, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+                               /* Note: We G-line *@IP so that if their host doesn't resolve the G-line still applies. */
+                               GLine* gl = new GLine(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+                               ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s (%s) was G-lined for %s (expires on %s) because their %s message matched %s (%s)",
+                                       user->nick.c_str(), gl->Displayable().c_str(),
+                                       InspIRCd::DurationString(f->duration).c_str(),
+                                       InspIRCd::TimeString(ServerInstance->Time() + f->duration).c_str(),
+                                       command.c_str(), f->freeform.c_str(), f->reason.c_str()));
+
                                if (ServerInstance->XLines->AddLine(gl,NULL))
                                {
                                        ServerInstance->XLines->ApplyLines();
@@ -460,24 +534,69 @@ ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::stri
                                else
                                        delete gl;
                        }
+                       if (f->action == FA_ZLINE)
+                       {
+                               ZLine* zl = new ZLine(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), user->GetIPString());
+                               ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s (%s) was Z-lined for %s (expires on %s) because their %s message matched %s (%s)",
+                                       user->nick.c_str(), zl->Displayable().c_str(),
+                                       InspIRCd::DurationString(f->duration).c_str(),
+                                       InspIRCd::TimeString(ServerInstance->Time() + f->duration).c_str(),
+                                       command.c_str(), f->freeform.c_str(), f->reason.c_str()));
+
+                               if (ServerInstance->XLines->AddLine(zl,NULL))
+                               {
+                                       ServerInstance->XLines->ApplyLines();
+                               }
+                               else
+                                       delete zl;
+                       }
+                       else if (f->action == FA_SHUN && (ServerInstance->XLines->GetFactory("SHUN")))
+                       {
+                               /* Note: We shun *!*@IP so that if their host doesnt resolve the shun still applies. */
+                               Shun* sh = new Shun(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), user->GetIPString());
+                               ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s (%s) was shunned for %s (expires on %s) because their %s message matched %s (%s)",
+                                       user->nick.c_str(), sh->Displayable().c_str(),
+                                       InspIRCd::DurationString(f->duration).c_str(),
+                                       InspIRCd::TimeString(ServerInstance->Time() + f->duration).c_str(),
+                                       command.c_str(), f->freeform.c_str(), f->reason.c_str()));
+
+                               if (ServerInstance->XLines->AddLine(sh, NULL))
+                               {
+                                       ServerInstance->XLines->ApplyLines();
+                               }
+                               else
+                                       delete sh;
+                       }
                        return MOD_RES_DENY;
                }
        }
        return MOD_RES_PASSTHRU;
 }
 
-void ModuleFilter::OnRehash(User* user)
+void ModuleFilter::ReadConfig(ConfigStatus& status)
 {
        ConfigTagList tags = ServerInstance->Config->ConfTags("exemptfromfilter");
-       exemptfromfilter.clear();
+       exemptedchans.clear();
+       exemptednicks.clear();
+
        for (ConfigIter i = tags.first; i != tags.second; ++i)
        {
-               std::string chan = i->second->getString("channel");
-               if (!chan.empty())
-                       exemptfromfilter.insert(chan);
+               ConfigTag* tag = i->second;
+
+               // If "target" is not found, try the old "channel" key to keep compatibility with 2.0 configs
+               const std::string target = tag->getString("target", tag->getString("channel"));
+               if (!target.empty())
+               {
+                       if (target[0] == '#')
+                               exemptedchans.insert(target);
+                       else
+                               exemptednicks.insert(target);
+               }
        }
 
-       std::string newrxengine = ServerInstance->Config->ConfValue("filteropts")->getString("engine");
+       ConfigTag* tag = ServerInstance->Config->ConfValue("filteropts");
+       std::string newrxengine = tag->getString("engine");
+       notifyuser = tag->getBool("notifyuser", true);
 
        factory = RegexEngine ? (RegexEngine.operator->()) : NULL;
 
@@ -489,9 +608,9 @@ void ModuleFilter::OnRehash(User* user)
        if (!RegexEngine)
        {
                if (newrxengine.empty())
-                       ServerInstance->SNO->WriteGlobalSno('a', "WARNING: No regex engine loaded - Filter functionality disabled until this is corrected.");
+                       ServerInstance->SNO->WriteGlobalSno('f', "WARNING: No regex engine loaded - Filter functionality disabled until this is corrected.");
                else
-                       ServerInstance->SNO->WriteGlobalSno('a', "WARNING: Regex engine '%s' is not loaded - Filter functionality disabled until this is corrected.", newrxengine.c_str());
+                       ServerInstance->SNO->WriteGlobalSno('f', "WARNING: Regex engine '%s' is not loaded - Filter functionality disabled until this is corrected.", newrxengine.c_str());
 
                initing = false;
                FreeFilters();
@@ -500,7 +619,7 @@ void ModuleFilter::OnRehash(User* user)
 
        if ((!initing) && (RegexEngine.operator->() != factory))
        {
-               ServerInstance->SNO->WriteGlobalSno('a', "Dumping all filters due to regex engine change");
+               ServerInstance->SNO->WriteGlobalSno('f', "Dumping all filters due to regex engine change");
                FreeFilters();
        }
 
@@ -510,7 +629,7 @@ void ModuleFilter::OnRehash(User* user)
 
 Version ModuleFilter::GetVersion()
 {
-       return Version("Text (spam) filtering", VF_VENDOR | VF_COMMON, RegexEngine ? RegexEngine->name : "");
+       return Version("Provides text (spam) filtering", VF_VENDOR | VF_COMMON, RegexEngine ? RegexEngine->name : "");
 }
 
 std::string ModuleFilter::EncodeFilter(FilterResult* filter)
@@ -523,7 +642,7 @@ std::string ModuleFilter::EncodeFilter(FilterResult* filter)
                if (*n == ' ')
                        *n = '\7';
 
-       stream << x << " " << FilterActionToString(filter->action) << " " << filter->GetFlags() << " " << filter->gline_time << " :" << filter->reason;
+       stream << x << " " << FilterActionToString(filter->action) << " " << filter->GetFlags() << " " << filter->duration << " :" << filter->reason;
        return stream.str();
 }
 
@@ -532,19 +651,22 @@ FilterResult ModuleFilter::DecodeFilter(const std::string &data)
        std::string filteraction;
        FilterResult res;
        irc::tokenstream tokens(data);
-       tokens.GetToken(res.freeform);
-       tokens.GetToken(filteraction);
+       tokens.GetMiddle(res.freeform);
+       tokens.GetMiddle(filteraction);
        if (!StringToFilterAction(filteraction, res.action))
                throw ModuleException("Invalid action: " + filteraction);
 
        std::string filterflags;
-       tokens.GetToken(filterflags);
+       tokens.GetMiddle(filterflags);
        char c = res.FillFlags(filterflags);
        if (c != 0)
                throw ModuleException("Invalid flag: '" + std::string(1, c) + "'");
 
-       tokens.GetToken(res.gline_time);
-       tokens.GetToken(res.reason);
+       std::string duration;
+       tokens.GetMiddle(duration);
+       res.duration = ConvToNum<unsigned long>(duration);
+
+       tokens.GetTrailing(res.reason);
 
        /* Hax to allow spaces in the freeform without changing the design of the irc protocol */
        for (std::string::iterator n = res.freeform.begin(); n != res.freeform.end(); n++)
@@ -554,11 +676,15 @@ FilterResult ModuleFilter::DecodeFilter(const std::string &data)
        return res;
 }
 
-void ModuleFilter::OnSyncNetwork(Module* proto, void* opaque)
+void ModuleFilter::OnSyncNetwork(ProtocolInterface::Server& server)
 {
-       for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); ++i)
+       for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); ++i)
        {
-               proto->ProtoSendMetaData(opaque, NULL, "filter", EncodeFilter(&(*i)));
+               FilterResult& filter = *i;
+               if (filter.from_config)
+                       continue;
+
+               server.SendMetaData("filter", EncodeFilter(&filter));
        }
 }
 
@@ -569,31 +695,23 @@ void ModuleFilter::OnDecodeMetaData(Extensible* target, const std::string &extna
                try
                {
                        FilterResult data = DecodeFilter(extdata);
-                       this->AddFilter(data.freeform, data.action, data.reason, data.gline_time, data.GetFlags());
+                       this->AddFilter(data.freeform, data.action, data.reason, data.duration, data.GetFlags());
                }
                catch (ModuleException& e)
                {
-                       ServerInstance->Logs->Log("m_filter", DEBUG, "Error when unserializing filter: " + std::string(e.GetReason()));
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error when unserializing filter: " + e.GetReason());
                }
        }
 }
 
-ImplFilter::ImplFilter(ModuleFilter* mymodule, const std::string &rea, FilterAction act, long glinetime, const std::string &pat, const std::string &flgs)
-               : FilterResult(pat, rea, act, glinetime, flgs)
-{
-       if (!mymodule->RegexEngine)
-               throw ModuleException("Regex module implementing '"+mymodule->RegexEngine.GetProvider()+"' is not loaded!");
-       regex = mymodule->RegexEngine->Create(pat);
-}
-
 FilterResult* ModuleFilter::FilterMatch(User* user, const std::string &text, int flgs)
 {
        static std::string stripped_text;
        stripped_text.clear();
 
-       for (std::vector<ImplFilter>::iterator index = filters.begin(); index != filters.end(); index++)
+       for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); ++i)
        {
-               FilterResult* filter = dynamic_cast<FilterResult*>(&(*index));
+               FilterResult* filter = &*i;
 
                /* Skip ones that dont apply to us */
                if (!AppliesToMe(user, filter, flgs))
@@ -605,23 +723,19 @@ FilterResult* ModuleFilter::FilterMatch(User* user, const std::string &text, int
                        InspIRCd::StripColor(stripped_text);
                }
 
-               //ServerInstance->Logs->Log("m_filter", DEBUG, "Match '%s' against '%s'", text.c_str(), index->freeform.c_str());
-               if (index->regex->Matches(filter->flag_strip_color ? stripped_text : text))
-               {
-                       //ServerInstance->Logs->Log("m_filter", DEBUG, "MATCH");
-                       return &*index;
-               }
-               //ServerInstance->Logs->Log("m_filter", DEBUG, "NO MATCH");
+               if (filter->regex->Matches(filter->flag_strip_color ? stripped_text : text))
+                       return filter;
        }
        return NULL;
 }
 
-bool ModuleFilter::DeleteFilter(const std::string &freeform)
+bool ModuleFilter::DeleteFilter(const std::string& freeform, std::string& reason)
 {
-       for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+       for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); i++)
        {
                if (i->freeform == freeform)
                {
+                       reason.assign(i->reason);
                        delete i->regex;
                        filters.erase(i);
                        return true;
@@ -630,9 +744,9 @@ bool ModuleFilter::DeleteFilter(const std::string &freeform)
        return false;
 }
 
-std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &freeform, FilterAction type, const std::string &reason, long duration, const std::string &flgs)
+std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string& freeform, FilterAction type, const std::string& reason, unsigned long duration, const std::string& flgs, bool config)
 {
-       for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+       for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); i++)
        {
                if (i->freeform == freeform)
                {
@@ -642,11 +756,11 @@ std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &freeform
 
        try
        {
-               filters.push_back(ImplFilter(this, reason, type, duration, freeform, flgs));
+               filters.push_back(FilterResult(RegexEngine, freeform, reason, type, duration, flgs, config));
        }
        catch (ModuleException &e)
        {
-               ServerInstance->Logs->Log("m_filter", DEFAULT, "Error in regular expression '%s': %s", freeform.c_str(), e.GetReason());
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error in regular expression '%s': %s", freeform.c_str(), e.GetReason().c_str());
                return std::make_pair(false, e.GetReason());
        }
        return std::make_pair(true, "");
@@ -654,17 +768,21 @@ 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, "zline"))
+               fa = FA_ZLINE;
+       else if (stdalgo::string::equalsci(str, "warn"))
+               fa = FA_WARN;
+       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, "shun") && (ServerInstance->XLines->GetFactory("SHUN")))
+               fa = FA_SHUN;
+       else if (stdalgo::string::equalsci(str, "none"))
                fa = FA_NONE;
        else
                return false;
@@ -677,25 +795,42 @@ std::string ModuleFilter::FilterActionToString(FilterAction fa)
        switch (fa)
        {
                case FA_GLINE:  return "gline";
+               case FA_ZLINE:  return "zline";
+               case FA_WARN:   return "warn";
                case FA_BLOCK:  return "block";
                case FA_SILENT: return "silent";
                case FA_KILL:   return "kill";
+               case FA_SHUN:   return "shun";
                default:                return "none";
        }
 }
 
 void ModuleFilter::ReadFilters()
 {
+       insp::flat_set<std::string> removedfilters;
+
+       for (std::vector<FilterResult>::iterator filter = filters.begin(); filter != filters.end(); )
+       {
+               if (filter->from_config)
+               {
+                       removedfilters.insert(filter->freeform);
+                       delete filter->regex;
+                       filter = filters.erase(filter);
+                       continue;
+               }
+
+               // The filter is not from the config.
+               filter++;
+       }
+
        ConfigTagList tags = ServerInstance->Config->ConfTags("keyword");
        for (ConfigIter i = tags.first; i != tags.second; ++i)
        {
                std::string pattern = i->second->getString("pattern");
-               this->DeleteFilter(pattern);
-
                std::string reason = i->second->getString("reason");
                std::string action = i->second->getString("action");
                std::string flgs = i->second->getString("flags");
-               long gline_time = ServerInstance->Duration(i->second->getString("duration"));
+               unsigned long duration = i->second->getDuration("duration", 10*60, 1);
                if (flgs.empty())
                        flgs = "*";
 
@@ -703,29 +838,35 @@ void ModuleFilter::ReadFilters()
                if (!StringToFilterAction(action, fa))
                        fa = FA_NONE;
 
-               try
-               {
-                       filters.push_back(ImplFilter(this, reason, fa, gline_time, pattern, flgs));
-                       ServerInstance->Logs->Log("m_filter", DEFAULT, "Regular expression %s loaded.", pattern.c_str());
-               }
-               catch (ModuleException &e)
-               {
-                       ServerInstance->Logs->Log("m_filter", DEFAULT, "Error in regular expression '%s': %s", pattern.c_str(), e.GetReason());
-               }
+               std::pair<bool, std::string> result = static_cast<ModuleFilter*>(this)->AddFilter(pattern, fa, reason, duration, flgs, true);
+               if (result.first)
+                       removedfilters.erase(pattern);
+               else
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Filter '%s' could not be added: %s", pattern.c_str(), result.second.c_str());
+       }
+
+       if (!removedfilters.empty())
+       {
+               for (insp::flat_set<std::string>::const_iterator it = removedfilters.begin(); it != removedfilters.end(); ++it)
+                       ServerInstance->SNO->WriteGlobalSno('f', "Removing filter '" + *(it) + "' due to config rehash.");
        }
 }
 
-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<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+               for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); i++)
+               {
+                       stats.AddRow(223, RegexEngine.GetProvider()+":"+i->freeform+" "+i->GetFlags()+" "+FilterActionToString(i->action)+" "+ConvToStr(i->duration)+" :"+i->reason);
+               }
+               for (ExemptTargetSet::const_iterator i = exemptedchans.begin(); i != exemptedchans.end(); ++i)
                {
-                       results.push_back(ServerInstance->Config->ServerName+" 223 "+user->nick+" :"+RegexEngine.GetProvider()+":"+i->freeform+" "+i->GetFlags()+" "+FilterActionToString(i->action)+" "+ConvToStr(i->gline_time)+" :"+i->reason);
+                       stats.AddRow(223, "EXEMPT "+(*i));
                }
-               for (std::set<std::string>::iterator i = exemptfromfilter.begin(); i != exemptfromfilter.end(); ++i)
+               for (ExemptTargetSet::const_iterator i = exemptednicks.begin(); i != exemptednicks.end(); ++i)
                {
-                       results.push_back(ServerInstance->Config->ServerName+" 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
new file mode 100644 (file)
index 0000000..923c0cb
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * 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/>.
+ */
+
+
+#include "inspircd.h"
+
+class FlashPDSocket;
+
+namespace
+{
+       insp::intrusive_list<FlashPDSocket> sockets;
+       std::string policy_reply;
+       const std::string expected_request("<policy-file-request/>\0", 23);
+}
+
+class FlashPDSocket : public BufferedSocket, public Timer, public insp::intrusive_list_node<FlashPDSocket>
+{
+       /** True if this object is in the cull list
+        */
+       bool waitingcull;
+
+       bool Tick(time_t currtime) CXX11_OVERRIDE
+       {
+               AddToCull();
+               return false;
+       }
+
+ public:
+       FlashPDSocket(int newfd, unsigned int timeoutsec)
+               : BufferedSocket(newfd)
+               , Timer(timeoutsec)
+               , waitingcull(false)
+       {
+               ServerInstance->Timers.AddTimer(this);
+       }
+
+       ~FlashPDSocket()
+       {
+               sockets.erase(this);
+       }
+
+       void OnError(BufferedSocketError) CXX11_OVERRIDE
+       {
+               AddToCull();
+       }
+
+       void OnDataReady() CXX11_OVERRIDE
+       {
+               if (recvq == expected_request)
+                       WriteData(policy_reply);
+               AddToCull();
+       }
+
+       void AddToCull()
+       {
+               if (waitingcull)
+                       return;
+
+               waitingcull = true;
+               Close();
+               ServerInstance->GlobalCulls.AddItem(this);
+       }
+};
+
+class ModuleFlashPD : public Module
+{
+       unsigned int timeout;
+
+ public:
+       ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+       {
+               if (!stdalgo::string::equalsci(from->bind_tag->getString("type"), "flashpolicyd"))
+                       return MOD_RES_PASSTHRU;
+
+               if (policy_reply.empty())
+                       return MOD_RES_DENY;
+
+               sockets.push_front(new FlashPDSocket(nfd, timeout));
+               return MOD_RES_ALLOW;
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("flashpolicyd");
+               std::string file = tag->getString("file");
+
+               if (!file.empty())
+               {
+                       try
+                       {
+                               FileReader reader(file);
+                               policy_reply = reader.GetString();
+                       }
+                       catch (CoreException&)
+                       {
+                               throw ModuleException("A file was specified for FlashPD, but it could not be loaded at " + tag->getTagLocation());
+                       }
+                       return;
+               }
+
+               // A file was not specified. Set the default setting.
+               // We allow access to all client ports by default
+               std::string to_ports;
+               for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
+               {
+                               ListenSocket* ls = *i;
+                               if (!stdalgo::string::equalsci(ls->bind_tag->getString("type", "clients"), "clients") || !ls->bind_tag->getString("ssl").empty())
+                                       continue;
+
+                               to_ports.append(ConvToStr(ls->bind_sa.port())).push_back(',');
+               }
+
+               if (to_ports.empty())
+               {
+                       policy_reply.clear();
+                       return;
+               }
+
+               to_ports.erase(to_ports.size() - 1);
+
+               policy_reply =
+"<?xml version=\"1.0\"?>\
+<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\
+<cross-domain-policy>\
+<site-control permitted-cross-domain-policies=\"master-only\"/>\
+<allow-access-from domain=\"*\" to-ports=\"" + to_ports + "\" />\
+</cross-domain-policy>";
+               timeout = tag->getDuration("timeout", 5, 1);
+       }
+
+       CullResult cull() CXX11_OVERRIDE
+       {
+               for (insp::intrusive_list<FlashPDSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
+               {
+                       FlashPDSocket* sock = *i;
+                       sock->AddToCull();
+               }
+               return Module::cull();
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Flash Policy Daemon, allows Flash IRC clients to connect", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleFlashPD)
index 1497c1b87b3f808503c80da9a03df7d22fb14669..09f3c9dc7f4870fcfb0aa33b40cb8547b76153e8 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements extban +b r: - realname (gecos) bans */
-
 class ModuleGecosBan : public Module
 {
  public:
-       void init()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               return Version("Provides a way to ban users by their real name with the 'a' and 'r' extbans", VF_OPTCOMMON|VF_VENDOR);
        }
 
-       ~ModuleGecosBan()
+       ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
        {
-       }
+               if ((mask.length() > 2) && (mask[1] == ':'))
+               {
+                       if (mask[0] == 'r')
+                       {
+                               if (InspIRCd::Match(user->GetRealName(), mask.substr(2)))
+                                       return MOD_RES_DENY;
+                       }
+                       else if (mask[0] == 'a')
+                       {
+                               // Check that the user actually specified a real name.
+                               const size_t divider = mask.find('+', 1);
+                               if (divider == std::string::npos)
+                                       return MOD_RES_PASSTHRU;
 
-       Version GetVersion()
-       {
-               return Version("Extban 'r' - realname (gecos) ban", VF_OPTCOMMON|VF_VENDOR);
-       }
+                               // Check whether the user's mask matches.
+                               if (!c->CheckBan(user, mask.substr(2, divider - 2)))
+                                       return MOD_RES_PASSTHRU;
 
-       ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
-       {
-               if ((mask.length() > 2) && (mask[0] == 'r') && (mask[1] == ':'))
-               {
-                       if (InspIRCd::Match(user->fullname, mask.substr(2)))
-                               return MOD_RES_DENY;
+                               // Check whether the user's real name matches.
+                               if (InspIRCd::Match(user->GetRealName(), mask.substr(divider + 1)))
+                                       return MOD_RES_DENY;
+                       }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('r');
+               tokens["EXTBAN"].push_back('a');
+               tokens["EXTBAN"].push_back('r');
        }
 };
 
diff --git a/src/modules/m_geoban.cpp b/src/modules/m_geoban.cpp
new file mode 100644 (file)
index 0000000..221d6f8
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2019 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/geolocation.h"
+#include "modules/whois.h"
+
+enum
+{
+       // InspIRCd-specific.
+       RPL_WHOISCOUNTRY = 344
+};
+
+class ModuleGeoBan
+       : public Module
+       , public Whois::EventListener
+{
+ private:
+       Geolocation::API geoapi;
+
+ public:
+       ModuleGeoBan()
+               : Whois::EventListener(this)
+               , geoapi(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides a way to ban users by country", VF_OPTCOMMON|VF_VENDOR);
+       }
+
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+       {
+               tokens["EXTBAN"].push_back('G');
+       }
+
+       ModResult OnCheckBan(User* user, Channel*, const std::string& mask) CXX11_OVERRIDE
+       {
+               if ((mask.length() > 2) && (mask[0] == 'G') && (mask[1] == ':'))
+               {
+                       Geolocation::Location* location = geoapi ? geoapi->GetLocation(user) : NULL;
+                       const std::string code = location ? location->GetCode() : "XX";
+
+                       // Does this user match against the ban?
+                       if (InspIRCd::Match(code, mask.substr(2)))
+                               return MOD_RES_DENY;
+               }
+               return MOD_RES_PASSTHRU;
+       }
+
+       void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
+       {
+               Geolocation::Location* location = geoapi ? geoapi->GetLocation(whois.GetTarget()) : NULL;
+               if (location)
+                       whois.SendLine(RPL_WHOISCOUNTRY, location->GetCode(), "is connecting from " + location->GetName());
+               else
+                       whois.SendLine(RPL_WHOISCOUNTRY, "*", "is connecting from an unknown country");
+       }
+};
+
+MODULE_INIT(ModuleGeoBan)
diff --git a/src/modules/m_geoclass.cpp b/src/modules/m_geoclass.cpp
new file mode 100644 (file)
index 0000000..6ec03c7
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2019 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/geolocation.h"
+#include "modules/stats.h"
+
+enum
+{
+       // InspIRCd-specific.
+       RPL_STATSCOUNTRY = 801
+};
+
+class ModuleGeoClass
+       : public Module
+       , public Stats::EventListener
+{
+ private:
+       Geolocation::API geoapi;
+
+ public:
+       ModuleGeoClass()
+               : Stats::EventListener(this)
+               , geoapi(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides a way to assign users to connect classes by country", VF_VENDOR);
+       }
+
+       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
+       {
+               const std::string country = myclass->config->getString("country");
+               if (country.empty())
+                       return MOD_RES_PASSTHRU;
+
+               // If we can't find the location of this user then we can't assign
+               // them to a location-specific connect class.
+               Geolocation::Location* location = geoapi ? geoapi->GetLocation(user) : NULL;
+               const std::string code = location ? location->GetCode() : "XX";
+
+               irc::spacesepstream codes(country);
+               for (std::string token; codes.GetToken(token); )
+               {
+                       // If the user matches this country code then they can use this
+                       // connect class.
+                       if (stdalgo::string::equalsci(token, code))
+                               return MOD_RES_PASSTHRU;
+               }
+
+               // A list of country codes were specified but the user didn't match
+               // any of them.
+               return MOD_RES_DENY;
+       }
+
+       ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
+       {
+               if (stats.GetSymbol() != 'G')
+                       return MOD_RES_PASSTHRU;
+
+               // Counter for the number of users in each country.
+               typedef std::map<Geolocation::Location*, size_t> CountryCounts;
+               CountryCounts counts;
+
+               // Counter for the number of users in an unknown country.
+               size_t unknown = 0;
+
+               const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+               for (UserManager::LocalList::const_iterator iter = list.begin(); iter != list.end(); ++iter)
+               {
+                       Geolocation::Location* location = geoapi ? geoapi->GetLocation(*iter) : NULL;
+                       if (location)
+                               counts[location]++;
+                       else
+                               unknown++;
+               }
+
+               for (CountryCounts::const_iterator iter = counts.begin(); iter != counts.end(); ++iter)
+               {
+                       Geolocation::Location* location = iter->first;
+                       stats.AddRow(RPL_STATSCOUNTRY, iter->second, location->GetCode(), location->GetName());
+               }
+
+               if (unknown)
+                       stats.AddRow(RPL_STATSCOUNTRY, unknown, "*", "Unknown Country");
+
+               return MOD_RES_DENY;
+       }
+};
+
+MODULE_INIT(ModuleGeoClass)
index aed65045f58d78c9d5577e3a66e6a6834844de64..8bd63a065ace10acd606672786e5315b556807a5 100644 (file)
@@ -22,8 +22,6 @@
  */
 
 
-/* $ModDesc: Allows global loading of a module. */
-
 #include "inspircd.h"
 
 /** Handle /GLOADMODULE
@@ -34,11 +32,10 @@ class CommandGloadmodule : public Command
        CommandGloadmodule(Module* Creator) : Command(Creator,"GLOADMODULE", 1)
        {
                flags_needed = 'o';
-               syntax = "<modulename> [servermask]";
-               TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
+               syntax = "<modulename> [<servermask>]";
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                std::string servername = parameters.size() > 1 ? parameters[1] : "*";
 
@@ -47,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(975, "%s %s :Module successfully loaded.",user->nick.c_str(), parameters[0].c_str());
+                               user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Module successfully loaded.");
                        }
                        else
                        {
-                               user->WriteNumeric(974, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+                               user->WriteNumeric(ERR_CANTLOADMODULE, parameters[0], ServerInstance->Modules->LastError());
                        }
                }
                else
@@ -60,7 +57,7 @@ class CommandGloadmodule : public Command
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_BROADCAST;
        }
@@ -74,11 +71,18 @@ class CommandGunloadmodule : public Command
        CommandGunloadmodule(Module* Creator) : Command(Creator,"GUNLOADMODULE", 1)
        {
                flags_needed = 'o';
-               syntax = "<modulename> [servermask]";
+               syntax = "<modulename> [<servermask>]";
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
+               if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") &&
+                       InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map))
+               {
+                       user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "You cannot unload core commands!");
+                       return CMD_FAILURE;
+               }
+
                std::string servername = parameters.size() > 1 ? parameters[1] : "*";
 
                if (InspIRCd::Match(ServerInstance->Config->ServerName.c_str(), servername))
@@ -89,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(RPL_UNLOADEDMODULE, parameters[0], "Module successfully unloaded.");
                                }
                                else
                                {
-                                       user->WriteNumeric(972, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+                                       user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], ServerInstance->Modules->LastError());
                                }
                        }
                        else
-                               user->SendText(":%s 972 %s %s :No such module", ServerInstance->Config->ServerName.c_str(), 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());
@@ -106,31 +109,12 @@ class CommandGunloadmodule : public Command
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_BROADCAST;
        }
 };
 
-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(975, "%s %s :Module %ssuccessfully reloaded.",
-                               user->nick.c_str(), name.c_str(), result ? "" : "un");
-               ServerInstance->GlobalCulls.AddItem(this);
-       }
-};
-
 /** Handle /GRELOADMODULE
  */
 class CommandGreloadmodule : public Command
@@ -138,10 +122,10 @@ class CommandGreloadmodule : public Command
  public:
        CommandGreloadmodule(Module* Creator) : Command(Creator, "GRELOADMODULE", 1)
        {
-               flags_needed = 'o'; syntax = "<modulename> [servermask]";
+               flags_needed = 'o'; syntax = "<modulename> [<servermask>]";
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                std::string servername = parameters.size() > 1 ? parameters[1] : "*";
 
@@ -150,14 +134,12 @@ class CommandGreloadmodule : public Command
                        Module* m = ServerInstance->Modules->Find(parameters[0]);
                        if (m)
                        {
-                               GReloadModuleWorker* worker = NULL;
-                               if ((m != creator) && (!creator->dying))
-                                       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(975, "%s %s :Could not find module by that name", user->nick.c_str(), parameters[0].c_str());
+                               user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Could not find module by that name");
                                return CMD_FAILURE;
                        }
                }
@@ -167,7 +149,7 @@ class CommandGreloadmodule : public Command
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_BROADCAST;
        }
@@ -185,22 +167,10 @@ class ModuleGlobalLoad : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd1);
-               ServerInstance->Modules->AddService(cmd2);
-               ServerInstance->Modules->AddService(cmd3);
-       }
-
-       ~ModuleGlobalLoad()
+       Version GetVersion() CXX11_OVERRIDE
        {
-       }
-
-       Version GetVersion()
-       {
-               return Version("Allows global loading of a module.", VF_COMMON | VF_VENDOR);
+               return Version("Allows global loading of a module", VF_COMMON | VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleGlobalLoad)
-
index 85d84252b2b11875100f7b25148f40bf221980f5..e70645161ca42779333bdfdfbf9d3bc023e7120b 100644 (file)
@@ -23,8 +23,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for GLOBOPS and snomask +g */
-
 /** Handle /GLOBOPS
  */
 class CommandGlobops : public Command
@@ -32,11 +30,10 @@ class CommandGlobops : public Command
  public:
        CommandGlobops(Module* Creator) : Command(Creator,"GLOBOPS", 1,1)
        {
-               flags_needed = 'o'; syntax = "<any-text>";
-               TRANSLATE2(TR_TEXT, TR_END);
+               flags_needed = 'o'; syntax = ":<message>";
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                ServerInstance->SNO->WriteGlobalSno('g', "From " + user->nick + ": " + parameters[0]);
                return CMD_SUCCESS;
@@ -49,17 +46,15 @@ class ModuleGlobops : public Module
  public:
        ModuleGlobops() : cmd(this) {}
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd);
                ServerInstance->SNO->EnableSnomask('g',"GLOBOPS");
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for GLOBOPS and snomask +g", VF_VENDOR);
+               return Version("Provides the GLOBOPS command and snomask 'g'", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleGlobops)
diff --git a/src/modules/m_halfop.cpp b/src/modules/m_halfop.cpp
deleted file mode 100644 (file)
index 3194fcd..0000000
+++ /dev/null
@@ -1,104 +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/>.
- */
-
-
-/* $ModDesc: Channel half-operator mode provider */
-
-#include "inspircd.h"
-
-class ModeChannelHalfOp : public ModeHandler
-{
- public:
-       ModeChannelHalfOp(Module* parent);
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
-       unsigned int GetPrefixRank();
-       void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
-       void RemoveMode(User* user, irc::modestacker* stack = NULL);
-
-       ModResult AccessCheck(User* src, Channel*, std::string& value, bool adding)
-       {
-               if (!adding && src->nick == value)
-                       return MOD_RES_ALLOW;
-               return MOD_RES_PASSTHRU;
-       }
-};
-
-ModeChannelHalfOp::ModeChannelHalfOp(Module* parent) : ModeHandler(parent, "halfop", 'h', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
-       list = true;
-       prefix = '%';
-       levelrequired = OP_VALUE;
-       m_paramtype = TR_NICK;
-}
-
-unsigned int ModeChannelHalfOp::GetPrefixRank()
-{
-       return HALFOP_VALUE;
-}
-
-void ModeChannelHalfOp::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
-       const UserMembList* clist = channel->GetUsers();
-
-       for (UserMembCIter i = clist->begin(); i != clist->end(); i++)
-       {
-               if (stack)
-               {
-                       stack->Push(this->GetModeChar(), i->first->nick);
-               }
-               else
-               {
-                       std::vector<std::string> parameters;
-                       parameters.push_back(channel->name);
-                       parameters.push_back("-h");
-                       parameters.push_back(i->first->nick);
-                       ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
-               }
-       }
-
-}
-
-void ModeChannelHalfOp::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-ModeAction ModeChannelHalfOp::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-{
-       return MODEACTION_ALLOW;
-}
-
-class ModuleHalfop : public Module
-{
-       ModeChannelHalfOp mh;
- public:
-       ModuleHalfop() : mh(this)
-       {
-       }
-
-       void init()
-       {
-               ServerInstance->Modules->AddService(mh);
-       }
-
-       Version GetVersion()
-       {
-               return Version("Channel half-operator mode provider", VF_VENDOR);
-       }
-};
-
-MODULE_INIT(ModuleHalfop)
diff --git a/src/modules/m_haproxy.cpp b/src/modules/m_haproxy.cpp
new file mode 100644 (file)
index 0000000..ee9079c
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2018 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 "iohook.h"
+#include "modules/ssl.h"
+
+enum
+{
+       // The SSL TLV flag for a client being connected over SSL.
+       PP2_CLIENT_SSL = 0x01,
+
+       // The family for TCP over IPv4.
+       PP2_FAMILY_IPV4 = 0x11,
+
+       // The length of the PP2_FAMILY_IPV4 endpoints.
+       PP2_FAMILY_IPV4_LENGTH = 12,
+
+       // The family for TCP over IPv6.
+       PP2_FAMILY_IPV6 = 0x21,
+
+       // The length of the PP2_FAMILY_IPV6 endpoints.
+       PP2_FAMILY_IPV6_LENGTH = 36,
+
+       // The family for UNIX sockets.
+       PP2_FAMILY_UNIX = 0x31,
+
+       // The length of the PP2_FAMILY_UNIX endpoints.
+       PP2_FAMILY_UNIX_LENGTH = 216,
+
+       // The bitmask we apply to extract the command.
+       PP2_COMMAND_MASK = 0x0F,
+
+       // The length of the PROXY protocol header.
+       PP2_HEADER_LENGTH = 16,
+
+       // The minimum length of a Type-Length-Value entry.
+       PP2_TLV_LENGTH = 3,
+
+       // The identifier for a SSL TLV entry.
+       PP2_TYPE_SSL = 0x20,
+
+       // The minimum length of a PP2_TYPE_SSL TLV entry.
+       PP2_TYPE_SSL_LENGTH = 5,
+
+       // The length of the PROXY protocol signature.
+       PP2_SIGNATURE_LENGTH = 12,
+
+       // The PROXY protocol version we support.
+       PP2_VERSION = 0x20,
+
+       // The bitmask we apply to extract the protocol version.
+       PP2_VERSION_MASK = 0xF0
+};
+
+enum HAProxyState
+{
+       // We are waiting for the PROXY header section.
+       HPS_WAITING_FOR_HEADER,
+
+       // We are waiting for the PROXY address section.
+       HPS_WAITING_FOR_ADDRESS,
+
+       // The client is fully connected.
+       HPS_CONNECTED
+};
+
+enum HAProxyCommand
+{
+       // LOCAL command.
+       HPC_LOCAL = 0x00,
+
+       // PROXY command.
+       HPC_PROXY = 0x01
+};
+
+struct HAProxyHeader
+{
+       // The signature used to identify the HAProxy protocol.
+       uint8_t signature[PP2_SIGNATURE_LENGTH];
+
+       // The version of the PROXY protocol and command being sent.
+       uint8_t version_command;
+
+       // The family for the address.
+       uint8_t family;
+
+       // The length of the address section.
+       uint16_t length;
+};
+
+class HAProxyHookProvider : public IOHookProvider
+{
+ private:
+        UserCertificateAPI sslapi;
+
+ public:
+       HAProxyHookProvider(Module* mod)
+               : IOHookProvider(mod, "haproxy", IOHookProvider::IOH_UNKNOWN, true)
+               , sslapi(mod)
+       {
+       }
+
+       void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
+
+       void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+       {
+               // We don't need to implement this.
+       }
+};
+
+// The signature for a HAProxy PROXY protocol header.
+static const char proxy_signature[13] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
+
+class HAProxyHook : public IOHookMiddle
+{
+ private:
+       // The length of the address section.
+       uint16_t address_length;
+
+       // The endpoint the client is connecting from.
+       irc::sockets::sockaddrs client;
+
+       // The command sent by the proxy server.
+       HAProxyCommand command;
+
+       // The endpoint the client is connected to.
+       irc::sockets::sockaddrs server;
+
+       // The API for interacting with user SSL internals.
+       UserCertificateAPI& sslapi;
+
+       // The current state of the PROXY parser.
+       HAProxyState state;
+
+       size_t ReadProxyTLV(StreamSocket* sock, size_t start_index, uint16_t buffer_length)
+       {
+               // A TLV must at least consist of a type (uint8_t) and a length (uint16_t).
+               if (buffer_length < PP2_TLV_LENGTH)
+               {
+                       sock->SetError("Truncated HAProxy PROXY TLV type and/or length");
+                       return 0;
+               }
+
+               // Check that the length can actually contain the TLV value.
+               std::string& recvq = GetRecvQ();
+               uint16_t length = ntohs(recvq[start_index + 1] | (recvq[start_index + 2] << 8));
+               if (buffer_length < PP2_TLV_LENGTH + length)
+               {
+                       sock->SetError("Truncated HAProxy PROXY TLV value");
+                       return 0;
+               }
+
+               // What type of TLV are we parsing?
+               switch (recvq[start_index])
+               {
+                       case PP2_TYPE_SSL:
+                               if (!ReadProxyTLVSSL(sock, start_index + PP2_TLV_LENGTH, length))
+                                       return 0;
+                               break;
+               }
+
+               return PP2_TLV_LENGTH + length;
+       }
+
+       bool ReadProxyTLVSSL(StreamSocket* sock, size_t start_index, uint16_t buffer_length)
+       {
+               // A SSL TLV must at least consist of client info (uint8_t) and verification info (uint32_t).
+               if (buffer_length < PP2_TYPE_SSL_LENGTH)
+               {
+                       sock->SetError("Truncated HAProxy PROXY SSL TLV");
+                       return false;
+               }
+
+               // If the socket is not a user socket we don't have to do
+               // anything with this TLVs information.
+               if (sock->type != StreamSocket::SS_USER)
+                       return true;
+
+               // If the sslinfo module is not loaded we can't
+               // do anything with this TLV.
+               if (!sslapi)
+                       return true;
+
+               // If the client is not connecting via SSL the rest of this TLV is irrelevant.
+               std::string& recvq = GetRecvQ();
+               if ((recvq[start_index] & PP2_CLIENT_SSL) == 0)
+                       return true;
+
+               // Create a fake ssl_cert for the user. Ideally we should use the user's
+               // SSL client certificate here but as of 2018-10-16 this is not forwarded
+               // by HAProxy.
+               ssl_cert* cert = new ssl_cert;
+               cert->error = "HAProxy does not forward client SSL certificates";
+               cert->invalid = true;
+               cert->revoked = true;
+               cert->trusted = false;
+               cert->unknownsigner = true;
+
+               // Extract the user for this socket and set their certificate.
+               LocalUser* luser = static_cast<UserIOHandler*>(sock)->user;
+               sslapi->SetCertificate(luser, cert);
+               return true;
+       }
+
+       int ReadProxyAddress(StreamSocket* sock)
+       {
+               // Block until we have the entire address.
+               std::string& recvq = GetRecvQ();
+               if (recvq.length() < address_length)
+                       return 0;
+
+               switch (command)
+               {
+                       case HPC_LOCAL:
+                               // Skip the address completely.
+                               recvq.erase(0, address_length);
+                               break;
+
+                       case HPC_PROXY:
+                               // Store the endpoint information.
+                               size_t tlv_index = 0;
+                               switch (client.family())
+                               {
+                                       case AF_INET:
+                                               memcpy(&client.in4.sin_addr.s_addr, &recvq[0], 4);
+                                               memcpy(&server.in4.sin_addr.s_addr, &recvq[4], 4);
+                                               memcpy(&client.in4.sin_port, &recvq[8], 2);
+                                               memcpy(&server.in4.sin_port, &recvq[10], 2);
+                                               tlv_index = 12;
+                                               break;
+
+                                       case AF_INET6:
+                                               memcpy(client.in6.sin6_addr.s6_addr, &recvq[0], 16);
+                                               memcpy(server.in6.sin6_addr.s6_addr, &recvq[16], 16);
+                                               memcpy(&client.in6.sin6_port, &recvq[32], 2);
+                                               memcpy(&server.in6.sin6_port, &recvq[34], 2);
+                                               tlv_index = 36;
+                                               break;
+
+                                       case AF_UNIX:
+                                               memcpy(client.un.sun_path, &recvq[0], 108);
+                                               memcpy(server.un.sun_path, &recvq[108], 108);
+                                               tlv_index = 216;
+                                               break;
+                               }
+
+                               if (!sock->OnSetEndPoint(server, client))
+                                       return -1;
+
+                               // Parse any available TLVs.
+                               while (tlv_index < address_length)
+                               {
+                                       size_t length = ReadProxyTLV(sock, tlv_index, address_length - tlv_index);
+                                       if (!length)
+                                               return -1;
+
+                                       tlv_index += length;
+                               }
+
+                               // Erase the processed proxy information from the receive queue.
+                               recvq.erase(0, address_length);
+               }
+
+               // We're done!
+               state = HPS_CONNECTED;
+               return 1;
+       }
+
+       int ReadProxyHeader(StreamSocket* sock)
+       {
+               // Block until we have a header.
+               std::string& recvq = GetRecvQ();
+               if (recvq.length() < PP2_HEADER_LENGTH)
+                       return 0;
+
+               // Read the header.
+               HAProxyHeader header;
+               memcpy(&header, recvq.c_str(), PP2_HEADER_LENGTH);
+               recvq.erase(0, PP2_HEADER_LENGTH);
+
+               // Check we are actually parsing a HAProxy header.
+               if (memcmp(&header.signature, proxy_signature, PP2_SIGNATURE_LENGTH) != 0)
+               {
+                       // If we've reached this point the proxy server did not send a proxy information.
+                       sock->SetError("Invalid HAProxy PROXY signature");
+                       return -1;
+               }
+
+               // We only support this version of the protocol.
+               const uint8_t version = (header.version_command & PP2_VERSION_MASK);
+               if (version != PP2_VERSION)
+               {
+                       sock->SetError("Unsupported HAProxy PROXY protocol version");
+                       return -1;
+               }
+
+               // We only support the LOCAL and PROXY commands.
+               command = static_cast<HAProxyCommand>(header.version_command & PP2_COMMAND_MASK);
+               switch (command)
+               {
+                       case HPC_LOCAL:
+                               // Intentionally left blank.
+                               break;
+
+                       case HPC_PROXY:
+                               // Check the protocol support and initialise the sockaddrs.
+                               uint16_t shortest_length;
+                               switch (header.family)
+                               {
+                                       case PP2_FAMILY_IPV4: // TCP over IPv4.
+                                               client.sa.sa_family = server.sa.sa_family = AF_INET;
+                                               shortest_length = PP2_FAMILY_IPV4_LENGTH;
+                                               break;
+
+                                       case PP2_FAMILY_IPV6: // TCP over IPv6.
+                                               client.sa.sa_family = server.sa.sa_family = AF_INET6;
+                                               shortest_length = PP2_FAMILY_IPV6_LENGTH;
+                                               break;
+
+                                       case PP2_FAMILY_UNIX: // UNIX stream.
+                                               client.sa.sa_family = server.sa.sa_family = AF_UNIX;
+                                               shortest_length = PP2_FAMILY_UNIX_LENGTH;
+                                               break;
+
+                                       default: // Unknown protocol.
+                                               sock->SetError("Invalid HAProxy PROXY protocol type");
+                                               return -1;
+                               }
+
+                               // Check that the length can actually contain the addresses.
+                               address_length = ntohs(header.length);
+                               if (address_length < shortest_length)
+                               {
+                                       sock->SetError("Truncated HAProxy PROXY address section");
+                                       return -1;
+                               }
+                               break;
+
+                       default:
+                               sock->SetError("Unsupported HAProxy PROXY command");
+                               return -1;
+               }
+
+               state = HPS_WAITING_FOR_ADDRESS;
+               return ReadProxyAddress(sock);
+       }
+
+ public:
+       HAProxyHook(IOHookProvider* Prov, StreamSocket* sock, UserCertificateAPI& api)
+               : IOHookMiddle(Prov)
+               , sslapi(api)
+               , state(HPS_WAITING_FOR_HEADER)
+       {
+               sock->AddIOHook(this);
+       }
+
+       int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
+       {
+               // We don't need to implement this.
+               GetSendQ().moveall(uppersendq);
+               return 1;
+       }
+
+       int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
+       {
+               switch (state)
+               {
+                       case HPS_WAITING_FOR_HEADER:
+                               return ReadProxyHeader(sock);
+
+                       case HPS_WAITING_FOR_ADDRESS:
+                               return ReadProxyAddress(sock);
+
+                       case HPS_CONNECTED:
+                               std::string& recvq = GetRecvQ();
+                               destrecvq.append(recvq);
+                               recvq.clear();
+                               return 1;
+               }
+
+               // We should never reach this point.
+               return -1;
+       }
+
+       void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
+       {
+               // We don't need to implement this.
+       }
+};
+
+void HAProxyHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+{
+       new HAProxyHook(this, sock, sslapi);
+}
+
+class ModuleHAProxy : public Module
+{
+ private:
+       reference<HAProxyHookProvider> hookprov;
+
+ public:
+       ModuleHAProxy()
+               : hookprov(new HAProxyHookProvider(this))
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides support for the HAProxy PROXY protocol", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleHAProxy)
index 4bbe8785e4ef4f7b2f22d059b56f81682eb8d6fe..5aa719c2f678fe84f46ad21dff078b951664fd4e 100644 (file)
  */
 
 
-/* $ModDesc: Provides the /HELPOP command for useful information */
-
 #include "inspircd.h"
+#include "modules/whois.h"
 
-static std::map<irc::string, std::string> helpop_map;
+enum
+{
+       // From UnrealIRCd.
+       RPL_WHOISHELPOP = 310,
+
+       // From ircd-ratbox.
+       ERR_HELPNOTFOUND = 524,
+       RPL_HELPSTART = 704,
+       RPL_HELPTXT = 705,
+       RPL_ENDOFHELP = 706
+};
+
+typedef std::map<std::string, std::string, irc::insensitive_swo> HelpopMap;
+static HelpopMap helpop_map;
 
 /** Handles user mode +h
  */
@@ -42,90 +54,81 @@ class Helpop : public SimpleUserModeHandler
  */
 class CommandHelpop : public Command
 {
+ private:
+       const std::string startkey;
+
  public:
-       CommandHelpop(Module* Creator) : Command(Creator, "HELPOP", 0)
+       std::string nohelp;
+
+       CommandHelpop(Module* Creator)
+               : Command(Creator, "HELPOP", 0)
+               , startkey("start")
        {
                syntax = "<any-text>";
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               irc::string parameter("start");
-               if (parameters.size() > 0)
-                       parameter = parameters[0].c_str();
+               const std::string& parameter = (!parameters.empty() ? parameters[0] : startkey);
 
                if (parameter == "index")
                {
                        /* iterate over all helpop items */
-                       user->WriteServ("290 %s :HELPOP topic index", user->nick.c_str());
-                       for (std::map<irc::string, std::string>::iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++)
-                       {
-                               user->WriteServ("292 %s :  %s", user->nick.c_str(), iter->first.c_str());
-                       }
-                       user->WriteServ("292 %s :*** End of HELPOP topic index", user->nick.c_str());
+                       user->WriteNumeric(RPL_HELPSTART, parameter, "HELPOP topic index");
+                       for (HelpopMap::const_iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++)
+                               user->WriteNumeric(RPL_HELPTXT, parameter, InspIRCd::Format("  %s", iter->first.c_str()));
+                       user->WriteNumeric(RPL_ENDOFHELP, parameter, "*** End of HELPOP topic index");
                }
                else
                {
-                       user->WriteServ("290 %s :*** HELPOP for %s", user->nick.c_str(), parameter.c_str());
-                       user->WriteServ("292 %s : -", user->nick.c_str());
-
-                       std::map<irc::string, std::string>::iterator iter = helpop_map.find(parameter);
-
+                       HelpopMap::const_iterator iter = helpop_map.find(parameter);
                        if (iter == helpop_map.end())
                        {
-                               iter = helpop_map.find("nohelp");
+                               user->WriteNumeric(ERR_HELPNOTFOUND, parameter, nohelp);
+                               return CMD_FAILURE;
                        }
 
-                       std::string value = iter->second;
-                       irc::sepstream stream(value, '\n');
+                       const std::string& value = iter->second;
+                       irc::sepstream stream(value, '\n', true);
                        std::string token = "*";
 
+                       user->WriteNumeric(RPL_HELPSTART, parameter, InspIRCd::Format("*** HELPOP for %s", parameter.c_str()));
                        while (stream.GetToken(token))
                        {
                                // Writing a blank line will not work with some clients
                                if (token.empty())
-                                       user->WriteServ("292 %s : ", user->nick.c_str());
+                                       user->WriteNumeric(RPL_HELPTXT, parameter, ' ');
                                else
-                                       user->WriteServ("292 %s :%s", user->nick.c_str(), token.c_str());
+                                       user->WriteNumeric(RPL_HELPTXT, parameter, token);
                        }
-
-                       user->WriteServ("292 %s : -", user->nick.c_str());
-                       user->WriteServ("292 %s :*** End of HELPOP", user->nick.c_str());
+                       user->WriteNumeric(RPL_ENDOFHELP, parameter, "*** End of HELPOP");
                }
                return CMD_SUCCESS;
        }
 };
 
-class ModuleHelpop : public Module
+class ModuleHelpop : public Module, public Whois::EventListener
 {
-       private:
                CommandHelpop cmd;
                Helpop ho;
 
        public:
                ModuleHelpop()
-                       : cmd(this), ho(this)
+                       : Whois::EventListener(this)
+                       , cmd(this)
+                       , ho(this)
                {
                }
 
-               void init()
+               void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
                {
-                       ReadConfig();
-                       ServerInstance->Modules->AddService(ho);
-                       ServerInstance->Modules->AddService(cmd);
-                       Implementation eventlist[] = { I_OnRehash, I_OnWhois };
-                       ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               }
-
-               void ReadConfig()
-               {
-                       std::map<irc::string, std::string> help;
+                       HelpopMap help;
 
                        ConfigTagList tags = ServerInstance->Config->ConfTags("helpop");
                        for(ConfigIter i = tags.first; i != tags.second; ++i)
                        {
                                ConfigTag* tag = i->second;
-                               irc::string key = assign(tag->getString("key"));
+                               std::string key = tag->getString("key");
                                std::string value;
                                tag->readString("value", value, true); /* Linefeeds allowed */
 
@@ -142,31 +145,24 @@ class ModuleHelpop : public Module
                                // error!
                                throw ModuleException("m_helpop: Helpop file is missing important entry 'start'. Please check the example conf.");
                        }
-                       else if (help.find("nohelp") == help.end())
-                       {
-                               // error!
-                               throw ModuleException("m_helpop: Helpop file is missing important entry 'nohelp'. Please check the example conf.");
-                       }
 
                        helpop_map.swap(help);
-               }
 
-               void OnRehash(User* user)
-               {
-                       ReadConfig();
+                       ConfigTag* tag = ServerInstance->Config->ConfValue("helpmsg");
+                       cmd.nohelp = tag->getString("nohelp", "There is no help for the topic you searched for. Please try again.", 1);
                }
 
-               void OnWhois(User* src, User* dst)
+               void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
                {
-                       if (dst->IsModeSet('h'))
+                       if (whois.GetTarget()->IsModeSet(ho))
                        {
-                               ServerInstance->SendWhoisLine(src, dst, 310, src->nick+" "+dst->nick+" :is available for help.");
+                               whois.SendLine(RPL_WHOISHELPOP, "is available for help.");
                        }
                }
 
-               Version GetVersion()
+               Version GetVersion() CXX11_OVERRIDE
                {
-                       return Version("Provides the /HELPOP command for useful information", VF_VENDOR);
+                       return Version("Provides the HELPOP command for useful information", VF_VENDOR);
                }
 };
 
index 008c622086efc8caf14147480de27d4ef194b5df..1664582e0006ac3e7fe352c6305c1a14adf4c344 100644 (file)
@@ -19,8 +19,7 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides support for hiding channels with user mode +I */
+#include "modules/whois.h"
 
 /** Handles user mode +I
  */
@@ -30,49 +29,39 @@ 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)
-       {
-       }
-
-       void init()
+       ModuleHideChans()
+               : Whois::LineEventListener(this)
+               , hm(this)
        {
-               ServerInstance->Modules->AddService(hm);
-               Implementation eventlist[] = { I_OnWhoisLine, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
        }
 
-       virtual ~ModuleHideChans()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for hiding channels with user mode +I", VF_VENDOR);
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                AffectsOpers = ServerInstance->Config->ConfValue("hidechans")->getBool("affectsopers");
        }
 
-       ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+       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('I'))
+               if (!whois.GetTarget()->IsModeSet(hm))
                        return MOD_RES_PASSTHRU;
 
                /* if it affects opers, we don't care if they are opered */
@@ -80,7 +69,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. */
@@ -88,5 +77,4 @@ class ModuleHideChans : public Module
        }
 };
 
-
 MODULE_INIT(ModuleHideChans)
diff --git a/src/modules/m_hidelist.cpp b/src/modules/m_hidelist.cpp
new file mode 100644 (file)
index 0000000..9c8811f
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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) CXX11_OVERRIDE
+       {
+               // 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
+       {
+               ConfigTagList tags = ServerInstance->Config->ConfTags("hidelist");
+               typedef std::vector<std::pair<std::string, unsigned int> > NewConfigs;
+               NewConfigs newconfigs;
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+                       std::string modename = tag->getString("mode");
+                       if (modename.empty())
+                               throw ModuleException("Empty <hidelist:mode> at " + tag->getTagLocation());
+                       // If rank is set to 0 everyone inside the channel can view the list,
+                       // but non-members may not
+                       unsigned int rank = tag->getUInt("rank", HALFOP_VALUE);
+                       newconfigs.push_back(std::make_pair(modename, rank));
+               }
+
+               stdalgo::delete_all(watchers);
+               watchers.clear();
+
+               for (NewConfigs::const_iterator i = newconfigs.begin(); i != newconfigs.end(); ++i)
+                       watchers.push_back(new ListWatcher(this, i->first, i->second));
+       }
+
+       ~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_hidemode.cpp b/src/modules/m_hidemode.cpp
new file mode 100644 (file)
index 0000000..d5ac571
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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
+{
+class Settings
+{
+       typedef insp::flat_map<std::string, unsigned int> RanksToSeeMap;
+       RanksToSeeMap rankstosee;
+
+ public:
+       unsigned int GetRequiredRank(const ModeHandler& mh) const
+       {
+               RanksToSeeMap::const_iterator it = rankstosee.find(mh.name);
+               if (it != rankstosee.end())
+                       return it->second;
+               return 0;
+       }
+
+       void Load()
+       {
+               RanksToSeeMap newranks;
+
+               ConfigTagList tags = ServerInstance->Config->ConfTags("hidemode");
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+                       const std::string modename = tag->getString("mode");
+                       if (modename.empty())
+                               throw ModuleException("<hidemode:mode> is empty at " + tag->getTagLocation());
+
+                       unsigned int rank = tag->getUInt("rank", 0);
+                       if (!rank)
+                               throw ModuleException("<hidemode:rank> must be greater than 0 at " + tag->getTagLocation());
+
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Hiding the %s mode from users below rank %u", modename.c_str(), rank);
+                       newranks.insert(std::make_pair(modename, rank));
+               }
+               rankstosee.swap(newranks);
+       }
+};
+
+class ModeHook : public ClientProtocol::EventHook
+{
+       typedef insp::flat_map<unsigned int, const ClientProtocol::MessageList*> FilteredModeMap;
+
+       std::vector<Modes::ChangeList> modechangelists;
+       std::list<ClientProtocol::Messages::Mode> filteredmodelist;
+       std::list<ClientProtocol::MessageList> filteredmsgplists;
+       FilteredModeMap cache;
+
+       static ModResult HandleResult(const ClientProtocol::MessageList* filteredmessagelist, ClientProtocol::MessageList& messagelist)
+       {
+               // Deny if member isn't allowed to see even a single mode change from this mode event
+               if (!filteredmessagelist)
+                       return MOD_RES_DENY;
+
+               // Member is allowed to see at least one mode change, replace list
+               if (filteredmessagelist != &messagelist)
+                       messagelist = *filteredmessagelist;
+
+               return MOD_RES_PASSTHRU;
+       }
+
+       Modes::ChangeList* FilterModeChangeList(const ClientProtocol::Events::Mode& mode, unsigned int rank)
+       {
+               Modes::ChangeList* modechangelist = NULL;
+               for (Modes::ChangeList::List::const_iterator i = mode.GetChangeList().getlist().begin(); i != mode.GetChangeList().getlist().end(); ++i)
+               {
+                       const Modes::Change& curr = *i;
+                       if (settings.GetRequiredRank(*curr.mh) <= rank)
+                       {
+                                // No restriction on who can see this mode or there is one but the member's rank is sufficient
+                               if (modechangelist)
+                                       modechangelist->push(curr);
+
+                               continue;
+                       }
+
+                       // Member cannot see the current mode change
+
+                       if (!modechangelist)
+                       {
+                               // Create new mode change list or reuse the last one if it's empty
+                               if ((modechangelists.empty()) || (!modechangelists.back().empty()))
+                                       modechangelists.push_back(Modes::ChangeList());
+
+                               // Add all modes to it which we've accepted so far
+                               modechangelists.back().push(mode.GetChangeList().getlist().begin(), i);
+                               modechangelist = &modechangelists.back();
+                       }
+               }
+               return modechangelist;
+       }
+
+       void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
+       {
+               cache.clear();
+               filteredmsgplists.clear();
+               filteredmodelist.clear();
+               modechangelists.clear();
+
+               // Ensure no reallocations will happen
+               const size_t numprefixmodes = ServerInstance->Modes.GetPrefixModes().size();
+               modechangelists.reserve(numprefixmodes);
+       }
+
+       ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
+       {
+               const ClientProtocol::Events::Mode& mode = static_cast<const ClientProtocol::Events::Mode&>(ev);
+               Channel* const chan = mode.GetMessages().front().GetChanTarget();
+               if (!chan)
+                       return MOD_RES_PASSTHRU;
+
+               Membership* const memb = chan->GetUser(user);
+               if (!memb)
+                       return MOD_RES_PASSTHRU;
+
+               // Check cache first
+               const FilteredModeMap::const_iterator it = cache.find(memb->getRank());
+               if (it != cache.end())
+                       return HandleResult(it->second, messagelist);
+
+               // Message for this rank isn't cached, generate it now
+               const Modes::ChangeList* const filteredchangelist = FilterModeChangeList(mode, memb->getRank());
+
+               // If no new change list was generated (above method returned NULL) it means the member and everyone else
+               // with the same rank can see everything in the original change list.
+               ClientProtocol::MessageList* finalmsgplist = &messagelist;
+               if (filteredchangelist)
+               {
+                       if (filteredchangelist->empty())
+                       {
+                               // This rank cannot see any mode changes in the original change list
+                               finalmsgplist = NULL;
+                       }
+                       else
+                       {
+                               // This rank can see some of the mode changes in the filtered mode change list.
+                               // Create and store a new protocol message from it.
+                               filteredmsgplists.push_back(ClientProtocol::MessageList());
+                               ClientProtocol::Events::Mode::BuildMessages(mode.GetMessages().front().GetSourceUser(), chan, NULL, *filteredchangelist, filteredmodelist, filteredmsgplists.back());
+                               finalmsgplist = &filteredmsgplists.back();
+                       }
+               }
+
+               // Cache the result in all cases so it can be reused for further members with the same rank
+               cache.insert(std::make_pair(memb->getRank(), finalmsgplist));
+               return HandleResult(finalmsgplist, messagelist);
+       }
+
+ public:
+       Settings settings;
+
+       ModeHook(Module* mod)
+               : ClientProtocol::EventHook(mod, "MODE", 10)
+       {
+       }
+};
+}
+
+class ModuleHideMode : public Module
+{
+ private:
+       ModeHook modehook;
+
+ public:
+       ModuleHideMode()
+               : modehook(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               modehook.settings.Load();
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides support for hiding mode changes", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleHideMode)
index 32999d9f0b36025e6e25acf95b1f53caf09eccad..d78ed538b64e2046fad85617897f777b734c6b32 100644 (file)
@@ -20,8 +20,9 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides support for hiding oper status with user mode +H */
+#include "modules/stats.h"
+#include "modules/who.h"
+#include "modules/whois.h"
 
 /** Handles user mode +H
  */
@@ -36,7 +37,7 @@ class HideOper : public SimpleUserModeHandler
                oper = true;
        }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
        {
                if (SimpleUserModeHandler::OnModeChange(source, dest, channel, parameter, adding) == MODEACTION_DENY)
                        return MODEACTION_DENY;
@@ -50,43 +51,40 @@ class HideOper : public SimpleUserModeHandler
        }
 };
 
-class ModuleHideOper : public Module
+class ModuleHideOper
+       : public Module
+       , public Stats::EventListener
+       , public Who::EventListener
+       , public Whois::LineEventListener
 {
+ private:
        HideOper hm;
        bool active;
+
  public:
        ModuleHideOper()
-               : hm(this)
+               : Stats::EventListener(this)
+               , Who::EventListener(this)
+               , Whois::LineEventListener(this)
+               , hm(this)
                , active(false)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(hm);
-               Implementation eventlist[] = { I_OnWhoisLine, I_OnSendWhoLine, I_OnStats, I_OnNumeric, I_OnUserQuit };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-
-       virtual ~ModuleHideOper()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for hiding oper status with user mode +H", VF_VENDOR);
        }
 
-       void OnUserQuit(User* user, const std::string&, const std::string&)
+       void OnUserQuit(User* user, const std::string&, const std::string&) CXX11_OVERRIDE
        {
-               if (user->IsModeSet('H'))
+               if (user->IsModeSet(hm))
                        hm.opercount--;
        }
 
-       ModResult OnNumeric(User* user, unsigned int numeric, const std::string& text)
+       ModResult OnNumeric(User* user, const Numeric::Numeric& numeric) CXX11_OVERRIDE
        {
-               if (numeric != 252 || active || user->HasPrivPermission("users/auspex"))
+               if (numeric.GetNumeric() != RPL_LUSEROP || active || user->HasPrivPermission("users/auspex"))
                        return MOD_RES_PASSTHRU;
 
                // If there are no visible operators then we shouldn't send the numeric.
@@ -94,68 +92,76 @@ class ModuleHideOper : public Module
                if (opercount)
                {
                        active = true;
-                       user->WriteNumeric(252, "%s %lu :operator(s) online", user->nick.c_str(),  static_cast<unsigned long>(opercount));
+                       user->WriteNumeric(RPL_LUSEROP, opercount, "operator(s) online");
                        active = false;
                }
                return MOD_RES_DENY;
        }
 
-       ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+       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('H'))
+               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, std::string& line)
+       ModResult OnWhoLine(const Who::Request& request, LocalUser* source, User* user, Membership* memb, Numeric::Numeric& numeric) CXX11_OVERRIDE
        {
-               if (user->IsModeSet('H') && !source->HasPrivPermission("users/auspex"))
+               if (user->IsModeSet(hm) && !source->HasPrivPermission("users/auspex"))
                {
+                       // Hide the line completely if doing a "/who * o" query
+                       if (request.flags['o'])
+                               return MOD_RES_DENY;
+
+                       size_t flag_index;
+                       if (!request.GetFieldIndex('f', flag_index))
+                               return MOD_RES_PASSTHRU;
+
                        // hide the "*" that marks the user as an oper from the /WHO line
-                       std::string::size_type spcolon = line.find(" :");
-                       if (spcolon == std::string::npos)
-                               return; // Another module hid the user completely
-                       std::string::size_type sp = line.rfind(' ', spcolon-1);
-                       std::string::size_type pos = line.find('*', sp);
+                       // #chan ident localhost insp22.test nick H@ :0 Attila
+                       if (numeric.GetParams().size() <= flag_index)
+                               return MOD_RES_PASSTHRU;
+
+                       std::string& param = numeric.GetParams()[flag_index];
+                       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(char symbol, User* user, string_list &results)
+       ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
        {
-               if (symbol != 'P')
+               if (stats.GetSymbol() != 'P')
                        return MOD_RES_PASSTHRU;
 
                unsigned int count = 0;
-               for (std::list<User*>::const_iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); ++i)
+               const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+               for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
                {
                        User* oper = *i;
-                       if (!ServerInstance->ULine(oper->server) && (IS_OPER(user) || !oper->IsModeSet('H')))
+                       if (!oper->server->IsULine() && (stats.GetSource()->IsOper() || !oper->IsModeSet(hm)))
                        {
-                               results.push_back(ServerInstance->Config->ServerName+" 249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
-                                               (IS_LOCAL(oper) ? ConvToStr(ServerInstance->Time() - oper->idle_lastmsg) + " secs" : "unavailable"));
+                               LocalUser* lu = IS_LOCAL(oper);
+                               stats.AddRow(249, oper->nick + " (" + oper->ident + "@" + oper->GetDisplayedHost() + ") Idle: " +
+                                               (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
                                count++;
                        }
                }
-               results.push_back(ServerInstance->Config->ServerName+" 249 "+user->nick+" :"+ConvToStr(count)+" OPER(s)");
+               stats.AddRow(249, ConvToStr(count)+" OPER(s)");
 
                return MOD_RES_DENY;
        }
 };
 
-
 MODULE_INIT(ModuleHideOper)
index 7433fccd3e0195953f5ae9d1ad269bc0de868872..895e0f04a29ebba0f2a3c615a91bc4c42c71fa96 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/account.h"
 
-/* $ModDesc: Provides masking of user hostnames in a different way to m_cloaking */
-
-/** Holds information on a host set by m_hostchange
- */
-class Host
+// Holds information about a <hostchange> rule.
+class HostRule
 {
  public:
        enum HostChangeAction
        {
-               HCA_SET,
-               HCA_SUFFIX,
-               HCA_ADDNICK
+               // Add the user's account name to their hostname.
+               HCA_ADDACCOUNT,
+
+               // Add the user's nickname to their hostname.
+               HCA_ADDNICK,
+
+               // Set the user's hostname to the specific value.
+               HCA_SET
        };
 
+ private:
        HostChangeAction action;
-       std::string newhost;
-       std::string ports;
+       std::string host;
+       std::string mask;
+       insp::flat_set<int> ports;
+       std::string prefix;
+       std::string suffix;
+
+ public:
+       HostRule(const std::string& Mask, const std::string& Host, const insp::flat_set<int>& Ports)
+               : action(HCA_SET)
+               , host(Host)
+               , mask(Mask)
+               , ports(Ports)
+       {
+       }
+
+       HostRule(HostChangeAction Action, const std::string& Mask, const insp::flat_set<int>& Ports, const std::string& Prefix, const std::string& Suffix)
+               : action(Action)
+               , mask(Mask)
+               , ports(Ports)
+               , prefix(Prefix)
+               , suffix(Suffix)
+       {
+       }
+
+       HostChangeAction GetAction() const
+       {
+               return action;
+       }
 
-       Host(HostChangeAction Action, const std::string& Newhost, const std::string& Ports) :
-               action(Action), newhost(Newhost), ports(Ports) {}
+       const std::string& GetHost() const
+       {
+               return host;
+       }
+
+       bool Matches(LocalUser* user) const
+       {
+               if (!ports.empty() && !ports.count(user->server_sa.port()))
+                       return false;
+
+               if (InspIRCd::MatchCIDR(user->MakeHost(), mask))
+                       return true;
+
+               return InspIRCd::MatchCIDR(user->MakeHostIP(), mask);
+       }
+
+       void Wrap(const std::string& value, std::string& out) const
+       {
+               if (!prefix.empty())
+                       out.append(prefix);
+
+               out.append(value);
+
+               if (!suffix.empty())
+                       out.append(suffix);
+       }
 };
 
-typedef std::vector<std::pair<std::string, Host> > hostchanges_t;
+typedef std::vector<HostRule> HostRules;
 
 class ModuleHostChange : public Module
 {
- private:
-       hostchanges_t hostchanges;
-       std::string MySuffix;
-       std::string MyPrefix;
-       std::string MySeparator;
+private:
+       std::bitset<UCHAR_MAX> hostmap;
+       HostRules hostrules;
 
- public:
-       void init()
+       std::string CleanName(const std::string& name)
        {
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnRehash, I_OnUserConnect };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               std::string buffer;
+               buffer.reserve(name.length());
+               for (std::string::const_iterator iter = name.begin(); iter != name.end(); ++iter)
+               {
+                       if (hostmap.test(static_cast<unsigned char>(*iter)))
+                       {
+                               buffer.push_back(*iter);
+                       }
+               }
+               return buffer;
        }
 
-       virtual void OnRehash(User* user)
+ public:
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               ConfigTag* host = ServerInstance->Config->ConfValue("host");
-               MySuffix = host->getString("suffix");
-               MyPrefix = host->getString("prefix");
-               MySeparator = host->getString("separator", ".");
-               hostchanges.clear();
+               HostRules rules;
 
-               std::set<std::string> dupecheck;
                ConfigTagList tags = ServerInstance->Config->ConfTags("hostchange");
                for (ConfigIter i = tags.first; i != tags.second; ++i)
                {
                        ConfigTag* tag = i->second;
-                       std::string mask = tag->getString("mask");
-                       if (!dupecheck.insert(mask).second)
-                               throw ModuleException("Duplicate hostchange entry: " + mask);
 
-                       Host::HostChangeAction act;
-                       std::string newhost;
-                       std::string action = tag->getString("action");
-                       if (!strcasecmp(action.c_str(), "set"))
+                       // Ensure that we have the <hostchange:mask> parameter.
+                       const std::string mask = tag->getString("mask");
+                       if (mask.empty())
+                               throw ModuleException("<hostchange:mask> is a mandatory field, at " + tag->getTagLocation());
+
+                       insp::flat_set<int> ports;
+                       const std::string portlist = tag->getString("ports");
+                       if (!ports.empty())
                        {
-                               act = Host::HCA_SET;
-                               newhost = tag->getString("value");
+                               irc::portparser portrange(portlist, false);
+                               while (int port = portrange.GetToken())
+                                       ports.insert(port);
                        }
-                       else if (!strcasecmp(action.c_str(), "suffix"))
-                               act = Host::HCA_SUFFIX;
-                       else if (!strcasecmp(action.c_str(), "addnick"))
-                               act = Host::HCA_ADDNICK;
-                       else
-                               throw ModuleException("Invalid hostchange action: " + action);
 
-                       hostchanges.push_back(std::make_pair(mask, Host(act, newhost, tag->getString("ports"))));
+                       // Determine what type of host rule this is.
+                       const std::string action = tag->getString("action");
+                       if (stdalgo::string::equalsci(action, "addaccount"))
+                       {
+                               // The hostname is in the format [prefix]<account>[suffix].
+                               rules.push_back(HostRule(HostRule::HCA_ADDACCOUNT, mask, ports, tag->getString("prefix"), tag->getString("suffix")));
+                       }
+                       else if (stdalgo::string::equalsci(action, "addnick"))
+                       {
+                               // The hostname is in the format [prefix]<nick>[suffix].
+                               rules.push_back(HostRule(HostRule::HCA_ADDNICK, mask, ports, tag->getString("prefix"), tag->getString("suffix")));
+                       }
+                       else if (stdalgo::string::equalsci(action, "set"))
+                       {
+                               // Ensure that we have the <hostchange:value> parameter.
+                               const std::string value = tag->getString("value");
+                               if (value.empty())
+                                       throw ModuleException("<hostchange:value> is a mandatory field when using the 'set' action, at " + tag->getTagLocation());
+
+                               // The hostname is in the format <value>.
+                               rules.push_back(HostRule(mask, value, ports));
+                               continue;
+                       }
+                       else
+                       {
+                               throw ModuleException(action + " is an invalid <hostchange:action> type, at " + tag->getTagLocation());
+                       }
                }
+
+               const std::string hmap = ServerInstance->Config->ConfValue("hostname")->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789");
+               hostmap.reset();
+               for (std::string::const_iterator iter = hmap.begin(); iter != hmap.end(); ++iter)
+                       hostmap.set(static_cast<unsigned char>(*iter));
+               hostrules.swap(rules);
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               // returns the version number of the module to be
-               // listed in /MODULES
-               return Version("Provides masking of user hostnames in a different way to m_cloaking", VF_VENDOR);
+               return Version("Provides rule-based masking of user hostnames", VF_VENDOR);
        }
 
-       virtual void OnUserConnect(LocalUser* user)
+       void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
        {
-               for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
+               for (HostRules::const_iterator iter = hostrules.begin(); iter != hostrules.end(); ++iter)
                {
-                       if (((InspIRCd::MatchCIDR(user->MakeHost(), i->first)) || (InspIRCd::MatchCIDR(user->MakeHostIP(), i->first))))
+                       const HostRule& rule = *iter;
+                       if (!rule.Matches(user))
+                               continue;
+
+                       std::string newhost;
+                       if (rule.GetAction() == HostRule::HCA_ADDACCOUNT)
+                       {
+                               // Retrieve the account name.
+                               const AccountExtItem* accountext = GetAccountExtItem();
+                               const std::string* accountptr = accountext ? accountext->get(user) : NULL;
+                               if (!accountptr)
+                                       continue;
+
+                               // Remove invalid hostname characters.
+                               std::string accountname = CleanName(*accountptr);
+                               if (accountname.empty())
+                                       continue;
+
+                               // Create the hostname.
+                               rule.Wrap(accountname, newhost);
+                       }
+                       else if (rule.GetAction() == HostRule::HCA_ADDNICK)
+                       {
+                               // Remove invalid hostname characters.
+                               const std::string nickname = CleanName(user->nick);
+                               if (nickname.empty())
+                                       continue;
+
+                               // Create the hostname.
+                               rule.Wrap(nickname, newhost);
+                       }
+                       else if (rule.GetAction() == HostRule::HCA_SET)
+                       {
+                               newhost.assign(rule.GetHost());
+                       }
+
+                       if (!newhost.empty())
                        {
-                               const Host& h = i->second;
-
-                               if (!h.ports.empty())
-                               {
-                                       irc::portparser portrange(h.ports, false);
-                                       long portno = -1;
-                                       bool foundany = false;
-
-                                       while ((portno = portrange.GetToken()))
-                                               if (portno == user->GetServerPort())
-                                                       foundany = true;
-
-                                       if (!foundany)
-                                               continue;
-                               }
-
-                               // host of new user matches a hostchange tag's mask
-                               std::string newhost;
-                               if (h.action == Host::HCA_SET)
-                               {
-                                       newhost = h.newhost;
-                               }
-                               else if (h.action == Host::HCA_SUFFIX)
-                               {
-                                       newhost = MySuffix;
-                               }
-                               else if (h.action == Host::HCA_ADDNICK)
-                               {
-                                       // first take their nick and strip out non-dns, leaving just [A-Z0-9\-]
-                                       std::string complete;
-                                       for (std::string::const_iterator j = user->nick.begin(); j != user->nick.end(); ++j)
-                                       {
-                                               if  (((*j >= 'A') && (*j <= 'Z')) ||
-                                                   ((*j >= 'a') && (*j <= 'z')) ||
-                                                   ((*j >= '0') && (*j <= '9')) ||
-                                                   (*j == '-'))
-                                               {
-                                                       complete = complete + *j;
-                                               }
-                                       }
-                                       if (complete.empty())
-                                               complete = "i-have-a-lame-nick";
-
-                                       if (!MyPrefix.empty())
-                                               newhost = MyPrefix + MySeparator + complete;
-                                       else
-                                               newhost = complete + MySeparator + MySuffix;
-                               }
-                               if (!newhost.empty())
-                               {
-                                       user->WriteServ("NOTICE "+user->nick+" :Setting your virtual host: " + newhost);
-                                       if (!user->ChangeDisplayedHost(newhost.c_str()))
-                                               user->WriteServ("NOTICE "+user->nick+" :Could not set your virtual host: " + newhost);
-                                       return;
-                               }
+                               user->WriteNotice("Setting your virtual host: " + newhost);
+                               if (!user->ChangeDisplayedHost(newhost))
+                                       user->WriteNotice("Could not set your virtual host: " + newhost);
+                               return;
                        }
                }
        }
diff --git a/src/modules/m_hostcycle.cpp b/src/modules/m_hostcycle.cpp
new file mode 100644 (file)
index 0000000..a3c81df
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *   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
+ * 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 ModuleHostCycle : public Module
+{
+       Cap::Reference chghostcap;
+       const std::string quitmsghost;
+       const std::string quitmsgident;
+
+       /** Send fake quit/join/mode messages for host or ident cycle.
+        */
+       void DoHostCycle(User* user, const std::string& newident, const std::string& newhost, const std::string& reason)
+       {
+               // The user has the original ident/host at the time this function is called
+               ClientProtocol::Messages::Quit quitmsg(user, reason);
+               ClientProtocol::Event quitevent(ServerInstance->GetRFCEvents().quit, quitmsg);
+
+               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) && (!chghostcap.get(u)))
+                       {
+                               if (i->second)
+                               {
+                                       u->already_sent = seen_id;
+                                       u->Send(quitevent);
+                               }
+                               else
+                               {
+                                       u->already_sent = silent_id;
+                               }
+                       }
+               }
+
+               std::string newfullhost = user->nick + "!" + newident + "@" + newhost;
+
+               for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
+               {
+                       Membership* memb = *i;
+                       Channel* c = memb->chan;
+
+                       ClientProtocol::Events::Join joinevent(memb, newfullhost);
+
+                       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)
+                               {
+                                       u->Send(quitevent);
+                                       u->already_sent = seen_id;
+                               }
+
+                               u->Send(joinevent);
+                       }
+               }
+       }
+
+ public:
+       ModuleHostCycle()
+               : chghostcap(this, "chghost")
+               , quitmsghost("Changing host")
+               , quitmsgident("Changing ident")
+       {
+       }
+
+       void OnChangeIdent(User* user, const std::string& newident) CXX11_OVERRIDE
+       {
+               DoHostCycle(user, newident, user->GetDisplayedHost(), quitmsgident);
+       }
+
+       void OnChangeHost(User* user, const std::string& newhost) CXX11_OVERRIDE
+       {
+               DoHostCycle(user, user->ident, newhost, quitmsghost);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Cycles users in all their channels when their host or ident changes", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleHostCycle)
index cb17a0383663d9d086c1574791e54f87c9ffdb21..f9e5bc0fdca282acb328208c51deef4780847e02 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/// $CompilerFlags: -Ivendor_directory("http_parser")
 
-#include "inspircd.h"
-#include "httpd.h"
 
-/* $ModDesc: Provides HTTP serving facilities to modules */
-/* $ModDep: httpd.h */
+#include "inspircd.h"
+#include "iohook.h"
+#include "modules/httpd.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__
+# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))
+#  pragma GCC diagnostic ignored "-Wpedantic"
+# else
+#  pragma GCC diagnostic ignored "-pedantic"
+# endif
+#endif
+
+// Fix warnings about shadowing in http_parser.
+#ifdef __GNUC__
+# pragma GCC diagnostic ignored "-Wshadow"
+#endif
+
+#include <http_parser.c>
 
 class ModuleHttpServer;
 
 static ModuleHttpServer* HttpModule;
-static bool claimed;
-static std::set<HttpServerSocket*> sockets;
-
-/** HTTP socket states
- */
-enum HttpState
-{
-       HTTP_SERVE_WAIT_REQUEST = 0, /* Waiting for a full request */
-       HTTP_SERVE_RECV_POSTDATA = 1, /* Waiting to finish recieving POST data */
-       HTTP_SERVE_SEND_DATA = 2 /* Sending response */
-};
+static insp::intrusive_list<HttpServerSocket> sockets;
+static Events::ModuleEventProvider* aclevprov;
+static Events::ModuleEventProvider* reqevprov;
+static http_parser_settings parser_settings;
 
 /** 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;
+       friend ModuleHttpServer;
 
-       HTTPHeaders headers;
-       std::string reqbuffer;
-       std::string postdata;
-       unsigned int postsize;
-       std::string request_type;
+       http_parser parser;
+       http_parser_url url;
+       std::string ip;
        std::string uri;
-       std::string http_version;
+       HTTPHeaders headers;
+       std::string body;
+       size_t total_buffers;
+       int status_code;
 
- 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
        {
-               InternalState = HTTP_SERVE_WAIT_REQUEST;
+               AddToCull();
+               return false;
+       }
 
-               FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
-               if (GetIOHook())
-                       GetIOHook()->OnStreamSocketAccept(this, client, server);
+       template<int (HttpServerSocket::*f)()>
+       static int Callback(http_parser* p)
+       {
+               HttpServerSocket* sock = static_cast<HttpServerSocket*>(p->data);
+               return (sock->*f)();
        }
 
-       ~HttpServerSocket()
+       template<int (HttpServerSocket::*f)(const char*, size_t)>
+       static int DataCallback(http_parser* p, const char* buf, size_t len)
        {
-               sockets.erase(this);
+               HttpServerSocket* sock = static_cast<HttpServerSocket*>(p->data);
+               return (sock->*f)(buf, len);
        }
 
-       virtual void OnError(BufferedSocketError)
+       static void ConfigureParser()
        {
-               ServerInstance->GlobalCulls.AddItem(this);
+               http_parser_settings_init(&parser_settings);
+               parser_settings.on_message_begin = Callback<&HttpServerSocket::OnMessageBegin>;
+               parser_settings.on_url = DataCallback<&HttpServerSocket::OnUrl>;
+               parser_settings.on_header_field = DataCallback<&HttpServerSocket::OnHeaderField>;
+               parser_settings.on_body = DataCallback<&HttpServerSocket::OnBody>;
+               parser_settings.on_message_complete = Callback<&HttpServerSocket::OnMessageComplete>;
+       }
+
+       int OnMessageBegin()
+       {
+               uri.clear();
+               header_state = HEADER_NONE;
+               body.clear();
+               total_buffers = 0;
+               return 0;
+       }
+
+       bool AcceptData(size_t len)
+       {
+               total_buffers += len;
+               return total_buffers < 8192;
+       }
+
+       int OnUrl(const char* buf, size_t len)
+       {
+               if (!AcceptData(len))
+               {
+                       status_code = HTTP_STATUS_URI_TOO_LONG;
+                       return -1;
+               }
+               uri.append(buf, len);
+               return 0;
+       }
+
+       enum { HEADER_NONE, HEADER_FIELD, HEADER_VALUE } header_state;
+       std::string header_field;
+       std::string header_value;
+
+       void OnHeaderComplete()
+       {
+               headers.SetHeader(header_field, header_value);
+               header_field.clear();
+               header_value.clear();
+       }
+
+       int OnHeaderField(const char* buf, size_t len)
+       {
+               if (header_state == HEADER_VALUE)
+                       OnHeaderComplete();
+               header_state = HEADER_FIELD;
+               if (!AcceptData(len))
+               {
+                       status_code = HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+                       return -1;
+               }
+               header_field.append(buf, len);
+               return 0;
        }
 
-       std::string Response(int response)
+       int OnHeaderValue(const char* buf, size_t len)
        {
-               switch (response)
+               header_state = HEADER_VALUE;
+               if (!AcceptData(len))
                {
-                       case 100:
-                               return "CONTINUE";
-                       case 101:
-                               return "SWITCHING PROTOCOLS";
-                       case 200:
-                               return "OK";
-                       case 201:
-                               return "CREATED";
-                       case 202:
-                               return "ACCEPTED";
-                       case 203:
-                               return "NON-AUTHORITATIVE INFORMATION";
-                       case 204:
-                               return "NO CONTENT";
-                       case 205:
-                               return "RESET CONTENT";
-                       case 206:
-                               return "PARTIAL CONTENT";
-                       case 300:
-                               return "MULTIPLE CHOICES";
-                       case 301:
-                               return "MOVED PERMANENTLY";
-                       case 302:
-                               return "FOUND";
-                       case 303:
-                               return "SEE OTHER";
-                       case 304:
-                               return "NOT MODIFIED";
-                       case 305:
-                               return "USE PROXY";
-                       case 307:
-                               return "TEMPORARY REDIRECT";
-                       case 400:
-                               return "BAD REQUEST";
-                       case 401:
-                               return "UNAUTHORIZED";
-                       case 402:
-                               return "PAYMENT REQUIRED";
-                       case 403:
-                               return "FORBIDDEN";
-                       case 404:
-                               return "NOT FOUND";
-                       case 405:
-                               return "METHOD NOT ALLOWED";
-                       case 406:
-                               return "NOT ACCEPTABLE";
-                       case 407:
-                               return "PROXY AUTHENTICATION REQUIRED";
-                       case 408:
-                               return "REQUEST TIMEOUT";
-                       case 409:
-                               return "CONFLICT";
-                       case 410:
-                               return "GONE";
-                       case 411:
-                               return "LENGTH REQUIRED";
-                       case 412:
-                               return "PRECONDITION FAILED";
-                       case 413:
-                               return "REQUEST ENTITY TOO LARGE";
-                       case 414:
-                               return "REQUEST-URI TOO LONG";
-                       case 415:
-                               return "UNSUPPORTED MEDIA TYPE";
-                       case 416:
-                               return "REQUESTED RANGE NOT SATISFIABLE";
-                       case 417:
-                               return "EXPECTATION FAILED";
-                       case 500:
-                               return "INTERNAL SERVER ERROR";
-                       case 501:
-                               return "NOT IMPLEMENTED";
-                       case 502:
-                               return "BAD GATEWAY";
-                       case 503:
-                               return "SERVICE UNAVAILABLE";
-                       case 504:
-                               return "GATEWAY TIMEOUT";
-                       case 505:
-                               return "HTTP VERSION NOT SUPPORTED";
-                       default:
-                               return "WTF";
-                       break;
+                       status_code = HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+                       return -1;
+               }
+               header_value.append(buf, len);
+               return 0;
+       }
+
+       int OnHeadersComplete()
+       {
+               if (header_state != HEADER_NONE)
+                       OnHeaderComplete();
+               return 0;
+       }
+
+       int OnBody(const char* buf, size_t len)
+       {
+               if (!AcceptData(len))
+               {
+                       status_code = HTTP_STATUS_PAYLOAD_TOO_LARGE;
+                       return -1;
+               }
+               body.append(buf, len);
+               return 0;
+       }
+
+       int OnMessageComplete()
+       {
+               ServeData();
+               return 0;
+       }
 
+ 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)
+               , ip(IP)
+               , status_code(0)
+               , waitingcull(false)
+       {
+               if ((!via->iohookprovs.empty()) && (via->iohookprovs.back()))
+               {
+                       via->iohookprovs.back()->OnAccept(this, client, server);
+                       // IOHook may have errored
+                       if (!getError().empty())
+                       {
+                               AddToCull();
+                               return;
+                       }
                }
+
+               parser.data = this;
+               http_parser_init(&parser, HTTP_REQUEST);
+               ServerInstance->Timers.AddTimer(this);
+       }
+
+       ~HttpServerSocket()
+       {
+               sockets.erase(this);
+       }
+
+       void OnError(BufferedSocketError) CXX11_OVERRIDE
+       {
+               AddToCull();
        }
 
-       void SendHTTPError(int response)
+       void SendHTTPError(unsigned int response)
        {
                HTTPHeaders empty;
-               std::string data = "<html><head></head><body>Server error "+ConvToStr(response)+": "+Response(response)+"<br>"+
-                                  "<small>Powered by <a href='http://www.inspircd.org'>InspIRCd</a></small></body></html>";
+               std::string data = InspIRCd::Format(
+                       "<html><head></head><body>Server error %u: %s<br>"
+                       "<small>Powered by <a href='https://www.inspircd.org'>InspIRCd</a></small></body></html>", response, http_status_str((http_status)response));
 
                SendHeaders(data.length(), response, empty);
                WriteData(data);
+               Close();
        }
 
-       void SendHeaders(unsigned long size, int response, HTTPHeaders &rheaders)
+       void SendHeaders(unsigned long size, unsigned int response, HTTPHeaders &rheaders)
        {
+               WriteData(InspIRCd::Format("HTTP/%u.%u %u %s\r\n", parser.http_major ? parser.http_major : 1, parser.http_major ? parser.http_minor : 1, response, http_status_str((http_status)response)));
 
-               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("Server", BRANCH);
+               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));
 
                if (size)
@@ -211,200 +258,145 @@ class HttpServerSocket : public BufferedSocket
                WriteData("\r\n");
        }
 
-       void OnDataReady()
+       void OnDataReady() CXX11_OVERRIDE
        {
-               if (InternalState == HTTP_SERVE_RECV_POSTDATA)
-               {
-                       postdata.append(recvq);
-                       if (postdata.length() >= postsize)
-                               ServeData();
-               }
-               else
-               {
-                       reqbuffer.append(recvq);
-
-                       if (reqbuffer.length() >= 8192)
-                       {
-                               ServerInstance->Logs->Log("m_httpd",DEBUG, "m_httpd dropped connection due to an oversized request buffer");
-                               reqbuffer.clear();
-                               SetError("Buffer");
-                       }
-
-                       if (InternalState == HTTP_SERVE_WAIT_REQUEST)
-                               CheckRequestBuffer();
-               }
+               if (parser.upgrade || HTTP_PARSER_ERRNO(&parser))
+                       return;
+               http_parser_execute(&parser, &parser_settings, recvq.data(), recvq.size());
+               if (parser.upgrade || HTTP_PARSER_ERRNO(&parser))
+                       SendHTTPError(status_code ? status_code : 400);
        }
 
-       void CheckRequestBuffer()
+       void ServeData()
        {
-               std::string::size_type reqend = reqbuffer.find("\r\n\r\n");
-               if (reqend == std::string::npos)
-                       return;
-
-               // We have the headers; parse them all
-               std::string::size_type hbegin = 0, hend;
-               while ((hend = reqbuffer.find("\r\n", hbegin)) != std::string::npos)
+               ModResult MOD_RESULT;
+               std::string method = http_method_str(static_cast<http_method>(parser.method));
+               HTTPRequestURI parsed;
+               ParseURI(uri, parsed);
+               HTTPRequest acl(method, parsed, &headers, this, ip, body);
+               FIRST_MOD_RESULT_CUSTOM(*aclevprov, HTTPACLEventListener, OnHTTPACLCheck, MOD_RESULT, (acl));
+               if (MOD_RESULT != MOD_RES_DENY)
                {
-                       if (hbegin == hend)
-                               break;
-
-                       if (request_type.empty())
+                       HTTPRequest url(method, parsed, &headers, this, ip, body);
+                       FIRST_MOD_RESULT_CUSTOM(*reqevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (url));
+                       if (MOD_RESULT == MOD_RES_PASSTHRU)
                        {
-                               std::istringstream cheader(std::string(reqbuffer, hbegin, hend - hbegin));
-                               cheader >> request_type;
-                               cheader >> uri;
-                               cheader >> http_version;
-
-                               if (request_type.empty() || uri.empty() || http_version.empty())
-                               {
-                                       SendHTTPError(400);
-                                       return;
-                               }
-
-                               hbegin = hend + 2;
-                               continue;
+                               SendHTTPError(404);
                        }
+               }
+       }
 
-                       std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
+       void Page(std::stringstream* n, unsigned int response, HTTPHeaders* hheaders)
+       {
+               SendHeaders(n->str().length(), response, *hheaders);
+               WriteData(n->str());
+               Close();
+       }
 
-                       std::string::size_type fieldsep = cheader.find(':');
-                       if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
-                       {
-                               SendHTTPError(400);
-                               return;
-                       }
+       void AddToCull()
+       {
+               if (waitingcull)
+                       return;
 
-                       headers.SetHeader(cheader.substr(0, fieldsep), cheader.substr(fieldsep + 2));
+               waitingcull = true;
+               Close();
+               ServerInstance->GlobalCulls.AddItem(this);
+       }
 
-                       hbegin = hend + 2;
-               }
+       bool ParseURI(const std::string& uri, HTTPRequestURI& out)
+       {
+               http_parser_url_init(&url);
+               if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &url) != 0)
+                       return false;
 
-               reqbuffer.erase(0, reqend + 4);
+               if (url.field_set & (1 << UF_PATH))
+                       out.path = uri.substr(url.field_data[UF_PATH].off, url.field_data[UF_PATH].len);
 
-               std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
-               std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
+               if (url.field_set & (1 << UF_FRAGMENT))
+                       out.fragment = uri.substr(url.field_data[UF_FRAGMENT].off, url.field_data[UF_FRAGMENT].len);
 
-               if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
-               {
-                       SendHTTPError(505);
-                       return;
-               }
+               std::string param_str;
+               if (url.field_set & (1 << UF_QUERY))
+                       param_str = uri.substr(url.field_data[UF_QUERY].off, url.field_data[UF_QUERY].len);
 
-               if (headers.IsSet("Content-Length") && (postsize = ConvToInt(headers.GetHeader("Content-Length"))) > 0)
+               irc::sepstream param_stream(param_str, '&');
+               std::string token;
+               std::string::size_type eq_pos;
+               while (param_stream.GetToken(token))
                {
-                       InternalState = HTTP_SERVE_RECV_POSTDATA;
-
-                       if (reqbuffer.length() >= postsize)
+                       eq_pos = token.find('=');
+                       if (eq_pos == std::string::npos)
                        {
-                               postdata = reqbuffer.substr(0, postsize);
-                               reqbuffer.erase(0, postsize);
+                               out.query_params.insert(std::make_pair(token, ""));
                        }
-                       else if (!reqbuffer.empty())
+                       else
                        {
-                               postdata = reqbuffer;
-                               reqbuffer.clear();
+                               out.query_params.insert(std::make_pair(token.substr(0, eq_pos), token.substr(eq_pos + 1)));
                        }
-
-                       if (postdata.length() >= postsize)
-                               ServeData();
-
-                       return;
                }
-
-               ServeData();
+               return true;
        }
+};
 
-       void ServeData()
+class HTTPdAPIImpl : public HTTPdAPIBase
+{
+ public:
+       HTTPdAPIImpl(Module* parent)
+               : HTTPdAPIBase(parent)
        {
-               InternalState = HTTP_SERVE_SEND_DATA;
-
-               claimed = false;
-               HTTPRequest acl((Module*)HttpModule, "httpd_acl", request_type, uri, &headers, this, ip, postdata);
-               acl.Send();
-               if (!claimed)
-               {
-                       HTTPRequest url((Module*)HttpModule, "httpd_url", request_type, uri, &headers, this, ip, postdata);
-                       url.Send();
-                       if (!claimed)
-                       {
-                               SendHTTPError(404);
-                       }
-               }
        }
 
-       void Page(std::stringstream* n, int response, HTTPHeaders *hheaders)
+       void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
        {
-               SendHeaders(n->str().length(), response, *hheaders);
-               WriteData(n->str());
-               Close();
+               resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
        }
 };
 
 class ModuleHttpServer : public Module
 {
+       HTTPdAPIImpl APIImpl;
        unsigned int timeoutsec;
+       Events::ModuleEventProvider acleventprov;
+       Events::ModuleEventProvider reqeventprov;
 
  public:
-
-       void init()
+       ModuleHttpServer()
+               : APIImpl(this)
+               , acleventprov(this, "event/http-acl")
+               , reqeventprov(this, "event/http-request")
        {
-               HttpModule = this;
-               Implementation eventlist[] = { I_OnAcceptConnection, I_OnBackgroundTimer, I_OnRehash, I_OnUnloadModule };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
+               aclevprov = &acleventprov;
+               reqevprov = &reqeventprov;
+               HttpServerSocket::ConfigureParser();
        }
 
-       void OnRehash(User* user)
+       void init() CXX11_OVERRIDE
        {
-               ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
-               timeoutsec = tag->getInt("timeout");
+               HttpModule = this;
        }
 
-       void OnRequest(Request& request)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               if (strcmp(request.id, "HTTP-DOC") != 0)
-                       return;
-               HTTPDocumentResponse& resp = static_cast<HTTPDocumentResponse&>(request);
-               claimed = true;
-               resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
+               ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
+               timeoutsec = tag->getDuration("timeout", 10, 1);
        }
 
-       ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+       ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
        {
-               if (from->bind_tag->getString("type") != "httpd")
+               if (!stdalgo::string::equalsci(from->bind_tag->getString("type"), "httpd"))
                        return MOD_RES_PASSTHRU;
-               int port;
-               std::string incomingip;
-               irc::sockets::satoap(*client, incomingip, port);
-               sockets.insert(new HttpServerSocket(nfd, incomingip, from, client, server));
-               return MOD_RES_ALLOW;
-       }
-
-       void OnBackgroundTimer(time_t curtime)
-       {
-               if (!timeoutsec)
-                       return;
 
-               time_t oldest_allowed = curtime - timeoutsec;
-               for (std::set<HttpServerSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); )
-               {
-                       HttpServerSocket* sock = *i;
-                       ++i;
-                       if (sock->createtime < oldest_allowed)
-                       {
-                               sock->cull();
-                               delete sock;
-                       }
-               }
+               sockets.push_front(new HttpServerSocket(nfd, client->addr(), from, client, server, timeoutsec));
+               return MOD_RES_ALLOW;
        }
 
-       void OnUnloadModule(Module* mod)
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE
        {
-               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->GetIOHook() == mod)
+                       if (sock->GetModHook(mod))
                        {
                                sock->cull();
                                delete sock;
@@ -412,20 +404,17 @@ class ModuleHttpServer : public Module
                }
        }
 
-       CullResult cull()
+       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();
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
        }
index c25cabc0ae5eb40cc135ed4acf268718ca835914..9845e5b0ff45bbe6d7e9813b87474a597d177fde 100644 (file)
 
 
 #include "inspircd.h"
-#include "httpd.h"
-#include "protocol.h"
-
-/* $ModDesc: Provides access control lists (passwording of resources, ip restrictions etc) to m_httpd.so dependent modules */
+#include "modules/httpd.h"
 
 class HTTPACL
 {
@@ -37,21 +34,24 @@ class HTTPACL
                const std::string &set_whitelist, const std::string &set_blacklist)
                : path(set_path), username(set_username), password(set_password), whitelist(set_whitelist),
                blacklist(set_blacklist) { }
-
-       ~HTTPACL() { }
 };
 
-class ModuleHTTPAccessList : public Module
+class ModuleHTTPAccessList : public Module, public HTTPACLEventListener
 {
-
        std::string stylesheet;
        std::vector<HTTPACL> acl_list;
+       HTTPdAPI API;
 
  public:
+       ModuleHTTPAccessList()
+               : HTTPACLEventListener(this)
+               , API(this)
+       {
+       }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               acl_list.clear();
+               std::vector<HTTPACL> new_acls;
                ConfigTagList acls = ServerInstance->Config->ConfTags("httpdacl");
                for (ConfigIter i = acls.first; i != acls.second; i++)
                {
@@ -67,16 +67,16 @@ class ModuleHTTPAccessList : public Module
 
                        while (sep.GetToken(type))
                        {
-                               if (type == "password")
+                               if (stdalgo::string::equalsci(type, "password"))
                                {
                                        username = c->getString("username");
                                        password = c->getString("password");
                                }
-                               else if (type == "whitelist")
+                               else if (stdalgo::string::equalsci(type, "whitelist"))
                                {
                                        whitelist = c->getString("whitelist");
                                }
-                               else if (type == "blacklist")
+                               else if (stdalgo::string::equalsci(type, "blacklist"))
                                {
                                        blacklist = c->getString("blacklist");
                                }
@@ -86,42 +86,34 @@ class ModuleHTTPAccessList : public Module
                                }
                        }
 
-                       ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Read ACL: path=%s pass=%s whitelist=%s blacklist=%s", path.c_str(),
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Read ACL: path=%s pass=%s whitelist=%s blacklist=%s", path.c_str(),
                                        password.c_str(), whitelist.c_str(), blacklist.c_str());
 
-                       acl_list.push_back(HTTPACL(path, username, password, whitelist, blacklist));
+                       new_acls.push_back(HTTPACL(path, username, password, whitelist, blacklist));
                }
+               acl_list.swap(new_acls);
        }
 
-       void init()
-       {
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnEvent, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       void BlockAccess(HTTPRequest* http, int returnval, const std::string &extraheaderkey = "", const std::string &extraheaderval="")
+       void BlockAccess(HTTPRequest* http, unsigned int returnval, const std::string &extraheaderkey = "", const std::string &extraheaderval="")
        {
-               ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "BlockAccess (%d)", returnval);
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BlockAccess (%u)", returnval);
 
                std::stringstream data("Access to this resource is denied by an access control list. Please contact your IRC administrator.");
                HTTPDocumentResponse response(this, *http, &data, returnval);
-               response.headers.SetHeader("X-Powered-By", "m_httpd_acl.so");
+               response.headers.SetHeader("X-Powered-By", MODNAME);
                if (!extraheaderkey.empty())
                        response.headers.SetHeader(extraheaderkey, extraheaderval);
-               response.Send();
+               API->SendResponse(response);
        }
 
-       void OnEvent(Event& event)
+       bool IsAccessAllowed(HTTPRequest* http)
        {
-               if (event.id == "httpd_acl")
                {
-                       ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd acl event");
-                       HTTPRequest* http = (HTTPRequest*)&event;
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd acl event");
 
                        for (std::vector<HTTPACL>::const_iterator this_acl = acl_list.begin(); this_acl != acl_list.end(); ++this_acl)
                        {
-                               if (InspIRCd::Match(http->GetURI(), this_acl->path, ascii_case_insensitive_map))
+                               if (InspIRCd::Match(http->GetPath(), this_acl->path, ascii_case_insensitive_map))
                                {
                                        if (!this_acl->blacklist.empty())
                                        {
@@ -133,10 +125,10 @@ class ModuleHTTPAccessList : public Module
                                                {
                                                        if (InspIRCd::Match(http->GetIP(), entry, ascii_case_insensitive_map))
                                                        {
-                                                               ServerInstance->Logs->Log("m_httpd_acl", 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());
+                                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to blacklisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)",
+                                                                               http->GetPath().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), entry.c_str());
                                                                BlockAccess(http, 403);
-                                                               return;
+                                                               return false;
                                                        }
                                                }
                                        }
@@ -155,17 +147,17 @@ class ModuleHTTPAccessList : public Module
 
                                                if (!allow_access)
                                                {
-                                                       ServerInstance->Logs->Log("m_httpd_acl", 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());
+                                                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to whitelisted resource %s (matched by pattern %s) from ip %s (Not in whitelist)",
+                                                                       http->GetPath().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())
                                        {
                                                /* Password auth, first look to see if we have a basic authentication header */
-                                               ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Checking HTTP auth password for resource %s (matched by pattern %s) from ip %s, against username %s",
-                                                               http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), this_acl->username.c_str());
+                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Checking HTTP auth password for resource %s (matched by pattern %s) from ip %s, against username %s",
+                                                               http->GetPath().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), this_acl->username.c_str());
 
                                                if (http->headers->IsSet("Authorization"))
                                                {
@@ -183,7 +175,7 @@ class ModuleHTTPAccessList : public Module
 
                                                                sep.GetToken(base64);
                                                                std::string userpass = Base64ToBin(base64);
-                                                               ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: %s (%s)", userpass.c_str(), base64.c_str());
+                                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "HTTP authorization: %s (%s)", userpass.c_str(), base64.c_str());
 
                                                                irc::sepstream userpasspair(userpass, ':');
                                                                if (userpasspair.GetToken(user))
@@ -193,8 +185,8 @@ class ModuleHTTPAccessList : public Module
                                                                        /* Access granted if username and password are correct */
                                                                        if (user == this_acl->username && pass == this_acl->password)
                                                                        {
-                                                                               ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: password and username match");
-                                                                               return;
+                                                                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "HTTP authorization: password and username match");
+                                                                               return true;
                                                                        }
                                                                        else
                                                                                /* Invalid password */
@@ -213,22 +205,27 @@ 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;
        }
 
-       virtual ~ModuleHTTPAccessList()
+       ModResult OnHTTPACLCheck(HTTPRequest& req) CXX11_OVERRIDE
        {
+               if (IsAccessAllowed(&req))
+                       return MOD_RES_PASSTHRU;
+               return MOD_RES_DENY;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides access control lists (passwording of resources, ip restrictions etc) to m_httpd.so dependent modules", VF_VENDOR);
+               return Version("Provides access control lists (passwording of resources, IP restrictions, etc) to m_httpd dependent modules", VF_VENDOR);
        }
 };
 
index 62314cd7ea0eef94282e3b4c8f94eaf8d0855f4a..25d2f54bf369431b986d82139c8b83419aba9e86 100644 (file)
 
 
 #include "inspircd.h"
-#include "httpd.h"
-#include "protocol.h"
+#include "modules/httpd.h"
 
-/* $ModDesc: Allows for the server configuration to be viewed over HTTP via m_httpd.so */
-
-class ModuleHttpConfig : public Module
+class ModuleHttpConfig : public Module, public HTTPRequestEventListener
 {
+       HTTPdAPI API;
+
  public:
-       void init()
+       ModuleHttpConfig()
+               : HTTPRequestEventListener(this)
+               , API(this)
        {
-               Implementation eventlist[] = { I_OnEvent };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       std::string Sanitize(const std::string &str)
+       ModResult OnHTTPRequest(HTTPRequest& request) CXX11_OVERRIDE
        {
-               std::string ret;
+               if ((request.GetPath() != "/config") && (request.GetPath() != "/config/"))
+                       return MOD_RES_PASSTHRU;
 
-               for (std::string::const_iterator x = str.begin(); x != str.end(); ++x)
-               {
-                       switch (*x)
-                       {
-                               case '<':
-                                       ret += "&lt;";
-                               break;
-                               case '>':
-                                       ret += "&gt;";
-                               break;
-                               case '&':
-                                       ret += "&amp;";
-                               break;
-                               case '"':
-                                       ret += "&quot;";
-                               break;
-                               default:
-                                       if (*x < 32 || *x > 126)
-                                       {
-                                               int n = *x;
-                                               ret += ("&#" + ConvToStr(n) + ";");
-                                       }
-                                       else
-                                               ret += *x;
-                               break;
-                       }
-               }
-               return ret;
-       }
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling request for the HTTP /config route");
+               std::stringstream buffer;
 
-       void OnEvent(Event& event)
-       {
-               std::stringstream data("");
-
-               if (event.id == "httpd_url")
+               ConfigDataHash& config = ServerInstance->Config->config_data;
+               for (ConfigDataHash::const_iterator citer = config.begin(); citer != config.end(); ++citer)
                {
-                       ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd event");
-                       HTTPRequest* http = (HTTPRequest*)&event;
+                       // Show the location of the tag in a comment.
+                       ConfigTag* tag = citer->second;
+                       buffer << "# " << tag->getTagLocation() << std::endl
+                               << '<' << tag->tag << ' ';
 
-                       if ((http->GetURI() == "/config") || (http->GetURI() == "/config/"))
+                       // Print out the tag with all keys aligned vertically.
+                       const std::string indent(tag->tag.length() + 2, ' ');
+                       const ConfigItems& items = tag->getItems();
+                       for (ConfigItems::const_iterator kiter = items.begin(); kiter != items.end(); )
                        {
-                               data << "<html><head><title>InspIRCd Configuration</title></head><body>";
-                               data << "<h1>InspIRCd Configuration</h1><p>";
-
-                               for (ConfigDataHash::iterator x = ServerInstance->Config->config_data.begin(); x != ServerInstance->Config->config_data.end(); ++x)
-                               {
-                                       data << "&lt;" << x->first << " ";
-                                       ConfigTag* tag = x->second;
-                                       for (std::vector<KeyVal>::const_iterator j = tag->getItems().begin(); j != tag->getItems().end(); j++)
-                                       {
-                                               data << Sanitize(j->first) << "=&quot;" << Sanitize(j->second) << "&quot; ";
-                                       }
-                                       data << "&gt;<br>";
-                               }
-
-                               data << "</body></html>";
-                               /* Send the document back to m_httpd */
-                               HTTPDocumentResponse response(this, *http, &data, 200);
-                               response.headers.SetHeader("X-Powered-By", "m_httpd_config.so");
-                               response.headers.SetHeader("Content-Type", "text/html");
-                               response.Send();
+                               ConfigItems::const_iterator curr = kiter++;
+                               buffer << curr->first << "=\"" << ServerConfig::Escape(curr->second) << '"';
+                               if (kiter != items.end())
+                                       buffer << std::endl << indent;
                        }
+                       buffer << '>' << std::endl << std::endl;
                }
-       }
 
-       virtual ~ModuleHttpConfig()
-       {
+               HTTPDocumentResponse response(this, request, &buffer, 200);
+               response.headers.SetHeader("X-Powered-By", MODNAME);
+               response.headers.SetHeader("Content-Type", "text/plain");
+               API->SendResponse(response);
+               return MOD_RES_DENY;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Allows for the server configuration to be viewed over HTTP via m_httpd.so", VF_VENDOR);
+               return Version("Allows for the server configuration to be viewed over HTTP via m_httpd", VF_VENDOR);
        }
 };
 
index e17bf514f0835d494e57f60074bd5e30b009a55a..3be8ec9703562225257e496016a01a5a81d964ac 100644 (file)
 
 
 #include "inspircd.h"
-#include "httpd.h"
+#include "modules/httpd.h"
 #include "xline.h"
-#include "protocol.h"
 
-/* $ModDesc: Provides statistics over HTTP via m_httpd.so */
-
-class ModuleHttpStats : public Module
+namespace Stats
 {
-       static std::map<char, char const*> const &entities;
-
- public:
+       struct Entities
+       {
+               static const insp::flat_map<char, char const*>& entities;
+       };
 
-       void init()
+       static const insp::flat_map<char, char const*>& init_entities()
        {
-               Implementation eventlist[] = { I_OnEvent };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               static insp::flat_map<char, char const*> entities;
+               entities['<'] = "lt";
+               entities['>'] = "gt";
+               entities['&'] = "amp";
+               entities['"'] = "quot";
+               return entities;
        }
 
-       std::string Sanitize(const std::string &str)
+       const insp::flat_map<char, char const*>& Entities::entities = init_entities();
+
+       std::string Sanitize(const std::string& str)
        {
                std::string ret;
                ret.reserve(str.length() * 2);
 
                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::entities.find(*x);
 
-                       if (it != entities.end())
+                       if (it != Entities::entities.end())
                        {
                                ret += '&';
                                ret += it->second;
                                ret += ';';
                        }
-                       else if (*x == 0x09 ||  *x == 0x0A || *x == 0x0D || ((*x >= 0x20) && (*x <= 0x7e)))
+                       else if (*x == 0x09 || *x == 0x0A || *x == 0x0D || ((*x >= 0x20) && (*x <= 0x7e)))
                        {
                                // The XML specification defines the following characters as valid inside an XML document:
                                // Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
@@ -76,10 +80,10 @@ class ModuleHttpStats : public Module
                return ret;
        }
 
-       void DumpMeta(std::stringstream& data, Extensible* ext)
+       void DumpMeta(std::ostream& data, Extensible* ext)
        {
                data << "<metadata>";
-               for(Extensible::ExtensibleStore::const_iterator i = ext->GetExtList().begin(); i != ext->GetExtList().end(); i++)
+               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);
@@ -91,167 +95,390 @@ class ModuleHttpStats : public Module
                data << "</metadata>";
        }
 
-       void OnEvent(Event& event)
+       std::ostream& ServerInfo(std::ostream& data)
        {
-               std::stringstream data("");
+               return data << "<server><name>" << ServerInstance->Config->ServerName << "</name><description>"
+                       << Sanitize(ServerInstance->Config->ServerDesc) << "</description><version>"
+                       << Sanitize(ServerInstance->GetVersionString(true)) << "</version></server>";
+       }
 
-               if (event.id == "httpd_url")
+       std::ostream& ISupport(std::ostream& data)
+       {
+               data << "<isupport>";
+               const std::vector<Numeric::Numeric>& isupport = ServerInstance->ISupport.GetLines();
+               for (std::vector<Numeric::Numeric>::const_iterator i = isupport.begin(); i != isupport.end(); ++i)
                {
-                       ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd event");
-                       HTTPRequest* http = (HTTPRequest*)&event;
+                       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>";
+               }
+               return data << "</isupport>";
+       }
 
-                       if ((http->GetURI() == "/stats") || (http->GetURI() == "/stats/"))
+       std::ostream& General(std::ostream& data)
+       {
+               data << "<general>";
+               data << "<usercount>" << ServerInstance->Users->GetUsers().size() << "</usercount>";
+               data << "<localusercount>" << ServerInstance->Users->GetLocalUsers().size() << "</localusercount>";
+               data << "<channelcount>" << ServerInstance->GetChans().size() << "</channelcount>";
+               data << "<opercount>" << ServerInstance->Users->all_opers.size() << "</opercount>";
+               data << "<socketcount>" << (SocketEngine::GetUsedFds()) << "</socketcount><socketmax>" << SocketEngine::GetMaxFds() << "</socketmax>";
+               data << "<uptime><boot_time_t>" << ServerInstance->startup_time << "</boot_time_t></uptime>";
+               data << "<currenttime>" << ServerInstance->Time() << "</currenttime>";
+
+               data << ISupport;
+               return data << "</general>";
+       }
+
+       std::ostream& XLines(std::ostream& data)
+       {
+               data << "<xlines>";
+               std::vector<std::string> xltypes = ServerInstance->XLines->GetAllTypes();
+               for (std::vector<std::string>::iterator it = xltypes.begin(); it != xltypes.end(); ++it)
+               {
+                       XLineLookup* lookup = ServerInstance->XLines->GetAll(*it);
+
+                       if (!lookup)
+                               continue;
+                       for (LookupIter i = lookup->begin(); i != lookup->end(); ++i)
                        {
-                               data << "<inspircdstats><server><name>" << ServerInstance->Config->ServerName << "</name><gecos>"
-                                       << Sanitize(ServerInstance->Config->ServerDesc) << "</gecos><version>"
-                                       << Sanitize(ServerInstance->GetVersionString()) << "</version></server>";
-
-                               data << "<general>";
-                               data << "<usercount>" << ServerInstance->Users->clientlist->size() << "</usercount>";
-                               data << "<channelcount>" << ServerInstance->chanlist->size() << "</channelcount>";
-                               data << "<opercount>" << ServerInstance->Users->all_opers.size() << "</opercount>";
-                               data << "<socketcount>" << (ServerInstance->SE->GetUsedFds()) << "</socketcount><socketmax>" << ServerInstance->SE->GetMaxFds() << "</socketmax><socketengine>" << ServerInstance->SE->GetName() << "</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 << "<isupport>" << Sanitize(ServerInstance->Config->data005) << "</isupport></general><xlines>";
-                               std::vector<std::string> xltypes = ServerInstance->XLines->GetAllTypes();
-                               for (std::vector<std::string>::iterator it = xltypes.begin(); it != xltypes.end(); ++it)
-                               {
-                                       XLineLookup* lookup = ServerInstance->XLines->GetAll(*it);
-
-                                       if (!lookup)
-                                               continue;
-                                       for (LookupIter i = lookup->begin(); i != lookup->end(); ++i)
-                                       {
-                                               data << "<xline type=\"" << it->c_str() << "\"><mask>"
-                                                       << Sanitize(i->second->Displayable()) << "</mask><settime>"
-                                                       << i->second->set_time << "</settime><duration>" << i->second->duration
-                                                       << "</duration><reason>" << Sanitize(i->second->reason)
-                                                       << "</reason></xline>";
-                                       }
-                               }
-
-                               data << "</xlines><modulelist>";
-                               std::vector<std::string> module_names = ServerInstance->Modules->GetAllModuleNames(0);
-
-                               for (std::vector<std::string>::iterator i = module_names.begin(); i != module_names.end(); ++i)
-                               {
-                                       Module* m = ServerInstance->Modules->Find(i->c_str());
-                                       Version v = m->GetVersion();
-                                       data << "<module><name>" << *i << "</name><description>" << Sanitize(v.description) << "</description></module>";
-                               }
-                               data << "</modulelist><channellist>";
-
-                               for (chan_hash::const_iterator a = ServerInstance->chanlist->begin(); a != ServerInstance->chanlist->end(); ++a)
-                               {
-                                       Channel* c = a->second;
-
-                                       data << "<channel>";
-                                       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)
-                                       {
-                                               Membership* memb = x->second;
-                                               data << "<channelmember><uid>" << memb->user->uuid << "</uid><privs>"
-                                                       << Sanitize(c->GetAllPrefixChars(x->first)) << "</privs><modes>"
-                                                       << memb->modes << "</modes>";
-                                               DumpMeta(data, memb);
-                                               data << "</channelmember>";
-                                       }
-
-                                       DumpMeta(data, c);
-
-                                       data << "</channel>";
-                               }
-
-                               data << "</channellist><userlist>";
-
-                               for (user_hash::const_iterator a = ServerInstance->Users->clientlist->begin(); a != ServerInstance->Users->clientlist->end(); ++a)
-                               {
-                                       User* u = a->second;
-
-                                       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>";
-                                       if (IS_AWAY(u))
-                                               data << "<away>" << Sanitize(u->awaymsg) << "</away><awaytime>" << u->awaytime << "</awaytime>";
-                                       if (IS_OPER(u))
-                                               data << "<opertype>" << Sanitize(u->oper->NameStr()) << "</opertype>";
-                                       data << "<modes>" << u->FormatModes() << "</modes><ident>" << Sanitize(u->ident) << "</ident>";
-                                       LocalUser* lu = IS_LOCAL(u);
-                                       if (lu)
-                                               data << "<port>" << lu->GetServerPort() << "</port><servaddr>"
-                                                       << irc::sockets::satouser(lu->server_sa) << "</servaddr>";
-                                       data << "<ipaddress>" << u->GetIPString() << "</ipaddress>";
-
-                                       DumpMeta(data, u);
-
-                                       data << "</user>";
-                               }
-
-                               data << "</userlist><serverlist>";
-
-                               ProtoServerList sl;
-                               ServerInstance->PI->GetServerList(sl);
-
-                               for (ProtoServerList::iterator b = sl.begin(); b != sl.end(); ++b)
-                               {
-                                       data << "<server>";
-                                       data << "<servername>" << b->servername << "</servername>";
-                                       data << "<parentname>" << b->parentname << "</parentname>";
-                                       data << "<gecos>" << Sanitize(b->gecos) << "</gecos>";
-                                       data << "<usercount>" << b->usercount << "</usercount>";
+                               data << "<xline type=\"" << it->c_str() << "\"><mask>"
+                                       << Sanitize(i->second->Displayable()) << "</mask><settime>"
+                                       << i->second->set_time << "</settime><duration>" << i->second->duration
+                                       << "</duration><reason>" << Sanitize(i->second->reason)
+                                       << "</reason></xline>";
+                       }
+               }
+               return data << "</xlines>";
+       }
+
+       std::ostream& Modules(std::ostream& data)
+       {
+               data << "<modulelist>";
+               const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+
+               for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+               {
+                       Version v = i->second->GetVersion();
+                       data << "<module><name>" << i->first << "</name><description>" << Sanitize(v.description) << "</description></module>";
+               }
+               return data << "</modulelist>";
+       }
+
+       std::ostream& Channels(std::ostream& data)
+       {
+               data << "<channellist>";
+
+               const chan_hash& chans = ServerInstance->GetChans();
+               for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+               {
+                       Channel* c = i->second;
+
+                       data << "<channel>";
+                       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 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>"
+                                       << Sanitize(memb->GetAllPrefixChars()) << "</privs><modes>"
+                                       << memb->modes << "</modes>";
+                               DumpMeta(data, memb);
+                               data << "</channelmember>";
+                       }
+
+                       DumpMeta(data, c);
+
+                       data << "</channel>";
+               }
+
+               return data << "</channellist>";
+       }
+
+       std::ostream& DumpUser(std::ostream& data, User* u)
+       {
+               data << "<user>";
+               data << "<nickname>" << u->nick << "</nickname><uuid>" << u->uuid << "</uuid><realhost>"
+                       << u->GetRealHost() << "</realhost><displayhost>" << u->GetDisplayedHost() << "</displayhost><realname>"
+                       << Sanitize(u->GetRealName()) << "</realname><server>" << u->server->GetName() << "</server><signon>"
+                       << u->signon << "</signon><age>" << u->age << "</age>";
+
+               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->GetModeLetters().substr(1) << "</modes><ident>" << Sanitize(u->ident) << "</ident>";
+
+               LocalUser* lu = IS_LOCAL(u);
+               if (lu)
+                       data << "<local/><port>" << lu->server_sa.port() << "</port><servaddr>"
+                               << lu->server_sa.str() << "</servaddr><connectclass>"
+                               << lu->GetClass()->GetName() << "</connectclass><lastmsg>"
+                               << lu->idle_lastmsg << "</lastmsg>";
+
+               data << "<ipaddress>" << u->GetIPString() << "</ipaddress>";
+
+               DumpMeta(data, u);
+
+               data << "</user>";
+               return data;
+       }
+
+       std::ostream& Users(std::ostream& data)
+       {
+               data << "<userlist>";
+               const user_hash& users = ServerInstance->Users->GetUsers();
+               for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+               {
+                       User* u = i->second;
+
+                       if (u->registered != REG_ALL)
+                               continue;
+
+                       DumpUser(data, u);
+               }
+               return data << "</userlist>";
+       }
+
+       std::ostream& Servers(std::ostream& data)
+       {
+               data << "<serverlist>";
+
+               ProtocolInterface::ServerList sl;
+               ServerInstance->PI->GetServerList(sl);
+
+               for (ProtocolInterface::ServerList::const_iterator b = sl.begin(); b != sl.end(); ++b)
+               {
+                       data << "<server>";
+                       data << "<servername>" << b->servername << "</servername>";
+                       data << "<parentname>" << b->parentname << "</parentname>";
+                       data << "<description>" << Sanitize(b->description) << "</description>";
+                       data << "<usercount>" << b->usercount << "</usercount>";
 // This is currently not implemented, so, commented out.
 //                                     data << "<opercount>" << b->opercount << "</opercount>";
-                                       data << "<lagmillisecs>" << b->latencyms << "</lagmillisecs>";
-                                       data << "</server>";
-                               }
+                       data << "<lagmillisecs>" << b->latencyms << "</lagmillisecs>";
+                       data << "</server>";
+               }
+
+               return data << "</serverlist>";
+       }
+
+       std::ostream& Commands(std::ostream& data)
+       {
+               data << "<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>";
+               }
+               return data << "</commandlist>";
+       }
+
+       enum OrderBy
+       {
+               OB_NICK,
+               OB_LASTMSG,
 
-                               data << "</serverlist></inspircdstats>";
+               OB_NONE
+       };
+
+       struct UserSorter
+       {
+               OrderBy order;
+               bool desc;
+
+               UserSorter(OrderBy Order, bool Desc = false) : order(Order), desc(Desc) {}
+
+               template <typename T>
+               inline bool Compare(const T& a, const T& b)
+               {
+                       return desc ? a > b : a < b;
+               }
 
-                               /* Send the document back to m_httpd */
-                               HTTPDocumentResponse response(this, *http, &data, 200);
-                               response.headers.SetHeader("X-Powered-By", "m_httpd_stats.so");
-                               response.headers.SetHeader("Content-Type", "text/xml");
-                               response.Send();
+               bool operator()(User* u1, User* u2)
+               {
+                       switch (order) {
+                               case OB_LASTMSG:
+                                       return Compare(IS_LOCAL(u1)->idle_lastmsg, IS_LOCAL(u2)->idle_lastmsg);
+                                       break;
+                               case OB_NICK:
+                                       return Compare(u1->nick, u2->nick);
+                                       break;
+                               default:
+                               case OB_NONE:
+                                       return false;
+                                       break;
                        }
                }
+       };
+
+       std::ostream& ListUsers(std::ostream& data, const HTTPQueryParameters& params)
+       {
+               if (params.empty())
+                       return Users(data);
+
+               data << "<userlist>";
+
+               // Filters
+               size_t limit = params.getNum<size_t>("limit");
+               bool showunreg = params.getBool("showunreg");
+               bool localonly = params.getBool("localonly");
+
+               // Minimum time since a user's last message
+               unsigned long min_idle = params.getDuration("minidle");
+               time_t maxlastmsg = ServerInstance->Time() - min_idle;
+
+               if (min_idle)
+                       // We can only check idle times on local users
+                       localonly = true;
+
+               // Sorting
+               const std::string& sortmethod = params.getString("sortby");
+               bool desc = params.getBool("desc", false);
+
+               OrderBy orderby;
+               if (stdalgo::string::equalsci(sortmethod, "nick"))
+                       orderby = OB_NICK;
+               else if (stdalgo::string::equalsci(sortmethod, "lastmsg"))
+               {
+                       orderby = OB_LASTMSG;
+                       // We can only check idle times on local users
+                       localonly = true;
+               }
+               else
+                       orderby = OB_NONE;
+
+               typedef std::list<User*> NewUserList;
+               NewUserList user_list;
+               user_hash users = ServerInstance->Users->GetUsers();
+               for (user_hash::iterator i = users.begin(); i != users.end(); ++i)
+               {
+                       User* u = i->second;
+                       if (!showunreg && u->registered != REG_ALL)
+                               continue;
+
+                       LocalUser* lu = IS_LOCAL(u);
+                       if (localonly && !lu)
+                               continue;
+
+                       if (min_idle && lu->idle_lastmsg > maxlastmsg)
+                               continue;
+
+                       user_list.push_back(u);
+               }
+
+               UserSorter sorter(orderby, desc);
+               if (sorter.order != OB_NONE && !(!localonly && sorter.order == OB_LASTMSG))
+                       user_list.sort(sorter);
+
+               size_t count = 0;
+               for (NewUserList::const_iterator i = user_list.begin(); i != user_list.end() && (!limit || count < limit); ++i, ++count)
+                       DumpUser(data, *i);
+
+               data << "</userlist>";
+               return data;
        }
+}
+
+class ModuleHttpStats : public Module, public HTTPRequestEventListener
+{
+       HTTPdAPI API;
+       bool enableparams;
 
-       virtual ~ModuleHttpStats()
+ public:
+       ModuleHttpStats()
+               : HTTPRequestEventListener(this)
+               , API(this)
+               , enableparams(false)
        {
        }
 
-       virtual Version GetVersion()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               return Version("Provides statistics over HTTP via m_httpd.so", VF_VENDOR);
+               ConfigTag* conf = ServerInstance->Config->ConfValue("httpstats");
+
+               // Parameterized queries may cause a performance issue
+               // Due to the sheer volume of data
+               // So default them to disabled
+               enableparams = conf->getBool("enableparams");
        }
-};
 
-static std::map<char, char const*> const &init_entities()
-{
-       static std::map<char, char const*> entities;
-       entities['<'] = "lt";
-       entities['>'] = "gt";
-       entities['&'] = "amp";
-       entities['"'] = "quot";
-       return entities;
-}
+       ModResult HandleRequest(HTTPRequest* http)
+       {
+               std::string path = http->GetPath();
+
+               if (path != "/stats" && path.substr(0, 7) != "/stats/")
+                       return MOD_RES_PASSTHRU;
+
+               if (path[path.size() - 1] == '/')
+                       path.erase(path.size() - 1, 1);
+
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd event");
+
+               bool found = true;
+               std::stringstream data;
+               data << "<inspircdstats>";
+
+               if (path == "/stats")
+               {
+                       data << Stats::ServerInfo << Stats::General
+                               << Stats::XLines << Stats::Modules
+                               << Stats::Channels << Stats::Users
+                               << Stats::Servers << Stats::Commands;
+               }
+               else if (path == "/stats/general")
+               {
+                       data << Stats::General;
+               }
+               else if (path == "/stats/users")
+               {
+                       if (enableparams)
+                               Stats::ListUsers(data, http->GetParsedURI().query_params);
+                       else
+                               data << Stats::Users;
+               }
+               else
+               {
+                       found = false;
+               }
+
+               if (found)
+               {
+                       data << "</inspircdstats>";
+               }
+               else
+               {
+                       data.clear();
+                       data.str(std::string());
+               }
 
-std::map<char, char const*> const &ModuleHttpStats::entities = init_entities ();
+               /* Send the document back to m_httpd */
+               HTTPDocumentResponse response(this, *http, &data, found ? 200 : 404);
+               response.headers.SetHeader("X-Powered-By", MODNAME);
+               response.headers.SetHeader("Content-Type", "text/xml");
+               API->SendResponse(response);
+               return MOD_RES_DENY; // Handled
+       }
+
+       ModResult OnHTTPRequest(HTTPRequest& req) CXX11_OVERRIDE
+       {
+               return HandleRequest(&req);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides statistics over HTTP via m_httpd", VF_VENDOR);
+       }
+};
 
 MODULE_INIT(ModuleHttpStats)
index f0ced1db79789ba313711dc97defae384f1dc8b1..bc1bad383591dd705106d28bf15e21cb736f9929 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for RFC1413 ident lookups */
+enum
+{
+       // Either the ident looup has not started yet or the user is registered.
+       IDENT_UNKNOWN = 0,
+
+       // Ident lookups are not enabled and a user has been marked as being skipped.
+       IDENT_SKIPPED,
+
+       // Ident looups are not enabled and a user has been an insecure ident prefix.
+       IDENT_PREFIXED,
+
+       // An ident lookup was done and an ident was found.
+       IDENT_FOUND,
+
+       // An ident lookup was done but no ident was found
+       IDENT_MISSING
+};
 
 /* --------------------------------------------------------------
  * Note that this is the third incarnation of m_ident. The first
@@ -94,7 +110,7 @@ class IdentRequestSocket : public EventHandler
        {
                age = ServerInstance->Time();
 
-               SetFd(socket(user->server_sa.sa.sa_family, SOCK_STREAM, 0));
+               SetFd(socket(user->server_sa.family(), SOCK_STREAM, 0));
 
                if (GetFd() == -1)
                        throw ModuleException("Could not create socket");
@@ -107,7 +123,7 @@ class IdentRequestSocket : public EventHandler
                memcpy(&bindaddr, &user->server_sa, sizeof(bindaddr));
                memcpy(&connaddr, &user->client_sa, sizeof(connaddr));
 
-               if (connaddr.sa.sa_family == AF_INET6)
+               if (connaddr.family() == AF_INET6)
                {
                        bindaddr.in6.sin6_port = 0;
                        connaddr.in6.sin6_port = htons(113);
@@ -119,39 +135,38 @@ class IdentRequestSocket : public EventHandler
                }
 
                /* Attempt to bind (ident requests must come from the ip the query is referring to */
-               if (ServerInstance->SE->Bind(GetFd(), bindaddr) < 0)
+               if (SocketEngine::Bind(GetFd(), bindaddr) < 0)
                {
                        this->Close();
                        throw ModuleException("failed to bind()");
                }
 
-               ServerInstance->SE->NonBlocking(GetFd());
+               SocketEngine::NonBlocking(GetFd());
 
                /* Attempt connection (nonblocking) */
-               if (ServerInstance->SE->Connect(this, &connaddr.sa, connaddr.sa_size()) == -1 && errno != EINPROGRESS)
+               if (SocketEngine::Connect(this, connaddr) == -1 && errno != EINPROGRESS)
                {
                        this->Close();
                        throw ModuleException("connect() failed");
                }
 
                /* Add fd to socket engine */
-               if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_READ | FD_WANT_POLL_WRITE))
+               if (!SocketEngine::AddFd(this, FD_WANT_NO_READ | FD_WANT_POLL_WRITE))
                {
                        this->Close();
                        throw ModuleException("out of fds");
                }
        }
 
-       virtual void OnConnected()
+       void OnEventHandlerWrite() CXX11_OVERRIDE
        {
-               ServerInstance->Logs->Log("m_ident",DEBUG,"OnConnected()");
-               ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+               SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
 
                char req[32];
 
                /* Build request in the form 'localport,remoteport\r\n' */
                int req_size;
-               if (user->client_sa.sa.sa_family == AF_INET6)
+               if (user->client_sa.family() == AF_INET6)
                        req_size = snprintf(req, sizeof(req), "%d,%d\r\n",
                                ntohs(user->client_sa.in6.sin6_port), ntohs(user->server_sa.in6.sin6_port));
                else
@@ -161,34 +176,10 @@ class IdentRequestSocket : public EventHandler
                /* Send failed if we didnt write the whole ident request --
                 * might as well give up if this happens!
                 */
-               if (ServerInstance->SE->Send(this, req, req_size, 0) < req_size)
+               if (SocketEngine::Send(this, req, req_size, 0) < req_size)
                        done = true;
        }
 
-       virtual 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("m_ident",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
@@ -196,10 +187,8 @@ class IdentRequestSocket : public EventHandler
                 */
                if (GetFd() > -1)
                {
-                       ServerInstance->Logs->Log("m_ident",DEBUG,"Close ident socket %d", GetFd());
-                       ServerInstance->SE->DelFd(this);
-                       ServerInstance->SE->Close(GetFd());
-                       this->SetFd(-1);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Close ident socket %d", GetFd());
+                       SocketEngine::Close(this);
                }
        }
 
@@ -208,13 +197,13 @@ 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
                 */
-               char ibuf[MAXBUF];
-               int recvresult = ServerInstance->SE->Recv(this, ibuf, MAXBUF-1, 0);
+               char ibuf[256];
+               int recvresult = SocketEngine::Recv(this, ibuf, sizeof(ibuf)-1, 0);
 
                /* Close (but don't delete from memory) our socket
                 * and flag as done since the ident lookup has finished
@@ -228,7 +217,7 @@ class IdentRequestSocket : public EventHandler
                if (recvresult < 3)
                        return;
 
-               ServerInstance->Logs->Log("m_ident",DEBUG,"ReadResponse()");
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "ReadResponse()");
 
                /* Truncate at the first null character, but first make sure
                 * there is at least one null char (at the end of the buffer).
@@ -260,67 +249,107 @@ class IdentRequestSocket : public EventHandler
                         * we're done.
                         */
                        result += *i;
-                       if (!ServerInstance->IsIdent(result.c_str()))
+                       if (!ServerInstance->IsIdent(result))
                        {
                                result.erase(result.end()-1);
                                break;
                        }
                }
        }
+
+       void OnEventHandlerError(int errornum) CXX11_OVERRIDE
+       {
+               Close();
+               done = true;
+       }
+
+       CullResult cull() CXX11_OVERRIDE
+       {
+               Close();
+               return EventHandler::cull();
+       }
 };
 
 class ModuleIdent : public Module
 {
-       int RequestTimeout;
-       SimpleExtItem<IdentRequestSocket> ext;
- public:
-       ModuleIdent() : ext("ident_socket", this)
-       {
-       }
+ private:
+       unsigned int timeout;
+       bool prefixunqueried;
+       SimpleExtItem<IdentRequestSocket, stdalgo::culldeleter> socket;
+       LocalIntExt state;
 
-       void init()
+       static void PrefixIdent(LocalUser* user)
        {
-               ServerInstance->Modules->AddService(ext);
-               OnRehash(NULL);
-               Implementation eventlist[] = {
-                       I_OnRehash, I_OnUserInit, I_OnCheckReady,
-                       I_OnUserDisconnect, I_OnSetConnectClass
-               };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               // Check that they haven't been prefixed already.
+               if (user->ident[0] == '~')
+                       return;
+               
+               // All invalid usernames are prefixed with a tilde.
+               std::string newident(user->ident);
+               newident.insert(newident.begin(), '~');
+
+               // If the username is too long then truncate it.
+               if (newident.length() > ServerInstance->Config->Limits.IdentMax)
+                       newident.erase(ServerInstance->Config->Limits.IdentMax);
+
+               // Apply the new username.
+               user->ChangeIdent(newident);
        }
 
-       ~ModuleIdent()
+ public:
+       ModuleIdent()
+               : socket("ident_socket", ExtensionItem::EXT_USER, this)
+               , state("ident_state", ExtensionItem::EXT_USER, this)
        {
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for RFC1413 ident lookups", VF_VENDOR);
        }
 
-       virtual void OnRehash(User *user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               RequestTimeout = ServerInstance->Config->ConfValue("ident")->getInt("timeout", 5);
-               if (!RequestTimeout)
-                       RequestTimeout = 5;
+               ConfigTag* tag = ServerInstance->Config->ConfValue("ident");
+               timeout = tag->getDuration("timeout", 5, 1, 60);
+               prefixunqueried = tag->getBool("prefixunqueried");
        }
 
-       void OnUserInit(LocalUser *user)
+       void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
        {
+               IdentRequestSocket* isock = socket.get(user);
+               if (isock)
+               {
+                       // If an ident lookup request was in progress then cancel it.
+                       isock->Close();
+                       socket.unset(user);
+               }
+
+               // The ident protocol requires that clients are connecting over a protocol with ports.
+               if (user->client_sa.family() != AF_INET && user->client_sa.family() != AF_INET6)
+                       return;
+
+               // We don't want to look this up once the user has connected.
+               if (user->registered == REG_ALL)
+                       return;
+
                ConfigTag* tag = user->MyClass->config;
                if (!tag->getBool("useident", true))
+               {
+                       state.set(user, IDENT_SKIPPED);
                        return;
+               }
 
-               user->WriteServ("NOTICE Auth :*** Looking up your ident...");
+               user->WriteNotice("*** Looking up your ident...");
 
                try
                {
-                       IdentRequestSocket *isock = new IdentRequestSocket(IS_LOCAL(user));
-                       ext.set(user, isock);
+                       isock = new IdentRequestSocket(user);
+                       socket.set(user, isock);
                }
                catch (ModuleException &e)
                {
-                       ServerInstance->Logs->Log("m_ident",DEBUG,"Ident exception: %s", e.GetReason());
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Ident exception: " + e.GetReason());
                }
        }
 
@@ -328,84 +357,67 @@ class ModuleIdent : public Module
         * creating a Timer object and especially better than creating a
         * Timer per ident lookup!
         */
-       virtual ModResult OnCheckReady(LocalUser *user)
+       ModResult OnCheckReady(LocalUser *user) CXX11_OVERRIDE
        {
                /* Does user have an ident socket attached at all? */
-               IdentRequestSocket *isock = ext.get(user);
+               IdentRequestSocket* isock = socket.get(user);
                if (!isock)
                {
-                       ServerInstance->Logs->Log("m_ident",DEBUG, "No ident socket :(");
+                       if (prefixunqueried && state.get(user) == IDENT_SKIPPED)
+                       {
+                               PrefixIdent(user);
+                               state.set(user, IDENT_PREFIXED);
+                       }
                        return MOD_RES_PASSTHRU;
                }
 
-               ServerInstance->Logs->Log("m_ident",DEBUG, "Has ident_socket");
-
-               time_t compare = isock->age;
-               compare += RequestTimeout;
+               time_t compare = isock->age + timeout;
 
                /* Check for timeout of the socket */
                if (ServerInstance->Time() >= compare)
                {
                        /* Ident timeout */
-                       user->WriteServ("NOTICE Auth :*** Ident request timed out.");
-                       ServerInstance->Logs->Log("m_ident",DEBUG, "Timeout");
+                       state.set(user, IDENT_MISSING);
+                       PrefixIdent(user);
+                       user->WriteNotice("*** Ident lookup timed out, using " + user->ident + " instead.");
                }
                else if (!isock->HasResult())
                {
                        // time still good, no result yet... hold the registration
-                       ServerInstance->Logs->Log("m_ident",DEBUG, "No result yet");
                        return MOD_RES_DENY;
                }
 
-               ServerInstance->Logs->Log("m_ident",DEBUG, "Yay, result!");
-
                /* wooo, got a result (it will be good, or bad) */
-               if (isock->result.empty())
+               else if (isock->result.empty())
                {
-                       user->ident.insert(user->ident.begin(), 1, '~');
-                       user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead.", user->ident.c_str());
+                       state.set(user, IDENT_MISSING);
+                       PrefixIdent(user);
+                       user->WriteNotice("*** Could not find your ident, using " + user->ident + " instead.");
                }
                else
                {
-                       user->ident = isock->result;
-                       user->WriteServ("NOTICE Auth :*** Found your ident, '%s'", user->ident.c_str());
+                       state.set(user, IDENT_FOUND);
+                       user->ChangeIdent(isock->result);
+                       user->WriteNotice("*** Found your ident, '" + user->ident + "'");
                }
 
-               user->InvalidateCache();
                isock->Close();
-               ext.unset(user);
+               socket.unset(user);
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
        {
-               if (myclass->config->getBool("requireident") && user->ident[0] == '~')
+               if (myclass->config->getBool("requireident") && state.get(user) != IDENT_FOUND)
                        return MOD_RES_DENY;
                return MOD_RES_PASSTHRU;
        }
 
-       virtual void OnCleanup(int target_type, void *item)
+       void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
        {
-               /* Module unloading, tidy up users */
-               if (target_type == TYPE_USER)
-               {
-                       LocalUser* user = IS_LOCAL((User*) item);
-                       if (user)
-                               OnUserDisconnect(user);
-               }
-       }
-
-       virtual void OnUserDisconnect(LocalUser *user)
-       {
-               /* User disconnect (generic socket detatch event) */
-               IdentRequestSocket *isock = ext.get(user);
-               if (isock)
-               {
-                       isock->Close();
-                       ext.unset(user);
-               }
+               // Clear this as it is no longer necessary.
+               state.unset(user);
        }
 };
 
 MODULE_INIT(ModuleIdent)
-
index 747a3b30a53be2512bf01a80e4520ec0dea8cb8e..b12c98b5da42eaf82bbc589817e9341360f0a89c 100644 (file)
 
 
 #include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides support for the +I channel mode */
-/* $ModDep: ../../include/u_listmode.h */
+#include "listmode.h"
 
 /*
  * Written by Om <om@inspircd.org>, April 2005.
 class InviteException : public ListModeBase
 {
  public:
-       InviteException(Module* Creator) : ListModeBase(Creator, "invex", 'I', "End of Channel Invite Exception List", 346, 347, true) { }
+       InviteException(Module* Creator)
+               : ListModeBase(Creator, "invex", 'I', "End of Channel Invite Exception List", 346, 347, true)
+       {
+       }
 };
 
 class ModuleInviteException : public Module
@@ -54,27 +54,17 @@ public:
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(ie);
-
-               OnRehash(NULL);
-               ie.DoImplements(this);
-               Implementation eventlist[] = { I_On005Numeric, I_OnCheckInvite, I_OnCheckKey, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               output.append(" INVEX=I");
+               tokens["INVEX"] = ConvToStr(ie.GetModeChar());
        }
 
-       ModResult OnCheckInvite(User* user, Channel* chan)
+       ModResult OnCheckInvite(User* user, Channel* chan) CXX11_OVERRIDE
        {
-               modelist* list = ie.extItem.get(chan);
+               ListModeBase::ModeList* list = ie.GetList(chan);
                if (list)
                {
-                       for (modelist::iterator it = list->begin(); it != list->end(); it++)
+                       for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
                        {
                                if (chan->CheckBan(user, it->mask))
                                {
@@ -86,27 +76,22 @@ public:
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnCheckKey(User* user, Channel* chan, const std::string& key)
+       ModResult OnCheckKey(User* user, Channel* chan, const std::string& key) CXX11_OVERRIDE
        {
                if (invite_bypass_key)
                        return OnCheckInvite(user, chan);
                return MOD_RES_PASSTHRU;
        }
 
-       void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
-       {
-               ie.DoSyncChannel(chan, proto, opaque);
-       }
-
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               invite_bypass_key = ServerInstance->Config->ConfValue("inviteexception")->getBool("bypasskey", true);
                ie.DoRehash();
+               invite_bypass_key = ServerInstance->Config->ConfValue("inviteexception")->getBool("bypasskey", true);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for the +I channel mode", VF_VENDOR);
+               return Version("Provides channel mode +I, invite exceptions", VF_VENDOR);
        }
 };
 
index b7dd0e81bbfcd1e50a00f9f5aa198ec27804d0e2..14b1cf8a161ee46ca601fd4a74352fb5348f4c93 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* $ModDesc: Provides support for extended-join, away-notify and account-notify CAP capabilities */
-
 #include "inspircd.h"
-#include "account.h"
-#include "m_cap.h"
+#include "modules/account.h"
+#include "modules/away.h"
+#include "modules/cap.h"
+#include "modules/ircv3.h"
 
-class ModuleIRCv3 : public Module
+class AwayMessage : public ClientProtocol::Message
 {
-       GenericCap cap_accountnotify;
-       GenericCap cap_awaynotify;
-       GenericCap cap_extendedjoin;
-       bool accountnotify;
-       bool awaynotify;
-       bool extendedjoin;
-
-       CUList last_excepts;
-
-       void WriteNeighboursWithExt(User* user, const std::string& line, const LocalIntExt& ext)
+ public:
+       AwayMessage(User* user)
+               : ClientProtocol::Message("AWAY", user)
        {
-               UserChanList chans(user->chans);
-
-               std::map<User*, bool> exceptions;
-               FOREACH_MOD(I_OnBuildNeighborList, 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 (UCListIter i = chans.begin(); i != chans.end(); ++i)
-               {
-                       const UserMembList* userlist = (*i)->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);
-                       }
-               }
+               SetParams(user, user->awaymsg);
        }
 
- public:
-       ModuleIRCv3() : cap_accountnotify(this, "account-notify"),
-                                       cap_awaynotify(this, "away-notify"),
-                                       cap_extendedjoin(this, "extended-join")
+       AwayMessage()
+               : ClientProtocol::Message("AWAY")
        {
        }
 
-       void init()
+       void SetParams(User* user, const std::string& awaymsg)
        {
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnUserJoin, I_OnPostJoin, I_OnSetAway, I_OnEvent, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               // Going away: 1 parameter which is the away reason
+               // Back from away: no parameter
+               if (!awaymsg.empty())
+                       PushParam(awaymsg);
        }
+};
+
+class JoinHook : public ClientProtocol::EventHook
+{
+       ClientProtocol::Events::Join extendedjoinmsg;
 
-       void OnRehash(User* user)
+ public:
+       const std::string asterisk;
+       ClientProtocol::EventProvider awayprotoev;
+       AwayMessage awaymsg;
+       Cap::Capability extendedjoincap;
+       Cap::Capability awaycap;
+
+       JoinHook(Module* mod)
+               : ClientProtocol::EventHook(mod, "JOIN")
+               , asterisk(1, '*')
+               , awayprotoev(mod, "AWAY")
+               , extendedjoincap(mod, "extended-join")
+               , awaycap(mod, "away-notify")
        {
-               ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
-               accountnotify = conf->getBool("accountnotify", conf->getBool("accoutnotify", true));
-               awaynotify = conf->getBool("awaynotify", true);
-               extendedjoin = conf->getBool("extendedjoin", true);
        }
 
-       void OnEvent(Event& ev)
+       void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
        {
-               if (awaynotify)
-                       cap_awaynotify.HandleEvent(ev);
-               if (extendedjoin)
-                       cap_extendedjoin.HandleEvent(ev);
+               const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+
+               // An extended join has two extra parameters:
+               // First the account name of the joining user or an asterisk if the user is not logged in.
+               // The second parameter is the realname of the joining user.
+
+               Membership* const memb = join.GetMember();
+               const std::string* account = &asterisk;
+               const AccountExtItem* const accountext = GetAccountExtItem();
+               if (accountext)
+               {
+                       const std::string* accountname = accountext->get(memb->user);
+                       if (accountname)
+                               account = accountname;
+               }
+
+               extendedjoinmsg.ClearParams();
+               extendedjoinmsg.SetSource(join);
+               extendedjoinmsg.PushParamRef(memb->chan->name);
+               extendedjoinmsg.PushParamRef(*account);
+               extendedjoinmsg.PushParamRef(memb->user->GetRealName());
 
-               if (accountnotify)
+               awaymsg.ClearParams();
+               if ((memb->user->IsAway()) && (awaycap.IsActive()))
                {
-                       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);
-                       }
+                       awaymsg.SetSource(join);
+                       awaymsg.SetParams(memb->user, memb->user->awaymsg);
                }
        }
 
-       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+       ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
        {
-               // Remember who is not going to see the JOIN because of other modules
-               if ((awaynotify) && (IS_AWAY(memb->user)))
-                       last_excepts = excepts;
+               if (extendedjoincap.get(user))
+                       messagelist.front() = &extendedjoinmsg;
 
-               if (!extendedjoin)
-                       return;
+               if ((!awaymsg.GetParams().empty()) && (awaycap.get(user)))
+                       messagelist.push_back(&awaymsg);
 
-               /*
-                * Send extended joins to clients who have the extended-join capability.
-                * An extended join looks like this:
-                *
-                * :nick!user@host JOIN #chan account :realname
-                *
-                * account is the joining user's account if he's logged in, otherwise it's an asterisk (*).
-                */
+               return MOD_RES_PASSTHRU;
+       }
+};
 
-               std::string line;
-               std::string mode;
+class ModuleIRCv3
+       : public Module
+       , public AccountEventListener
+       , public Away::EventListener
+{
+       Cap::Capability cap_accountnotify;
+       JoinHook joinhook;
 
-               const UserMembList* userlist = memb->chan->GetUsers();
-               for (UserMembCIter 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()))
-                       {
-                               // Construct the lines we're going to send if we haven't constructed them already
-                               if (line.empty())
-                               {
-                                       bool has_account = false;
-                                       line = ":" + memb->user->GetFullHost() + " JOIN " + memb->chan->name + " ";
-                                       const AccountExtItem* accountext = GetAccountExtItem();
-                                       if (accountext)
-                                       {
-                                               std::string* accountname;
-                                               accountname = accountext->get(memb->user);
-                                               if (accountname)
-                                               {
-                                                       line += *accountname;
-                                                       has_account = true;
-                                               }
-                                       }
-
-                                       if (!has_account)
-                                               line += "*";
-
-                                       line += " :" + memb->user->fullname;
-
-                                       // If the joining user received privileges from another module then we must send them as well,
-                                       // since silencing the normal join means the MODE will be silenced as well
-                                       if (!memb->modes.empty())
-                                       {
-                                               const std::string& modefrom = ServerInstance->Config->CycleHostsFromUser ? memb->user->GetFullHost() : ServerInstance->Config->ServerName;
-                                               mode = ":" + modefrom + " MODE " + memb->chan->name + " +" + memb->modes;
-
-                                               for (unsigned int i = 0; i < memb->modes.length(); i++)
-                                                       mode += " " + memb->user->nick;
-                                       }
-                               }
-
-                               // Write the JOIN and the MODE, if any
-                               member->Write(line);
-                               if ((!mode.empty()) && (member != memb->user))
-                                       member->Write(mode);
-
-                               // Prevent the core from sending the JOIN and MODE to this user
-                               excepts.insert(it->first);
-                       }
-               }
+       ClientProtocol::EventProvider accountprotoev;
+
+ public:
+       ModuleIRCv3()
+               : AccountEventListener(this)
+               , Away::EventListener(this)
+               , cap_accountnotify(this, "account-notify")
+               , joinhook(this)
+               , accountprotoev(this, "ACCOUNT")
+       {
        }
 
-       ModResult OnSetAway(User* user, const std::string &awaymsg)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               if (awaynotify)
-               {
-                       // Going away: n!u@h AWAY :reason
-                       // Back from away: n!u@h AWAY
-                       std::string line = ":" + user->GetFullHost() + " AWAY";
-                       if (!awaymsg.empty())
-                               line += " :" + awaymsg;
+               ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
+               cap_accountnotify.SetActive(conf->getBool("accountnotify", true));
+               joinhook.awaycap.SetActive(conf->getBool("awaynotify", true));
+               joinhook.extendedjoincap.SetActive(conf->getBool("extendedjoin", true));
+       }
 
-                       WriteNeighboursWithExt(user, line, cap_awaynotify.ext);
-               }
-               return MOD_RES_PASSTHRU;
+       void OnAccountChange(User* user, const std::string& newaccount) CXX11_OVERRIDE
+       {
+               // Logged in: 1 parameter which is the account name
+               // Logged out: 1 parameter which is a "*"
+               ClientProtocol::Message msg("ACCOUNT", user);
+               const std::string& param = (newaccount.empty() ? joinhook.asterisk : newaccount);
+               msg.PushParamRef(param);
+               ClientProtocol::Event accountevent(accountprotoev, msg);
+               IRCv3::WriteNeighborsWithCap(user, accountevent, cap_accountnotify);
        }
 
-       void OnPostJoin(Membership *memb)
+       void OnUserAway(User* user) CXX11_OVERRIDE
        {
-               if ((!awaynotify) || (!IS_AWAY(memb->user)))
+               if (!joinhook.awaycap.IsActive())
                        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)
-               {
-                       // 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()) && (it->second != memb))
-                       {
-                               member->Write(line);
-                       }
-               }
-
-               last_excepts.clear();
+               // Going away: n!u@h AWAY :reason
+               AwayMessage msg(user);
+               ClientProtocol::Event awayevent(joinhook.awayprotoev, msg);
+               IRCv3::WriteNeighborsWithCap(user, awayevent, joinhook.awaycap);
        }
 
-       void Prioritize()
+       void OnUserBack(User* user) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->SetPriority(this, I_OnUserJoin, PRIORITY_LAST);
+               if (!joinhook.awaycap.IsActive())
+                       return;
+
+               // Back from away: n!u@h AWAY
+               AwayMessage msg(user);
+               ClientProtocol::Event awayevent(joinhook.awayprotoev, msg);
+               IRCv3::WriteNeighborsWithCap(user, awayevent, joinhook.awaycap);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for extended-join, away-notify and account-notify CAP capabilities", VF_VENDOR);
        }
diff --git a/src/modules/m_ircv3_accounttag.cpp b/src/modules/m_ircv3_accounttag.cpp
new file mode 100644 (file)
index 0000000..45fcf80
--- /dev/null
@@ -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 "modules/ircv3.h"
+#include "modules/account.h"
+
+class AccountTag : public IRCv3::CapTag<AccountTag>
+{
+ public:
+       const std::string* GetValue(const ClientProtocol::Message& msg) const
+       {
+               User* const user = msg.GetSourceUser();
+               if (!user)
+                       return NULL;
+
+               AccountExtItem* const accextitem = GetAccountExtItem();
+               if (!accextitem)
+                       return NULL;
+
+               return accextitem->get(user);
+       }
+
+       AccountTag(Module* mod)
+               : IRCv3::CapTag<AccountTag>(mod, "account-tag", "account")
+       {
+       }
+};
+
+class ModuleIRCv3AccountTag : public Module
+{
+       AccountTag tag;
+
+ public:
+       ModuleIRCv3AccountTag()
+               : tag(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the account-tag IRCv3 extension", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3AccountTag)
diff --git a/src/modules/m_ircv3_batch.cpp b/src/modules/m_ircv3_batch.cpp
new file mode 100644 (file)
index 0000000..df2b00f
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * 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 "modules/cap.h"
+#include "modules/ircv3_batch.h"
+
+class BatchMessage : public ClientProtocol::Message
+{
+ public:
+       BatchMessage(const IRCv3::Batch::Batch& batch, bool start)
+               : ClientProtocol::Message("BATCH", ServerInstance->Config->ServerName)
+       {
+               char c = (start ? '+' : '-');
+               PushParam(std::string(1, c) + batch.GetRefTagStr());
+               if ((start) && (!batch.GetType().empty()))
+                       PushParamRef(batch.GetType());
+       }
+};
+
+/** Extra structure allocated only for running batches, containing objects only relevant for
+ * that specific run of the batch.
+ */
+struct IRCv3::Batch::BatchInfo
+{
+       /** List of users that have received the batch start message
+        */
+       std::vector<LocalUser*> users;
+       BatchMessage startmsg;
+       ClientProtocol::Event startevent;
+
+       BatchInfo(ClientProtocol::EventProvider& protoevprov, IRCv3::Batch::Batch& b)
+               : startmsg(b, true)
+               , startevent(protoevprov, startmsg)
+       {
+       }
+};
+
+class IRCv3::Batch::ManagerImpl : public Manager
+{
+       typedef std::vector<Batch*> BatchList;
+
+       Cap::Capability cap;
+       ClientProtocol::EventProvider protoevprov;
+       LocalIntExt batchbits;
+       BatchList active_batches;
+       bool unloading;
+
+       bool ShouldSendTag(LocalUser* user, const ClientProtocol::MessageTagData& tagdata) CXX11_OVERRIDE
+       {
+               if (!cap.get(user))
+                       return false;
+
+               Batch& batch = *static_cast<Batch*>(tagdata.provdata);
+               // Check if this is the first message the user is getting that is part of the batch
+               const intptr_t bits = batchbits.get(user);
+               if (!(bits & batch.GetBit()))
+               {
+                       // Send the start batch command ("BATCH +reftag TYPE"), remember the user so we can send them a
+                       // "BATCH -reftag" message later when the batch ends and set the flag we just checked so this is
+                       // only done once per user per batch.
+                       batchbits.set(user, (bits | batch.GetBit()));
+                       batch.batchinfo->users.push_back(user);
+                       user->Send(batch.batchinfo->startevent);
+               }
+
+               return true;
+       }
+
+       unsigned int NextFreeId() const
+       {
+               if (active_batches.empty())
+                       return 0;
+               return active_batches.back()->GetId()+1;
+       }
+
+ public:
+       ManagerImpl(Module* mod)
+               : Manager(mod)
+               , cap(mod, "batch")
+               , protoevprov(mod, "BATCH")
+               , batchbits("batchbits", ExtensionItem::EXT_USER, mod)
+               , unloading(false)
+       {
+       }
+
+       void Init()
+       {
+               // Set batchbits to 0 for all users in case we were reloaded and the previous, now meaningless,
+               // batchbits are set on users
+               const UserManager::LocalList& users = ServerInstance->Users.GetLocalUsers();
+               for (UserManager::LocalList::const_iterator i = users.begin(); i != users.end(); ++i)
+               {
+                       LocalUser* const user = *i;
+                       batchbits.set(user, 0);
+               }
+       }
+
+       void Shutdown()
+       {
+               unloading = true;
+               while (!active_batches.empty())
+                       ManagerImpl::End(*active_batches.back());
+       }
+
+       void RemoveFromAll(LocalUser* user)
+       {
+               const intptr_t bits = batchbits.get(user);
+
+               // User is quitting, remove them from all lists
+               for (BatchList::iterator i = active_batches.begin(); i != active_batches.end(); ++i)
+               {
+                       Batch& batch = **i;
+                       // Check the bit first to avoid list scan in case they're not on the list
+                       if ((bits & batch.GetBit()) != 0)
+                               stdalgo::vector::swaperase(batch.batchinfo->users, user);
+               }
+       }
+
+       void Start(Batch& batch) CXX11_OVERRIDE
+       {
+               if (unloading)
+                       return;
+
+               if (batch.IsRunning())
+                       return; // Already started, don't start again
+
+               const size_t id = NextFreeId();
+               if (id >= MAX_BATCHES)
+                       return;
+
+               batch.Setup(id);
+               // Set the manager field which Batch::IsRunning() checks and is also used by AddToBatch()
+               // to set the message tag
+               batch.manager = this;
+               batch.batchinfo = new IRCv3::Batch::BatchInfo(protoevprov, batch);
+               batch.batchstartmsg = &batch.batchinfo->startmsg;
+               active_batches.push_back(&batch);
+       }
+
+       void End(Batch& batch) CXX11_OVERRIDE
+       {
+               if (!batch.IsRunning())
+                       return;
+
+               // Mark batch as stopped
+               batch.manager = NULL;
+
+               BatchInfo& batchinfo = *batch.batchinfo;
+               // Send end batch message to all users who got the batch start message and unset bit so it can be reused
+               BatchMessage endbatchmsg(batch, false);
+               ClientProtocol::Event endbatchevent(protoevprov, endbatchmsg);
+               for (std::vector<LocalUser*>::const_iterator i = batchinfo.users.begin(); i != batchinfo.users.end(); ++i)
+               {
+                       LocalUser* const user = *i;
+                       user->Send(endbatchevent);
+                       batchbits.set(user, batchbits.get(user) & ~batch.GetBit());
+               }
+
+               // erase() not swaperase because the reftag generation logic depends on the order of the elements
+               stdalgo::erase(active_batches, &batch);
+               delete batch.batchinfo;
+               batch.batchinfo = NULL;
+       }
+};
+
+class ModuleIRCv3Batch : public Module
+{
+       IRCv3::Batch::ManagerImpl manager;
+
+ public:
+       ModuleIRCv3Batch()
+               : manager(this)
+       {
+       }
+
+       void init() CXX11_OVERRIDE
+       {
+               manager.Init();
+       }
+
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE
+       {
+               if (mod == this)
+                       manager.Shutdown();
+       }
+
+       void OnUserDisconnect(LocalUser* user) CXX11_OVERRIDE
+       {
+               // Remove the user from all internal lists
+               manager.RemoveFromAll(user);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the batch IRCv3 extension", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3Batch)
diff --git a/src/modules/m_ircv3_capnotify.cpp b/src/modules/m_ircv3_capnotify.cpp
new file mode 100644 (file)
index 0000000..6190f15
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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 CapNotifyMessage : public Cap::MessageBase
+{
+ public:
+       CapNotifyMessage(bool add, const std::string& capname)
+               : Cap::MessageBase((add ? "NEW" : "DEL"))
+       {
+               PushParamRef(capname);
+       }
+};
+
+class CapNotifyValueMessage : public Cap::MessageBase
+{
+       std::string s;
+       const std::string::size_type pos;
+
+ public:
+       CapNotifyValueMessage(const std::string& capname)
+               : Cap::MessageBase("NEW")
+               , s(capname)
+               , pos(s.size()+1)
+       {
+               s.push_back('=');
+               PushParamRef(s);
+       }
+
+       void SetCapValue(const std::string& capvalue)
+       {
+               s.erase(pos);
+               s.append(capvalue);
+               InvalidateCache();
+       }
+};
+
+class ModuleIRCv3CapNotify : public Module, public Cap::EventListener, public ReloadModule::EventListener
+{
+       CapNotify capnotify;
+       std::string reloadedmod;
+       std::vector<std::string> reloadedcaps;
+       ClientProtocol::EventProvider protoev;
+
+       void Send(const std::string& capname, Cap::Capability* cap, bool add)
+       {
+               CapNotifyMessage msg(add, capname);
+               CapNotifyValueMessage msgwithval(capname);
+
+               ClientProtocol::Event event(protoev, msg);
+               ClientProtocol::Event eventwithval(protoev, msgwithval);
+
+               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.SetUser(user);
+                                       msgwithval.SetCapValue(*capvalue);
+                                       user->Send(eventwithval);
+                                       continue;
+                               }
+                       }
+                       msg.SetUser(user);
+                       user->Send(event);
+               }
+       }
+
+ public:
+       ModuleIRCv3CapNotify()
+               : Cap::EventListener(this)
+               , ReloadModule::EventListener(this)
+               , capnotify(this)
+               , protoev(this, "CAP_NOTIFY")
+       {
+       }
+
+       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 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 (file)
index 0000000..d786891
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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;
+       ClientProtocol::EventProvider protoevprov;
+
+       void DoChgHost(User* user, const std::string& ident, const std::string& host)
+       {
+               ClientProtocol::Message msg("CHGHOST", user);
+               msg.PushParamRef(ident);
+               msg.PushParamRef(host);
+               ClientProtocol::Event protoev(protoevprov, msg);
+               IRCv3::WriteNeighborsWithCap(user, protoev, cap, true);
+       }
+
+ public:
+       ModuleIRCv3ChgHost()
+               : cap(this, "chghost")
+               , protoevprov(this, "CHGHOST")
+       {
+       }
+
+       void OnChangeIdent(User* user, const std::string& newident) CXX11_OVERRIDE
+       {
+               DoChgHost(user, newident, user->GetDisplayedHost());
+       }
+
+       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 extension", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3ChgHost)
diff --git a/src/modules/m_ircv3_ctctags.cpp b/src/modules/m_ircv3_ctctags.cpp
new file mode 100644 (file)
index 0000000..6052051
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2019 Peter Powell <petpow@saberuk.com>
+ *   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 "modules/cap.h"
+#include "modules/ctctags.h"
+
+class CommandTagMsg : public Command
+{
+ private:
+       Cap::Capability& cap;
+       ChanModeReference moderatedmode;
+       ChanModeReference noextmsgmode;
+       Events::ModuleEventProvider tagevprov;
+       ClientProtocol::EventProvider msgevprov;
+
+       bool FirePreEvents(User* source, MessageTarget& msgtarget, CTCTags::TagMessageDetails& msgdetails)
+       {
+               // Inform modules that a TAGMSG wants to be sent.
+               ModResult modres;
+               FIRST_MOD_RESULT_CUSTOM(tagevprov, CTCTags::EventListener, OnUserPreTagMessage, modres, (source, msgtarget, msgdetails));
+               if (modres == MOD_RES_DENY)
+               {
+                       // Inform modules that a module blocked the TAGMSG.
+                       FOREACH_MOD_CUSTOM(tagevprov, CTCTags::EventListener, OnUserTagMessageBlocked, (source, msgtarget, msgdetails));
+                       return false;
+               }
+
+               // Inform modules that a TAGMSG is about to be sent.
+               FOREACH_MOD_CUSTOM(tagevprov, CTCTags::EventListener, OnUserTagMessage, (source, msgtarget, msgdetails));
+               return true;
+       }
+
+       CmdResult FirePostEvent(User* source, const MessageTarget& msgtarget, const CTCTags::TagMessageDetails& msgdetails)
+       {
+               // If the source is local then update its idle time.
+               LocalUser* lsource = IS_LOCAL(source);
+               if (lsource)
+                       lsource->idle_lastmsg = ServerInstance->Time();
+
+               // Inform modules that a TAGMSG was sent.
+               FOREACH_MOD_CUSTOM(tagevprov, CTCTags::EventListener, OnUserPostTagMessage, (source, msgtarget, msgdetails));
+               return CMD_SUCCESS;
+       }
+
+       CmdResult HandleChannelTarget(User* source, const Params& parameters, const char* target, PrefixMode* pm)
+       {
+               Channel* chan = ServerInstance->FindChan(target);
+               if (!chan)
+               {
+                       // The target channel does not exist.
+                       source->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+                       return CMD_FAILURE;
+               }
+
+               if (IS_LOCAL(source))
+               {
+                       if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(source))
+                       {
+                               // The noextmsg mode is set and the source is not in the channel.
+                               source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (no external messages)");
+                               return CMD_FAILURE;
+                       }
+
+                       bool no_chan_priv = chan->GetPrefixValue(source) < VOICE_VALUE;
+                       if (no_chan_priv && chan->IsModeSet(moderatedmode))
+                       {
+                               // The moderated mode is set and the source has no status rank.
+                               source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (+m is set)");
+                               return CMD_FAILURE;
+                       }
+
+                       if (no_chan_priv && ServerInstance->Config->RestrictBannedUsers != ServerConfig::BUT_NORMAL && chan->IsBanned(source))
+                       {
+                               // The source is banned in the channel and restrictbannedusers is enabled.
+                               if (ServerInstance->Config->RestrictBannedUsers == ServerConfig::BUT_RESTRICT_NOTIFY)
+                                       source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)");
+                               return CMD_FAILURE;
+                       }
+               }
+
+               // Fire the pre-message events.
+               MessageTarget msgtarget(chan, pm ? pm->GetPrefix() : 0);
+               CTCTags::TagMessageDetails msgdetails(parameters.GetTags());
+               if (!FirePreEvents(source, msgtarget, msgdetails))
+                       return CMD_FAILURE;
+
+               unsigned int minrank = pm ? pm->GetPrefixRank() : 0;
+               CTCTags::TagMessage message(source, chan, parameters.GetTags());
+               const Channel::MemberMap& userlist = chan->GetUsers();
+               for (Channel::MemberMap::const_iterator iter = userlist.begin(); iter != userlist.end(); ++iter)
+               {
+                       LocalUser* luser = IS_LOCAL(iter->first);
+
+                       // Don't send to remote users or the user who is the source. 
+                       if (!luser || luser == source)
+                               continue;
+
+                       // Don't send to unprivileged or exempt users.
+                       if (iter->second->getRank() < minrank || msgdetails.exemptions.count(luser))
+                               continue;
+
+                       // Send to users if they have the capability.
+                       if (cap.get(luser))
+                               luser->Send(msgevprov, message);
+               }
+               return FirePostEvent(source, msgtarget, msgdetails);
+       }
+
+       CmdResult HandleServerTarget(User* source, const Params& parameters)
+       {
+               // If the source isn't allowed to mass message users then reject
+               // the attempt to mass-message users.
+               if (!source->HasPrivPermission("users/mass-message"))
+                       return CMD_FAILURE;
+
+               // Extract the server glob match from the target parameter.
+               std::string servername(parameters[0], 1);
+
+               // Fire the pre-message events.
+               MessageTarget msgtarget(&servername);
+               CTCTags::TagMessageDetails msgdetails(parameters.GetTags());
+               if (!FirePreEvents(source, msgtarget, msgdetails))
+                       return CMD_FAILURE;
+
+               // If the current server name matches the server name glob then send
+               // the message out to the local users.
+               if (InspIRCd::Match(ServerInstance->Config->ServerName, servername))
+               {
+                       CTCTags::TagMessage message(source, "$*", parameters.GetTags());
+                       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+                       for (UserManager::LocalList::const_iterator iter = list.begin(); iter != list.end(); ++iter)
+                       {
+                               LocalUser* luser = IS_LOCAL(*iter);
+
+                               // Don't send to unregistered users or the user who is the source.
+                               if (luser->registered != REG_ALL || luser == source)
+                                       continue;
+
+                               // Don't send to exempt users.
+                               if (msgdetails.exemptions.count(luser))
+                                       continue;
+
+                               // Send to users if they have the capability.
+                               if (cap.get(luser))
+                                       luser->Send(msgevprov, message);
+                       }
+               }
+
+               // Fire the post-message event.
+               return FirePostEvent(source, msgtarget, msgdetails);
+       }
+
+       CmdResult HandleUserTarget(User* source, const Params& parameters)
+       {
+               User* target;
+               if (IS_LOCAL(source))
+               {
+                       // Local sources can specify either a nick or a nick@server mask as the target.
+                       const char* targetserver = strchr(parameters[0].c_str(), '@');
+                       if (targetserver)
+                       {
+                               // The target is a user on a specific server (e.g. jto@tolsun.oulu.fi).
+                               target = ServerInstance->FindNickOnly(parameters[0].substr(0, targetserver - parameters[0].c_str()));
+                               if (target && strcasecmp(target->server->GetName().c_str(), targetserver + 1))
+                                       target = NULL;
+                       }
+                       else
+                       {
+                               // If the source is a local user then we only look up the target by nick.
+                               target = ServerInstance->FindNickOnly(parameters[0]);
+                       }
+               }
+               else
+               {
+                       // Remote users can only specify a nick or UUID as the target.
+                       target = ServerInstance->FindNick(parameters[0]);
+               }
+
+               if (!target || target->registered != REG_ALL)
+               {
+                       // The target user does not exist or is not fully registered.
+                       source->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
+                       return CMD_FAILURE;
+               }
+
+               // Fire the pre-message events.
+               MessageTarget msgtarget(target);
+               CTCTags::TagMessageDetails msgdetails(parameters.GetTags());
+               if (!FirePreEvents(source, msgtarget, msgdetails))
+                       return CMD_FAILURE;
+
+               LocalUser* const localtarget = IS_LOCAL(target);
+               if (localtarget && cap.get(localtarget))
+               {
+                       // Send to the target if they have the capability and are a local user.
+                       CTCTags::TagMessage message(source, localtarget, parameters.GetTags());
+                       localtarget->Send(msgevprov, message);
+               }
+
+               // Fire the post-message event.
+               return FirePostEvent(source, msgtarget, msgdetails);
+       }
+
+ public:
+       CommandTagMsg(Module* Creator, Cap::Capability& Cap)
+               : Command(Creator, "TAGMSG", 1)
+               , cap(Cap)
+               , moderatedmode(Creator, "moderated")
+               , noextmsgmode(Creator, "noextmsg")
+               , tagevprov(Creator, "event/tagmsg")
+               , msgevprov(Creator, "TAGMSG")
+       {
+               allow_empty_last_param = false;
+       }
+
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               if (CommandParser::LoopCall(user, this, parameters, 0))
+                       return CMD_SUCCESS;
+
+               // Check that the source has the message tags capability.
+               if (IS_LOCAL(user) && !cap.get(user))
+                       return CMD_FAILURE;
+
+               // The target is a server glob.
+               if (parameters[0][0] == '$')
+                       return HandleServerTarget(user, parameters);
+
+               // If the message begins with a status character then look it up.
+               const char* target = parameters[0].c_str();
+               PrefixMode* pmh = ServerInstance->Modes->FindPrefix(target[0]);
+               if (pmh)
+                       target++;
+
+               // The target is a channel name.
+               if (*target == '#')
+                       return HandleChannelTarget(user, parameters, target, pmh);
+
+               // The target is a nickname.
+               return HandleUserTarget(user, parameters);
+       }
+
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               return ROUTE_MESSAGE(parameters[0]);
+       }
+};
+
+class C2CTags : public ClientProtocol::MessageTagProvider
+{
+ private:
+       Cap::Capability& cap;
+
+ public:
+       C2CTags(Module* Creator, Cap::Capability& Cap)
+               : ClientProtocol::MessageTagProvider(Creator)
+               , cap(Cap)
+       {
+       }
+
+       ModResult OnProcessTag(User* user, const std::string& tagname, std::string& tagvalue) CXX11_OVERRIDE
+       {
+               // A client-only tag is prefixed with a plus sign (+) and otherwise conforms
+               // to the format specified in IRCv3.2 tags.
+               if (tagname[0] != '+' || tagname.length() < 2)
+                       return MOD_RES_PASSTHRU;
+
+               // If the user is local then we check whether they have the message-tags cap
+               // enabled. If not then we reject all client-only tags originating from them.
+               LocalUser* lu = IS_LOCAL(user);
+               if (lu && !cap.get(lu))
+                       return MOD_RES_DENY;
+
+               // Remote users have their client-only tags checked by their local server.
+               return MOD_RES_ALLOW;
+       }
+
+       bool ShouldSendTag(LocalUser* user, const ClientProtocol::MessageTagData& tagdata) CXX11_OVERRIDE
+       {
+               return cap.get(user);
+       }
+};
+
+class ModuleIRCv3CTCTags
+       : public Module
+       , public CTCTags::EventListener
+{
+ private:
+       Cap::Capability cap;
+       CommandTagMsg cmd;
+       C2CTags c2ctags;
+
+       ModResult CopyClientTags(const ClientProtocol::TagMap& tags_in, ClientProtocol::TagMap& tags_out)
+       {
+               for (ClientProtocol::TagMap::const_iterator i = tags_in.begin(); i != tags_in.end(); ++i)
+               {
+                       const ClientProtocol::MessageTagData& tagdata = i->second;
+                       if (tagdata.tagprov == &c2ctags)
+                               tags_out.insert(*i);
+               }
+               return MOD_RES_PASSTHRU;
+       }
+
+ public:
+       ModuleIRCv3CTCTags()
+               : CTCTags::EventListener(this)
+               , cap(this, "message-tags")
+               , cmd(this, cap)
+               , c2ctags(this, cap)
+       {
+       }
+
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
+       {
+               return CopyClientTags(details.tags_in, details.tags_out);
+       }
+
+       ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
+       {
+               return CopyClientTags(details.tags_in, details.tags_out);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the DRAFT message-tags IRCv3 extension", VF_VENDOR | VF_COMMON);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3CTCTags)
diff --git a/src/modules/m_ircv3_echomessage.cpp b/src/modules/m_ircv3_echomessage.cpp
new file mode 100644 (file)
index 0000000..3ec534e
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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"
+#include "modules/ctctags.h"
+
+class ModuleIRCv3EchoMessage
+       : public Module
+       , public CTCTags::EventListener
+{
+ private:
+       Cap::Capability cap;
+       ClientProtocol::EventProvider tagmsgprov;
+
+ public:
+       ModuleIRCv3EchoMessage()
+               : CTCTags::EventListener(this)
+               , cap(this, "echo-message")
+               , tagmsgprov(this, "TAGMSG")
+       {
+       }
+
+       void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
+       {
+               if (!cap.get(user) || !details.echo)
+                       return;
+
+               // Caps are only set on local users
+               LocalUser* const localuser = static_cast<LocalUser*>(user);
+
+               const std::string& text = details.echo_original ? details.original_text : details.text;
+               const ClientProtocol::TagMap& tags = details.echo_original ? details.tags_in : details.tags_out;
+               if (target.type == MessageTarget::TYPE_USER)
+               {
+                       User* destuser = target.Get<User>();
+                       ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, destuser, text, details.type);
+                       privmsg.AddTags(tags);
+                       localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
+               }
+               else if (target.type == MessageTarget::TYPE_CHANNEL)
+               {
+                       Channel* chan = target.Get<Channel>();
+                       ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, chan, text, details.type, target.status);
+                       privmsg.AddTags(tags);
+                       localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
+               }
+               else
+               {
+                       const std::string* servername = target.Get<std::string>();
+                       ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, *servername, text, details.type);
+                       privmsg.AddTags(tags);
+                       localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
+               }
+       }
+
+       void OnUserPostTagMessage(User* user, const MessageTarget& target, const CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
+       {
+               if (!cap.get(user) || !details.echo)
+                       return;
+
+               // Caps are only set on local users
+               LocalUser* const localuser = static_cast<LocalUser*>(user);
+
+               const ClientProtocol::TagMap& tags = details.echo_original ? details.tags_in : details.tags_out;
+               if (target.type == MessageTarget::TYPE_USER)
+               {
+                       User* destuser = target.Get<User>();
+                       CTCTags::TagMessage message(user, destuser, tags);
+                       localuser->Send(tagmsgprov, message);
+               }
+               else if (target.type == MessageTarget::TYPE_CHANNEL)
+               {
+                       Channel* chan = target.Get<Channel>();
+                       CTCTags::TagMessage message(user, chan, tags);
+                       localuser->Send(tagmsgprov, message);
+               }
+               else
+               {
+                       const std::string* servername = target.Get<std::string>();
+                       CTCTags::TagMessage message(user, servername->c_str(), tags);
+                       localuser->Send(tagmsgprov, message);
+               }
+       }
+
+       void OnUserMessageBlocked(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
+       {
+               // Prevent spammers from knowing that their spam was blocked.
+               if (details.echo_original)
+                       OnUserPostMessage(user, target, details);
+       }
+
+       void OnUserTagMessageBlocked(User* user, const MessageTarget& target, const CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
+       {
+               // Prevent spammers from knowing that their spam was blocked.
+               if (details.echo_original)
+                       OnUserPostTagMessage(user, target, details);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the echo-message IRCv3 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 (file)
index 0000000..8bd4d56
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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
+       {
+               ClientProtocol::Messages::Invite invitemsg(source, dest, chan);
+               ClientProtocol::Event inviteevent(ServerInstance->GetRFCEvents().invite, invitemsg);
+               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;
+
+                       // Caps are only set on local users
+                       LocalUser* const localuser = static_cast<LocalUser*>(user);
+                       // Send and add the user to the exceptions so they won't get the NOTICE invite announcement message
+                       localuser->Send(inviteevent);
+                       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 extension", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3InviteNotify)
diff --git a/src/modules/m_ircv3_servertime.cpp b/src/modules/m_ircv3_servertime.cpp
new file mode 100644 (file)
index 0000000..1c35c42
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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 "modules/ircv3.h"
+#include "modules/ircv3_servertime.h"
+
+class ServerTimeTag : public IRCv3::ServerTime::Manager, public IRCv3::CapTag<ServerTimeTag>
+{
+       time_t lasttime;
+       std::string lasttimestring;
+
+       void RefreshTimeString()
+       {
+               const time_t currtime = ServerInstance->Time();
+               if (currtime != lasttime)
+               {
+                       lasttime = currtime;
+                       // Cache the string so it's not recreated every time a message is sent
+                       lasttimestring = IRCv3::ServerTime::FormatTime(currtime);
+               }
+       }
+
+ public:
+       ServerTimeTag(Module* mod)
+               : IRCv3::ServerTime::Manager(mod)
+               , IRCv3::CapTag<ServerTimeTag>(mod, "server-time", "time")
+               , lasttime(0)
+       {
+               tagprov = this;
+       }
+
+       const std::string* GetValue(const ClientProtocol::Message& msg)
+       {
+               RefreshTimeString();
+               return &lasttimestring;
+       }
+};
+
+class ModuleIRCv3ServerTime : public Module
+{
+       ServerTimeTag tag;
+
+ public:
+       ModuleIRCv3ServerTime()
+               : tag(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the server-time IRCv3 extension", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3ServerTime)
diff --git a/src/modules/m_ircv3_sts.cpp b/src/modules/m_ircv3_sts.cpp
new file mode 100644 (file)
index 0000000..a8738b2
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *   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 "inspircd.h"
+#include "modules/cap.h"
+#include "modules/ssl.h"
+
+class STSCap : public Cap::Capability
+{
+ private:
+       std::string host;
+       std::string plaintextpolicy;
+       std::string securepolicy;
+
+       bool OnList(LocalUser* user) CXX11_OVERRIDE
+       {
+               // Don't send the cap to clients that only support cap-3.1.
+               if (GetProtocol(user) == Cap::CAP_LEGACY)
+                       return false;
+
+               // Plaintext listeners have their own policy.
+               SSLIOHook* sslhook = SSLIOHook::IsSSL(&user->eh);
+               if (!sslhook)
+                       return true;
+
+               // If no hostname has been provided for the connection, an STS persistence policy SHOULD NOT be advertised.
+               std::string snihost;
+               if (!sslhook->GetServerName(snihost))
+                       return false;
+
+               // Before advertising an STS persistence policy over a secure connection, servers SHOULD verify whether the
+               // hostname provided by clients, for example, via TLS Server Name Indication (SNI), has been whitelisted by
+               // administrators in the server configuration.
+               return InspIRCd::Match(snihost, host, ascii_case_insensitive_map);
+       }
+
+       bool OnRequest(LocalUser* user, bool adding) CXX11_OVERRIDE
+       {
+               // Clients MUST NOT request this capability with CAP REQ. Servers MAY reply with a CAP NAK message if a
+               // client requests this capability.
+               return false;
+       }
+
+       const std::string* GetValue(LocalUser* user) const CXX11_OVERRIDE
+       {
+               return SSLIOHook::IsSSL(&user->eh) ? &securepolicy : &plaintextpolicy;
+       }
+
+ public:
+       STSCap(Module* mod)
+               : Cap::Capability(mod, "sts")
+       {
+       }
+
+       ~STSCap()
+       {
+               // TODO: Send duration=0 when STS vanishes.
+       }
+
+       void SetPolicy(const std::string& newhost, unsigned long duration, unsigned int port, bool preload)
+       {
+               // To enforce an STS upgrade policy, servers MUST send this key to insecurely connected clients. Servers
+               // MAY send this key to securely connected clients, but it will be ignored.
+               std::string newplaintextpolicy("port=");
+               newplaintextpolicy.append(ConvToStr(port));
+
+               // To enforce an STS persistence policy, servers MUST send this key to securely connected clients. Servers
+               // MAY send this key to all clients, but insecurely connected clients MUST ignore it.
+               std::string newsecurepolicy("duration=");
+               newsecurepolicy.append(ConvToStr(duration));
+
+               // Servers MAY send this key to all clients, but insecurely connected clients MUST ignore it.
+               if (preload)
+                       newsecurepolicy.append(",preload");
+
+               // Apply the new policy.
+               bool changed = false;
+               if (!irc::equals(host, newhost))
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing STS SNI hostname from \"%s\" to \"%s\"", host.c_str(), newhost.c_str());
+                       host = newhost;
+                       changed = true;
+               }
+
+               if (plaintextpolicy != newplaintextpolicy)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing plaintext STS policy from \"%s\" to \"%s\"", plaintextpolicy.c_str(), newplaintextpolicy.c_str());
+                       plaintextpolicy.swap(newplaintextpolicy);
+                       changed = true;
+               }
+
+               if (securepolicy != newsecurepolicy)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing secure STS policy from \"%s\" to \"%s\"", securepolicy.c_str(), newsecurepolicy.c_str());
+                       securepolicy.swap(newsecurepolicy);
+                       changed = true;
+               }
+
+               // If the policy has changed then notify all clients via cap-notify.
+               if (changed)
+                       NotifyValueChange();
+       }
+};
+
+class ModuleIRCv3STS : public Module
+{
+ private:
+       STSCap cap;
+
+       // The IRCv3 STS specification requires that the server is listening using SSL using a valid certificate.
+       bool HasValidSSLPort(unsigned int port)
+       {
+               for (std::vector<ListenSocket*>::const_iterator iter = ServerInstance->ports.begin(); iter != ServerInstance->ports.end(); ++iter)
+               {
+                       ListenSocket* ls = *iter;
+
+                       // Is this listener on the right port?
+                       unsigned int saport = ls->bind_sa.port();
+                       if (saport != port)
+                               continue;
+
+                       // Is this listener using SSL?
+                       if (ls->bind_tag->getString("ssl").empty())
+                               continue;
+
+                       // TODO: Add a way to check if a listener's TLS cert is CA-verified.
+                       return true;
+               }
+               return false;
+       }
+
+ public:
+       ModuleIRCv3STS()
+               : cap(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               // TODO: Multiple SNI profiles
+               ConfigTag* tag = ServerInstance->Config->ConfValue("sts");
+               if (tag == ServerInstance->Config->EmptyTag)
+                       throw ModuleException("You must define a STS policy!");
+
+               const std::string host = tag->getString("host");
+               if (host.empty())
+                       throw ModuleException("<sts:host> must contain a hostname, at " + tag->getTagLocation());
+
+               unsigned int port = tag->getUInt("port", 0, 0, UINT16_MAX);
+               if (!HasValidSSLPort(port))
+                       throw ModuleException("<sts:port> must be a TLS port, at " + tag->getTagLocation());
+
+               unsigned long duration = tag->getDuration("duration", 60*60*24*30*2);
+               bool preload = tag->getBool("preload");
+               cap.SetPolicy(host, duration, port, preload);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides IRCv3 Strict Transport Security policy advertisement", VF_OPTCOMMON|VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleIRCv3STS)
index 63bcc38a4180c30526814f66ee6396d433b94ae8..1b9deac5fe6f8289e82d65db2c02ef51c240ad9b 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +j (join flood protection) */
+enum
+{
+       // From RFC 2182.
+       ERR_UNAVAILRESOURCE = 437
+};
+
+// 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
 {
  public:
-       int secs;
-       int joins;
+       unsigned int secs;
+       unsigned int joins;
        time_t reset;
        time_t unlocktime;
-       int counter;
-       bool locked;
+       unsigned int counter;
 
-       joinfloodsettings(int b, int c) : secs(b), joins(c)
+       joinfloodsettings(unsigned int b, unsigned int c)
+               : secs(b), joins(c), unlocktime(0), counter(0)
        {
                reset = ServerInstance->Time() + secs;
-               counter = 0;
-               locked = false;
-       };
+       }
 
        void addjoin()
        {
-               counter++;
                if (ServerInstance->Time() > reset)
                {
-                       counter = 0;
+                       counter = 1;
                        reset = ServerInstance->Time() + secs;
                }
+               else
+                       counter++;
        }
 
        bool shouldlock()
@@ -66,155 +72,93 @@ class joinfloodsettings
 
        bool islocked()
        {
-               if (locked)
-               {
-                       if (ServerInstance->Time() > unlocktime)
-                       {
-                               locked = false;
-                               return false;
-                       }
-                       else
-                       {
-                               return true;
-                       }
-               }
-               return false;
+               if (ServerInstance->Time() > unlocktime)
+                       unlocktime = 0;
+
+               return (unlocktime != 0);
        }
 
        void lock()
        {
-               locked = true;
-               unlocktime = ServerInstance->Time() + 60;
+               unlocktime = ServerInstance->Time() + duration;
        }
 
+       bool operator==(const joinfloodsettings& other) const
+       {
+               return ((this->secs == other.secs) && (this->joins == other.joins));
+       }
 };
 
 /** Handles channel mode +j
  */
-class JoinFlood : public ModeHandler
+class JoinFlood : public ParamMode<JoinFlood, SimpleExtItem<joinfloodsettings> >
 {
  public:
-       SimpleExtItem<joinfloodsettings> ext;
-       JoinFlood(Module* Creator) : ModeHandler(Creator, "joinflood", 'j', PARAM_SETONLY, MODETYPE_CHANNEL),
-               ext("joinflood", Creator) { }
+       JoinFlood(Module* Creator)
+               : ParamMode<JoinFlood, SimpleExtItem<joinfloodsettings> >(Creator, "joinflood", 'j')
+       {
+       }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
        {
-               if (adding)
+               std::string::size_type colon = parameter.find(':');
+               if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
                {
-                       char ndata[MAXBUF];
-                       char* data = ndata;
-                       strlcpy(ndata,parameter.c_str(),MAXBUF);
-                       char* joins = data;
-                       char* secs = NULL;
-                       while (*data)
-                       {
-                               if (*data == ':')
-                               {
-                                       *data = 0;
-                                       data++;
-                                       secs = data;
-                                       break;
-                               }
-                               else data++;
-                       }
-                       if (secs)
-
-                       {
-                               /* Set up the flood parameters for this channel */
-                               int njoins = atoi(joins);
-                               int nsecs = atoi(secs);
-                               if ((njoins<1) || (nsecs<1))
-                               {
-                                       source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
-                                       parameter.clear();
-                                       return MODEACTION_DENY;
-                               }
-                               else
-                               {
-                                       joinfloodsettings* f = ext.get(channel);
-                                       if (!f)
-                                       {
-                                               parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs);
-                                               f = new joinfloodsettings(nsecs, njoins);
-                                               ext.set(channel, f);
-                                               channel->SetModeParam('j', parameter);
-                                               return MODEACTION_ALLOW;
-                                       }
-                                       else
-                                       {
-                                               std::string cur_param = channel->GetModeParameter('j');
-                                               parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs);
-                                               if (cur_param == parameter)
-                                               {
-                                                       // mode params match
-                                                       return MODEACTION_DENY;
-                                               }
-                                               else
-                                               {
-                                                       // new mode param, replace old with new
-                                                       f = new joinfloodsettings(nsecs, njoins);
-                                                       ext.set(channel, f);
-                                                       channel->SetModeParam('j', parameter);
-                                                       return MODEACTION_ALLOW;
-                                               }
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
-                               return MODEACTION_DENY;
-                       }
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+                       return MODEACTION_DENY;
                }
-               else
+
+               /* Set up the flood parameters for this channel */
+               unsigned int njoins = ConvToNum<unsigned int>(parameter.substr(0, colon));
+               unsigned int nsecs = ConvToNum<unsigned int>(parameter.substr(colon+1));
+               if ((njoins<1) || (nsecs<1))
                {
-                       if (channel->IsModeSet('j'))
-                       {
-                               ext.unset(channel);
-                               channel->SetModeParam('j', "");
-                               return MODEACTION_ALLOW;
-                       }
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+                       return MODEACTION_DENY;
                }
-               return MODEACTION_DENY;
+
+               ext.set(channel, new joinfloodsettings(nsecs, njoins));
+               return MODEACTION_ALLOW;
+       }
+
+       void SerializeParam(Channel* chan, const joinfloodsettings* jfs, std::string& out)
+       {
+               out.append(ConvToStr(jfs->joins)).push_back(':');
+               out.append(ConvToStr(jfs->secs));
        }
 };
 
 class ModuleJoinFlood : public Module
 {
-
        JoinFlood jf;
 
  public:
-
        ModuleJoinFlood()
                : jf(this)
        {
        }
 
-       void init()
+       void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(jf);
-               ServerInstance->Modules->AddService(jf.ext);
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserJoin };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               ConfigTag* tag = ServerInstance->Config->ConfValue("joinflood");
+               duration = tag->getDuration("duration", 60, 10, 600);
        }
 
-       ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
                if (chan)
                {
                        joinfloodsettings *f = jf.ext.get(chan);
                        if (f && f->islocked())
                        {
-                               user->WriteNumeric(609, "%s %s :This channel is temporarily unavailable (+j). Please try again later.",user->nick.c_str(),chan->name.c_str());
+                               user->WriteNumeric(ERR_UNAVAILRESOURCE, chan->name, "This channel is temporarily unavailable (+j is set). Please try again later.");
                                return MOD_RES_DENY;
                        }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
        {
                /* We arent interested in JOIN events caused by a network burst */
                if (sync)
@@ -230,18 +174,14 @@ 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));
                        }
                }
        }
 
-       ~ModuleJoinFlood()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides channel mode +j (join flood protection)", VF_VENDOR);
+               return Version("Provides channel mode +j, join flood protection", VF_VENDOR);
        }
 };
 
diff --git a/src/modules/m_jumpserver.cpp b/src/modules/m_jumpserver.cpp
deleted file mode 100644 (file)
index 44c6fc0..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-
-/* $ModDesc: Provides support for the RPL_REDIR numeric and the /JUMPSERVER command. */
-
-/** Handle /JUMPSERVER
- */
-class CommandJumpserver : public Command
-{
- public:
-       bool redirect_new_users;
-       std::string redirect_to;
-       std::string reason;
-       int port;
-
-       CommandJumpserver(Module* Creator) : Command(Creator, "JUMPSERVER", 0, 4)
-       {
-               flags_needed = 'o'; syntax = "[<server> <port> <+/-an> <reason>]";
-               port = 0;
-               redirect_new_users = false;
-       }
-
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
-       {
-               int n_done = 0;
-               reason = (parameters.size() < 4) ? "Please use this server/port instead" : parameters[3];
-               bool redirect_all_immediately = false;
-               redirect_new_users = true;
-               bool direction = true;
-               std::string n_done_s;
-
-               /* No parameters: jumpserver disabled */
-               if (!parameters.size())
-               {
-                       if (port)
-                               user->WriteServ("NOTICE %s :*** Disabled jumpserver (previously set to '%s:%d')", user->nick.c_str(), redirect_to.c_str(), port);
-                       else
-                               user->WriteServ("NOTICE %s :*** Jumpserver was not enabled.", user->nick.c_str());
-
-                       port = 0;
-                       redirect_to.clear();
-                       return CMD_SUCCESS;
-               }
-
-               port = 0;
-               redirect_to.clear();
-
-               if (parameters.size() >= 3)
-               {
-                       for (std::string::const_iterator n = parameters[2].begin(); n != parameters[2].end(); ++n)
-                       {
-                               switch (*n)
-                               {
-                                       case '+':
-                                               direction = true;
-                                       break;
-                                       case '-':
-                                               direction = false;
-                                       break;
-                                       case 'a':
-                                               redirect_all_immediately = direction;
-                                       break;
-                                       case 'n':
-                                               redirect_new_users = direction;
-                                       break;
-                                       default:
-                                               user->WriteServ("NOTICE %s :*** Invalid JUMPSERVER flag: %c", user->nick.c_str(), *n);
-                                               return CMD_FAILURE;
-                                       break;
-                               }
-                       }
-
-                       if (!atoi(parameters[1].c_str()))
-                       {
-                               user->WriteServ("NOTICE %s :*** Invalid port number", user->nick.c_str());
-                               return CMD_FAILURE;
-                       }
-
-                       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)
-                               {
-                                       User* t = *i;
-                                       if (!IS_OPER(t))
-                                       {
-                                               t->WriteNumeric(10, "%s %s %s :Please use this Server/Port instead", t->nick.c_str(), parameters[0].c_str(), parameters[1].c_str());
-                                               ServerInstance->Users->QuitUser(t, reason);
-                                               n_done++;
-                                       }
-                               }
-                               if (n_done)
-                               {
-                                       n_done_s = ConvToStr(n_done);
-                               }
-                       }
-
-                       if (redirect_new_users)
-                       {
-                               redirect_to = parameters[0];
-                               port = atoi(parameters[1].c_str());
-                       }
-
-                       user->WriteServ("NOTICE %s :*** Set jumpserver to server '%s' port '%s', flags '+%s%s'%s%s%s: %s", user->nick.c_str(), parameters[0].c_str(), parameters[1].c_str(),
-                                       redirect_all_immediately ? "a" : "",
-                                       redirect_new_users ? "n" : "",
-                                       n_done ? " (" : "",
-                                       n_done ? n_done_s.c_str() : "",
-                                       n_done ? " user(s) redirected)" : "",
-                                       reason.c_str());
-               }
-
-               return CMD_SUCCESS;
-       }
-};
-
-
-class ModuleJumpServer : public Module
-{
-       CommandJumpserver js;
- public:
-       ModuleJumpServer() : js(this)
-       {
-       }
-
-       void init()
-       {
-               ServerInstance->Modules->AddService(js);
-               Implementation eventlist[] = { I_OnUserRegister, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModuleJumpServer()
-       {
-       }
-
-       virtual ModResult OnUserRegister(LocalUser* user)
-       {
-               if (js.port && js.redirect_new_users)
-               {
-                       user->WriteNumeric(10, "%s %s %d :Please use this Server/Port instead",
-                               user->nick.c_str(), js.redirect_to.c_str(), js.port);
-                       ServerInstance->Users->QuitUser(user, js.reason);
-                       return MOD_RES_DENY;
-               }
-               return MOD_RES_PASSTHRU;
-       }
-
-       virtual void OnRehash(User* user)
-       {
-               // Emergency way to unlock
-               if (!user) js.redirect_new_users = false;
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides support for the RPL_REDIR numeric and the /JUMPSERVER command.", VF_VENDOR);
-       }
-
-};
-
-MODULE_INIT(ModuleJumpServer)
index a914f38693108b69d37a357d7131a92d72c6d43c..ec5ac661e89c224463c251d9d2b6b20196231ab0 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/invite.h"
 
-/* $ModDesc: Provides channel mode +J (delay rejoin after kick) */
+enum
+{
+       // From RFC 2182.
+       ERR_UNAVAILRESOURCE = 437
+};
+
+
+class KickRejoinData
+{
+       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;
 
-typedef std::map<std::string, time_t> delaylist;
+       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 ModeHandler
+class KickRejoin : public ParamMode<KickRejoin, SimpleExtItem<KickRejoinData> >
 {
+       const unsigned int max;
  public:
-       unsigned int max;
-       SimpleExtItem<delaylist> ext;
        KickRejoin(Module* Creator)
-               : ModeHandler(Creator, "kicknorejoin", 'J', PARAM_SETONLY, MODETYPE_CHANNEL)
-               , ext("norejoinusers", Creator)
+               : ParamMode<KickRejoin, SimpleExtItem<KickRejoinData> >(Creator, "kicknorejoin", 'J')
+               , max(60)
        {
        }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
        {
-               if (adding)
+               unsigned int v = ConvToNum<unsigned int>(parameter);
+               if (v <= 0)
                {
-                       int v = ConvToInt(parameter);
-                       if (v <= 0)
-                               return MODEACTION_DENY;
-                       if (parameter == channel->GetModeParameter(this))
-                               return MODEACTION_DENY;
-
-                       if ((IS_LOCAL(source) && ((unsigned int)v > max)))
-                               v = max;
-
-                       parameter = ConvToStr(v);
-                       channel->SetModeParam(this, parameter);
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+                       return MODEACTION_DENY;
                }
-               else
-               {
-                       if (!channel->IsModeSet(this))
-                               return MODEACTION_DENY;
 
-                       ext.unset(channel);
-                       channel->SetModeParam(this, "");
-               }
+               if (IS_LOCAL(source) && v > max)
+                       v = max;
+
+               ext.set(channel, new KickRejoinData(v));
                return MODEACTION_ALLOW;
        }
+
+       void SerializeParam(Channel* chan, const KickRejoinData* krd, std::string& out)
+       {
+               out.append(ConvToStr(krd->delay));
+       }
+
+       std::string GetModuleSettings() const
+       {
+               return ConvToStr(max);
+       }
 };
 
 class ModuleKickNoRejoin : public Module
 {
        KickRejoin kr;
+       Invite::API invapi;
 
 public:
-
        ModuleKickNoRejoin()
                : kr(this)
+               , invapi(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(kr);
-               ServerInstance->Modules->AddService(kr.ext);
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserKick, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
-       }
-
-       void OnRehash(User* user)
-       {
-               kr.max = ServerInstance->Duration(ServerInstance->Config->ConfValue("kicknorejoin")->getString("maxtime"));
-               if (!kr.max)
-                       kr.max = 30*60;
-       }
-
-       ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
                if (chan)
                {
-                       delaylist* dl = kr.ext.get(chan);
-                       if (dl)
+                       const KickRejoinData* data = kr.ext.get(chan);
+                       if ((data) && !invapi->IsInvited(user, chan) && (!data->canjoin(user)))
                        {
-                               for (delaylist::iterator iter = dl->begin(); iter != dl->end(); )
-                               {
-                                       if (iter->second > ServerInstance->Time())
-                                       {
-                                               if (iter->first == user->uuid)
-                                               {
-                                                       std::string modeparam = chan->GetModeParameter(&kr);
-                                                       user->WriteNumeric(ERR_DELAYREJOIN, "%s %s :You must wait %s seconds after being kicked to rejoin (+J)",
-                                                               user->nick.c_str(), chan->name.c_str(), modeparam.c_str());
-                                                       return MOD_RES_DENY;
-                                               }
-                                               ++iter;
-                                       }
-                                       else
-                                       {
-                                               // Expired record, remove.
-                                               dl->erase(iter++);
-                                       }
-                               }
-
-                               if (dl->empty())
-                                       kr.ext.unset(chan);
+                               user->WriteNumeric(ERR_UNAVAILRESOURCE, chan, InspIRCd::Format("You must wait %u seconds after being kicked to rejoin (+J is set)", data->delay));
+                               return MOD_RES_DENY;
                        }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
+       void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) CXX11_OVERRIDE
        {
-               if (memb->chan->IsModeSet(&kr) && (IS_LOCAL(memb->user)) && (source != memb->user))
+               if ((!IS_LOCAL(memb->user)) || (source == memb->user))
+                       return;
+
+               KickRejoinData* data = kr.ext.get(memb->chan);
+               if (data)
                {
-                       delaylist* dl = kr.ext.get(memb->chan);
-                       if (!dl)
-                       {
-                               dl = new delaylist;
-                               kr.ext.set(memb->chan, dl);
-                       }
-                       (*dl)[memb->user->uuid] = ServerInstance->Time() + ConvToInt(memb->chan->GetModeParameter(&kr));
+                       data->add(memb->user);
                }
        }
 
-       ~ModuleKickNoRejoin()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Channel mode to delay rejoin after kick", VF_VENDOR);
+               return Version("Provides channel mode +J, delays rejoins after kicks", VF_VENDOR | VF_COMMON, kr.GetModuleSettings());
        }
 };
 
-
 MODULE_INIT(ModuleKickNoRejoin)
index 8d2aa4543065be26e7b410ef41007c6d664cd37d..8d6c70092273ba5bdbde27404a92ca75706f6db2 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for /KNOCK and channel mode +K */
+enum
+{
+       // From UnrealIRCd.
+       ERR_CANNOTKNOCK = 480,
+
+       // From ircd-ratbox.
+       ERR_CHANOPEN = 713,
+       ERR_KNOCKONCHAN = 714
+};
 
 /** Handles the /KNOCK command
  */
 class CommandKnock : public Command
 {
+       SimpleChannelModeHandler& noknockmode;
+       ChanModeReference inviteonlymode;
+
  public:
        bool sendnotice;
        bool sendnumeric;
-       CommandKnock(Module* Creator) : Command(Creator,"KNOCK", 2, 2)
+       CommandKnock(Module* Creator, SimpleChannelModeHandler& Noknockmode)
+               : Command(Creator,"KNOCK", 2, 2)
+               , noknockmode(Noknockmode)
+               , inviteonlymode(Creator, "inviteonly")
        {
-               syntax = "<channel> <reason>";
+               syntax = "<channel> :<reason>";
                Penalty = 5;
-               TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                Channel* c = ServerInstance->FindChan(parameters[0]);
                if (!c)
                {
-                       user->WriteNumeric(401, "%s %s :No such channel",user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
                        return CMD_FAILURE;
                }
 
                if (c->HasUser(user))
                {
-                       user->WriteNumeric(480, "%s :Can't KNOCK on %s, you are already on that channel.", user->nick.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('K'))
+               if (c->IsModeSet(noknockmode))
                {
-                       user->WriteNumeric(480, "%s :Can't KNOCK on %s, +K is set.",user->nick.c_str(), c->name.c_str());
+                       user->WriteNumeric(ERR_CANNOTKNOCK, InspIRCd::Format("Can't KNOCK on %s, +K is set.", c->name.c_str()));
                        return CMD_FAILURE;
                }
 
-               if (!c->IsModeSet('i'))
+               if (!c->IsModeSet(inviteonlymode))
                {
-                       user->WriteNumeric(480, "%s :Can't KNOCK on %s, channel is not invite only so knocking is pointless!",user->nick.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());
+               {
+                       Numeric::Numeric numeric(710);
+                       numeric.push(c->name).push(user->GetFullHost()).push("is KNOCKing: " + parameters[1]);
 
-               user->WriteServ("NOTICE %s :KNOCKing on %s", user->nick.c_str(), c->name.c_str());
+                       ClientProtocol::Messages::Numeric numericmsg(numeric, c->name);
+                       c->Write(ServerInstance->GetRFCEvents().numeric, numericmsg);
+               }
+
+               user->WriteNotice("KNOCKing on " + c->name);
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_OPT_BCAST;
        }
 };
 
-/** Handles channel mode +K
- */
-class Knock : public SimpleChannelModeHandler
-{
- public:
-       Knock(Module* Creator) : SimpleChannelModeHandler(Creator, "noknock", 'K') { }
-};
-
 class ModuleKnock : public Module
 {
+       SimpleChannelModeHandler kn;
        CommandKnock cmd;
-       Knock kn;
- public:
-       ModuleKnock() : cmd(this), kn(this)
-       {
-       }
 
-       void init()
+ public:
+       ModuleKnock()
+               : kn(this, "noknock", 'K')
+               , cmd(this, kn)
        {
-               ServerInstance->Modules->AddService(kn);
-               ServerInstance->Modules->AddService(cmd);
-
-               ServerInstance->Modules->Attach(I_OnRehash, this);
-               OnRehash(NULL);
        }
 
-       void OnRehash(User* user)
+       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;
@@ -128,9 +131,9 @@ class ModuleKnock : public Module
                }
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for /KNOCK and channel mode +K", VF_OPTCOMMON | VF_VENDOR);
+               return Version("Provides the KNOCK command and channel mode +K", VF_OPTCOMMON | VF_VENDOR);
        }
 };
 
diff --git a/src/modules/m_ldapauth.cpp b/src/modules/m_ldapauth.cpp
new file mode 100644 (file)
index 0000000..b833b93
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Adam <Adam@anope.org>
+ *   Copyright (C) 2011 Pierre Carrier <pierre@spotify.com>
+ *   Copyright (C) 2009-2010 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2008 Dennis Friis <peavey@inspircd.org>
+ *   Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@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 "modules/ldap.h"
+
+namespace
+{
+       Module* me;
+       std::string killreason;
+       LocalIntExt* authed;
+       bool verbose;
+       std::string vhost;
+       LocalStringExt* vhosts;
+       std::vector<std::pair<std::string, std::string> > requiredattributes;
+}
+
+class BindInterface : public LDAPInterface
+{
+       const std::string provider;
+       const std::string uid;
+       std::string DN;
+       bool checkingAttributes;
+       bool passed;
+       int attrCount;
+
+       static std::string SafeReplace(const std::string& text, std::map<std::string, std::string>& replacements)
+       {
+               std::string result;
+               result.reserve(text.length());
+
+               for (unsigned int i = 0; i < text.length(); ++i)
+               {
+                       char c = text[i];
+                       if (c == '$')
+                       {
+                               // find the first nonalpha
+                               i++;
+                               unsigned int start = i;
+
+                               while (i < text.length() - 1 && isalpha(text[i + 1]))
+                                       ++i;
+
+                               std::string key(text, start, (i - start) + 1);
+                               result.append(replacements[key]);
+                       }
+                       else
+                               result.push_back(c);
+               }
+
+               return result;
+       }
+
+       static void SetVHost(User* user, const std::string& DN)
+       {
+               if (!vhost.empty())
+               {
+                       irc::commasepstream stream(DN);
+
+                       // mashed map of key:value parts of the DN
+                       std::map<std::string, std::string> dnParts;
+
+                       std::string dnPart;
+                       while (stream.GetToken(dnPart))
+                       {
+                               std::string::size_type pos = dnPart.find('=');
+                               if (pos == std::string::npos) // malformed
+                                       continue;
+
+                               std::string key(dnPart, 0, pos);
+                               std::string value(dnPart, pos + 1, dnPart.length() - pos + 1); // +1s to skip the = itself
+                               dnParts[key] = value;
+                       }
+
+                       // change host according to config key
+                       vhosts->set(user, SafeReplace(vhost, dnParts));
+               }
+       }
+
+ public:
+       BindInterface(Module* c, const std::string& p, const std::string& u, const std::string& dn)
+               : LDAPInterface(c)
+               , provider(p), uid(u), DN(dn), checkingAttributes(false), passed(false), attrCount(0)
+       {
+       }
+
+       void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+       {
+               User* user = ServerInstance->FindUUID(uid);
+               dynamic_reference<LDAPProvider> LDAP(me, provider);
+
+               if (!user || !LDAP)
+               {
+                       if (!checkingAttributes || !--attrCount)
+                               delete this;
+                       return;
+               }
+
+               if (!checkingAttributes && requiredattributes.empty())
+               {
+                       // We're done, there are no attributes to check
+                       SetVHost(user, DN);
+                       authed->set(user, 1);
+
+                       delete this;
+                       return;
+               }
+
+               // Already checked attributes?
+               if (checkingAttributes)
+               {
+                       if (!passed)
+                       {
+                               // Only one has to pass
+                               passed = true;
+
+                               SetVHost(user, DN);
+                               authed->set(user, 1);
+                       }
+
+                       // Delete this if this is the last ref
+                       if (!--attrCount)
+                               delete this;
+
+                       return;
+               }
+
+               // check required attributes
+               checkingAttributes = true;
+
+               for (std::vector<std::pair<std::string, std::string> >::const_iterator it = requiredattributes.begin(); it != requiredattributes.end(); ++it)
+               {
+                       // Note that only one of these has to match for it to be success
+                       const std::string& attr = it->first;
+                       const std::string& val = it->second;
+
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "LDAP compare: %s=%s", attr.c_str(), val.c_str());
+                       try
+                       {
+                               LDAP->Compare(this, DN, attr, val);
+                               ++attrCount;
+                       }
+                       catch (LDAPException &ex)
+                       {
+                               if (verbose)
+                                       ServerInstance->SNO->WriteToSnoMask('c', "Unable to compare attributes %s=%s: %s", attr.c_str(), val.c_str(), ex.GetReason().c_str());
+                       }
+               }
+
+               // Nothing done
+               if (!attrCount)
+               {
+                       if (verbose)
+                               ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (unable to validate attributes)", user->GetFullRealHost().c_str());
+                       ServerInstance->Users->QuitUser(user, killreason);
+                       delete this;
+               }
+       }
+
+       void OnError(const LDAPResult& err) CXX11_OVERRIDE
+       {
+               if (checkingAttributes && --attrCount)
+                       return;
+
+               if (passed)
+               {
+                       delete this;
+                       return;
+               }
+
+               User* user = ServerInstance->FindUUID(uid);
+               if (user)
+               {
+                       if (verbose)
+                               ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (%s)", user->GetFullRealHost().c_str(), err.getError().c_str());
+                       ServerInstance->Users->QuitUser(user, killreason);
+               }
+
+               delete this;
+       }
+};
+
+class SearchInterface : public LDAPInterface
+{
+       const std::string provider;
+       const std::string uid;
+
+ public:
+       SearchInterface(Module* c, const std::string& p, const std::string& u)
+               : LDAPInterface(c), provider(p), uid(u)
+       {
+       }
+
+       void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+       {
+               LocalUser* user = static_cast<LocalUser*>(ServerInstance->FindUUID(uid));
+               dynamic_reference<LDAPProvider> LDAP(me, provider);
+               if (!LDAP || r.empty() || !user)
+               {
+                       if (user)
+                               ServerInstance->Users->QuitUser(user, killreason);
+                       delete this;
+                       return;
+               }
+
+               try
+               {
+                       const LDAPAttributes& a = r.get(0);
+                       std::string bindDn = a.get("dn");
+                       if (bindDn.empty())
+                       {
+                               ServerInstance->Users->QuitUser(user, killreason);
+                               delete this;
+                               return;
+                       }
+
+                       LDAP->Bind(new BindInterface(this->creator, provider, uid, bindDn), bindDn, user->password);
+               }
+               catch (LDAPException& ex)
+               {
+                       ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+               }
+               delete this;
+       }
+
+       void OnError(const LDAPResult& err) CXX11_OVERRIDE
+       {
+               ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: %s", err.getError().c_str());
+               User* user = ServerInstance->FindUUID(uid);
+               if (user)
+                       ServerInstance->Users->QuitUser(user, killreason);
+               delete this;
+       }
+};
+
+class AdminBindInterface : public LDAPInterface
+{
+       const std::string provider;
+       const std::string uuid;
+       const std::string base;
+       const std::string what;
+
+ public:
+       AdminBindInterface(Module* c, const std::string& p, const std::string& u, const std::string& b, const std::string& w)
+               : LDAPInterface(c), provider(p), uuid(u), base(b), what(w)
+       {
+       }
+
+       void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+       {
+               dynamic_reference<LDAPProvider> LDAP(me, provider);
+               if (LDAP)
+               {
+                       try
+                       {
+                               LDAP->Search(new SearchInterface(this->creator, provider, uuid), base, what);
+                       }
+                       catch (LDAPException& ex)
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+                       }
+               }
+               delete this;
+       }
+
+       void OnError(const LDAPResult& err) CXX11_OVERRIDE
+       {
+               ServerInstance->SNO->WriteToSnoMask('a', "Error binding as manager to LDAP server: " + err.getError());
+               delete this;
+       }
+};
+
+class ModuleLDAPAuth : public Module
+{
+       dynamic_reference<LDAPProvider> LDAP;
+       LocalIntExt ldapAuthed;
+       LocalStringExt ldapVhost;
+       std::string base;
+       std::string attribute;
+       std::vector<std::string> allowpatterns;
+       std::vector<std::string> whitelistedcidrs;
+       bool useusername;
+
+public:
+       ModuleLDAPAuth()
+               : LDAP(this, "LDAP")
+               , ldapAuthed("ldapauth", ExtensionItem::EXT_USER, this)
+               , ldapVhost("ldapauth_vhost", ExtensionItem::EXT_USER, this)
+       {
+               me = this;
+               authed = &ldapAuthed;
+               vhosts = &ldapVhost;
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("ldapauth");
+               whitelistedcidrs.clear();
+               requiredattributes.clear();
+
+               base                    = tag->getString("baserdn");
+               attribute               = tag->getString("attribute");
+               killreason              = tag->getString("killreason");
+               vhost                   = tag->getString("host");
+               // Set to true if failed connects should be reported to operators
+               verbose                 = tag->getBool("verbose");
+               useusername             = tag->getBool("userfield");
+
+               LDAP.SetProvider("LDAP/" + tag->getString("dbid"));
+
+               ConfigTagList whitelisttags = ServerInstance->Config->ConfTags("ldapwhitelist");
+
+               for (ConfigIter i = whitelisttags.first; i != whitelisttags.second; ++i)
+               {
+                       std::string cidr = i->second->getString("cidr");
+                       if (!cidr.empty()) {
+                               whitelistedcidrs.push_back(cidr);
+                       }
+               }
+
+               ConfigTagList attributetags = ServerInstance->Config->ConfTags("ldaprequire");
+
+               for (ConfigIter i = attributetags.first; i != attributetags.second; ++i)
+               {
+                       const std::string attr = i->second->getString("attribute");
+                       const std::string val = i->second->getString("value");
+
+                       if (!attr.empty() && !val.empty())
+                               requiredattributes.push_back(make_pair(attr, val));
+               }
+
+               std::string allowpattern = tag->getString("allowpattern");
+               irc::spacesepstream ss(allowpattern);
+               for (std::string more; ss.GetToken(more); )
+               {
+                       allowpatterns.push_back(more);
+               }
+       }
+
+       void OnUserConnect(LocalUser *user) CXX11_OVERRIDE
+       {
+               std::string* cc = ldapVhost.get(user);
+               if (cc)
+               {
+                       user->ChangeDisplayedHost(*cc);
+                       ldapVhost.unset(user);
+               }
+       }
+
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
+       {
+               for (std::vector<std::string>::const_iterator i = allowpatterns.begin(); i != allowpatterns.end(); ++i)
+               {
+                       if (InspIRCd::Match(user->nick, *i))
+                       {
+                               ldapAuthed.set(user,1);
+                               return MOD_RES_PASSTHRU;
+                       }
+               }
+
+               for (std::vector<std::string>::iterator i = whitelistedcidrs.begin(); i != whitelistedcidrs.end(); i++)
+               {
+                       if (InspIRCd::MatchCIDR(user->GetIPString(), *i, ascii_case_insensitive_map))
+                       {
+                               ldapAuthed.set(user,1);
+                               return MOD_RES_PASSTHRU;
+                       }
+               }
+
+               if (user->password.empty())
+               {
+                       if (verbose)
+                               ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (no password provided)", user->GetFullRealHost().c_str());
+                       ServerInstance->Users->QuitUser(user, killreason);
+                       return MOD_RES_DENY;
+               }
+
+               if (!LDAP)
+               {
+                       if (verbose)
+                               ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (unable to find LDAP provider)", user->GetFullRealHost().c_str());
+                       ServerInstance->Users->QuitUser(user, killreason);
+                       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
+               {
+                       LDAP->BindAsManager(new AdminBindInterface(this, LDAP.GetProvider(), user->uuid, base, what));
+               }
+               catch (LDAPException &ex)
+               {
+                       ServerInstance->SNO->WriteToSnoMask('a', "LDAP exception: " + ex.GetReason());
+                       ServerInstance->Users->QuitUser(user, killreason);
+               }
+
+               return MOD_RES_DENY;
+       }
+
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+       {
+               return ldapAuthed.get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Allow/deny connections based upon answers from an LDAP server", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleLDAPAuth)
diff --git a/src/modules/m_ldapoper.cpp b/src/modules/m_ldapoper.cpp
new file mode 100644 (file)
index 0000000..cde5b00
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Adam <Adam@anope.org>
+ *   Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@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 "modules/ldap.h"
+
+namespace
+{
+       Module* me;
+}
+
+class LDAPOperBase : public LDAPInterface
+{
+ protected:
+       const std::string uid;
+       const std::string opername;
+       const std::string password;
+
+       void Fallback(User* user)
+       {
+               if (!user)
+                       return;
+
+               Command* oper_command = ServerInstance->Parser.GetHandler("OPER");
+               if (!oper_command)
+                       return;
+
+               CommandBase::Params params;
+               params.push_back(opername);
+               params.push_back(password);
+               ClientProtocol::TagMap tags;
+               oper_command->Handle(user, CommandBase::Params(params, tags));
+       }
+
+       void Fallback()
+       {
+               User* user = ServerInstance->FindUUID(uid);
+               Fallback(user);
+       }
+
+ public:
+       LDAPOperBase(Module* mod, const std::string& uuid, const std::string& oper, const std::string& pass)
+               : LDAPInterface(mod)
+               , uid(uuid), opername(oper), password(pass)
+       {
+       }
+
+       void OnError(const LDAPResult& err) CXX11_OVERRIDE
+       {
+               ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: %s", err.getError().c_str());
+               Fallback();
+               delete this;
+       }
+};
+
+class BindInterface : public LDAPOperBase
+{
+ public:
+       BindInterface(Module* mod, const std::string& uuid, const std::string& oper, const std::string& pass)
+               : LDAPOperBase(mod, uuid, oper, pass)
+       {
+       }
+
+       void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+       {
+               User* user = ServerInstance->FindUUID(uid);
+               ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->oper_blocks.find(opername);
+
+               if (!user || iter == ServerInstance->Config->oper_blocks.end())
+               {
+                       Fallback();
+                       delete this;
+                       return;
+               }
+
+               OperInfo* ifo = iter->second;
+               user->Oper(ifo);
+               delete this;
+       }
+};
+
+class SearchInterface : public LDAPOperBase
+{
+       const std::string provider;
+
+       bool HandleResult(const LDAPResult& result)
+       {
+               dynamic_reference<LDAPProvider> LDAP(me, provider);
+               if (!LDAP || result.empty())
+                       return false;
+
+               try
+               {
+                       const LDAPAttributes& attr = result.get(0);
+                       std::string bindDn = attr.get("dn");
+                       if (bindDn.empty())
+                               return false;
+
+                       LDAP->Bind(new BindInterface(this->creator, uid, opername, password), bindDn, password);
+               }
+               catch (LDAPException& ex)
+               {
+                       ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+               }
+
+               return true;
+       }
+
+ public:
+       SearchInterface(Module* mod, const std::string& prov, const std::string &uuid, const std::string& oper, const std::string& pass)
+               : LDAPOperBase(mod, uuid, oper, pass)
+               , provider(prov)
+       {
+       }
+
+       void OnResult(const LDAPResult& result) CXX11_OVERRIDE
+       {
+               if (!HandleResult(result))
+                       Fallback();
+               delete this;
+       }
+};
+
+class AdminBindInterface : public LDAPInterface
+{
+       const std::string provider;
+       const std::string user;
+       const std::string opername;
+       const std::string password;
+       const std::string base;
+       const std::string what;
+
+ public:
+       AdminBindInterface(Module* c, const std::string& p, const std::string& u, const std::string& o, const std::string& pa, const std::string& b, const std::string& w)
+               : LDAPInterface(c), provider(p), user(u), opername(p), password(pa), base(b), what(w)
+       {
+       }
+
+       void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+       {
+               dynamic_reference<LDAPProvider> LDAP(me, provider);
+               if (LDAP)
+               {
+                       try
+                       {
+                               LDAP->Search(new SearchInterface(this->creator, provider, user, opername, password), base, what);
+                       }
+                       catch (LDAPException& ex)
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+                       }
+               }
+               delete this;
+       }
+
+       void OnError(const LDAPResult& err) CXX11_OVERRIDE
+       {
+               ServerInstance->SNO->WriteToSnoMask('a', "Error binding as manager to LDAP server: " + err.getError());
+               delete this;
+       }
+};
+
+class ModuleLDAPAuth : public Module
+{
+       dynamic_reference<LDAPProvider> LDAP;
+       std::string base;
+       std::string attribute;
+
+ public:
+       ModuleLDAPAuth()
+               : LDAP(this, "LDAP")
+       {
+               me = this;
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("ldapoper");
+
+               LDAP.SetProvider("LDAP/" + tag->getString("dbid"));
+               base = tag->getString("baserdn");
+               attribute = tag->getString("attribute");
+       }
+
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
+       {
+               if (validated && command == "OPER" && parameters.size() >= 2)
+               {
+                       const std::string& opername = parameters[0];
+                       const std::string& password = parameters[1];
+
+                       ServerConfig::OperIndex::const_iterator it = ServerInstance->Config->oper_blocks.find(opername);
+                       if (it == ServerInstance->Config->oper_blocks.end())
+                               return MOD_RES_PASSTHRU;
+
+                       ConfigTag* tag = it->second->oper_block;
+                       if (!tag)
+                               return MOD_RES_PASSTHRU;
+
+                       std::string acceptedhosts = tag->getString("host");
+                       std::string hostname = user->ident + "@" + user->GetRealHost();
+                       if (!InspIRCd::MatchMask(acceptedhosts, hostname, user->GetIPString()))
+                               return MOD_RES_PASSTHRU;
+
+                       if (!LDAP)
+                               return MOD_RES_PASSTHRU;
+
+                       try
+                       {
+                               std::string what = attribute + "=" + opername;
+                               LDAP->BindAsManager(new AdminBindInterface(this, LDAP.GetProvider(), user->uuid, opername, password, base, what));
+                               return MOD_RES_DENY;
+                       }
+                       catch (LDAPException& ex)
+                       {
+                               ServerInstance->SNO->WriteToSnoMask('a', "LDAP exception: " + ex.GetReason());
+                       }
+               }
+
+               return MOD_RES_PASSTHRU;
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Adds the ability to authenticate opers via LDAP", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleLDAPAuth)
index 4983ae16abe3afa01eaa7022feca4fe7c2a51f7c..5d049423d06b51133ef3aa696316de331e2a9c02 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Allows locking of the server to stop all incoming connections till unlocked again */
-
 /** Adds numerics
  * 988 <nick> <servername> :Closed for new connections
  * 989 <nick> <servername> :Open for new connections
-*/
-
+ */
+enum
+{
+       // InspIRCd-specific.
+       RPL_SERVLOCKON = 988,
+       RPL_SERVLOCKOFF = 989
+};
 
 class CommandLockserv : public Command
 {
-       bool& locked;
-public:
-       CommandLockserv(Module* Creator, bool& lock) : Command(Creator, "LOCKSERV", 0), locked(lock)
+       std::string& locked;
+
+ public:
+       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)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               if (locked)
+               if (!locked.empty())
                {
-                       user->WriteServ("NOTICE %s :The server is already locked.", user->nick.c_str());
+                       user->WriteNotice("The server is already locked.");
                        return CMD_FAILURE;
                }
 
-               locked = true;
-               user->WriteNumeric(988, "%s %s :Closed for new connections", user->nick.c_str(), user->server.c_str());
+               locked = parameters.empty() ? "Server is temporarily closed. Please try again later." : parameters[0];
+               user->WriteNumeric(RPL_SERVLOCKON, 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;
        }
@@ -54,25 +59,24 @@ public:
 
 class CommandUnlockserv : public Command
 {
-private:
-       bool& locked;
+       std::string& locked;
 
-public:
-       CommandUnlockserv(Module* Creator, bool &lock) : Command(Creator, "UNLOCKSERV", 0), locked(lock)
+ public:
+       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)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               if (!locked)
+               if (locked.empty())
                {
-                       user->WriteServ("NOTICE %s :The server isn't locked.", user->nick.c_str());
+                       user->WriteNotice("The server isn't locked.");
                        return CMD_FAILURE;
                }
 
-               locked = false;
-               user->WriteNumeric(989, "%s %s :Open for new connections", user->nick.c_str(), user->server.c_str());
+               locked.clear();
+               user->WriteNumeric(RPL_SERVLOCKOFF, 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;
        }
@@ -80,54 +84,46 @@ public:
 
 class ModuleLockserv : public Module
 {
-private:
-       bool locked;
+       std::string locked;
        CommandLockserv lockcommand;
        CommandUnlockserv unlockcommand;
 
-public:
+ public:
        ModuleLockserv() : lockcommand(this, locked), unlockcommand(this, locked)
        {
        }
 
-       void init()
-       {
-               locked = false;
-               ServerInstance->Modules->AddService(lockcommand);
-               ServerInstance->Modules->AddService(unlockcommand);
-               Implementation eventlist[] = { I_OnUserRegister, I_OnRehash, I_OnCheckReady };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModuleLockserv()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
+               // Emergency way to unlock
+               if (!status.srcuser)
+                       locked.clear();
        }
 
-
-       virtual void OnRehash(User* user)
+       void OnModuleRehash(User* user, const std::string& param) CXX11_OVERRIDE
        {
-               // Emergency way to unlock
-               if (!user) locked = false;
+               if (irc::equals(param, "lockserv") && !locked.empty())
+                       locked.clear();
        }
 
-       virtual ModResult OnUserRegister(LocalUser* user)
+       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;
        }
 
-       virtual ModResult OnCheckReady(LocalUser* user)
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
-               return locked ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+               return !locked.empty() ? MOD_RES_DENY : MOD_RES_PASSTHRU;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Allows locking of the server to stop all incoming connections until unlocked again", VF_VENDOR);
+               return Version("Provides the LOCKSERV and UNLOCKSERV commands to lock the server and block all incoming connections until unlocked again", VF_VENDOR);
        }
 };
 
index 546e342ae36180d4644921501689e8b6c24b6f3f..8228c56c35a8b5f1419132040178a76e51059f2e 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Hide /MAP and /LINKS in the same form as ircu (mostly useless) */
-
 class ModuleMapHide : public Module
 {
        std::string url;
  public:
-       void init()
-       {
-               Implementation eventlist[] = { I_OnPreCommand, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
-       }
-
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                url = ServerInstance->Config->ConfValue("security")->getString("maphide");
        }
 
-       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
        {
-               if (validated && !IS_OPER(user) && !url.empty() && (command == "MAP" || command == "LINKS"))
+               if (validated && !user->IsOper() && !url.empty() && (command == "MAP" || command == "LINKS"))
                {
-                       user->WriteServ("NOTICE %s :/%s has been disabled; visit %s", user->nick.c_str(), command.c_str(), url.c_str());
+                       user->WriteNotice("/" + command + " has been disabled; visit " + url);
                        return MOD_RES_DENY;
                }
                else
                        return MOD_RES_PASSTHRU;
        }
 
-       virtual ~ModuleMapHide()
+       Version GetVersion() CXX11_OVERRIDE
        {
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Hide /MAP and /LINKS in the same form as ircu (mostly useless)", VF_VENDOR);
+               return Version("Replaces the output of the MAP and LINKS commands with an URL", VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleMapHide)
-
index c902ee3cb753c8f9c527a0bfd3e381656261c766..8de70872fc234e14205ef02461bcab91d5b96691 100644 (file)
  */
 
 
-/* $ModDesc: Allows for MD5 encrypted oper passwords */
-
 #include "inspircd.h"
-#ifdef HAS_STDINT
-#include <stdint.h>
-#endif
-#include "hash.h"
+#include "modules/hash.h"
 
 /* The four core functions - F1 is optimized somewhat */
 #define F1(x, y, z) (z ^ (x & (y ^ z)))
 #define MD5STEP(f,w,x,y,z,in,s) \
        (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
 
-#ifndef HAS_STDINT
-typedef unsigned int uint32_t;
-#endif
-
 typedef uint32_t word32; /* NOT unsigned long. We don't support 16 bit platforms, anyway. */
 typedef unsigned char byte;
 
@@ -70,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;
@@ -245,44 +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) CXX11_OVERRIDE
        {
                char res[16];
-               MyMD5(res, (void*)data.data(), data.length(), NULL);
+               MyMD5(res, (void*)data.data(), data.length());
                return std::string(res, 16);
        }
 
-       std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
-       {
-               char res[33];
-               GenHash(sdata.data(), res, HexMap, IV, sdata.length());
-               return res;
-       }
-
-       MD5Provider(Module* parent) : HashProvider(parent, "hash/md5", 16, 64) {}
+       MD5Provider(Module* parent) : HashProvider(parent, "md5", 16, 64) {}
 };
 
 class ModuleMD5 : public Module
@@ -291,10 +251,9 @@ class ModuleMD5 : public Module
  public:
        ModuleMD5() : md5(this)
        {
-               ServerInstance->Modules->AddService(md5);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Implements MD5 hashing",VF_VENDOR);
        }
index 9ff17924d1c38bbb154c4a959fa151824894a221..3021b1771e65940e52cdb1b57a09ab431047132c 100644 (file)
@@ -24,8 +24,8 @@
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides channel mode +f (message flood protection) */
+#include "modules/ctctags.h"
+#include "modules/exemption.h"
 
 /** Holds flood settings and state for mode +f
  */
@@ -36,9 +36,12 @@ 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)
+       floodsettings(bool a, unsigned int b, unsigned int c)
+               : ban(a)
+               , secs(b)
+               , lines(c)
        {
                reset = ServerInstance->Time() + secs;
        }
@@ -56,92 +59,80 @@ 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);
        }
 };
 
 /** Handles channel mode +f
  */
-class MsgFlood : public ModeHandler
+class MsgFlood : public ParamMode<MsgFlood, SimpleExtItem<floodsettings> >
 {
  public:
-       SimpleExtItem<floodsettings> ext;
-       MsgFlood(Module* Creator) : ModeHandler(Creator, "flood", 'f', PARAM_SETONLY, MODETYPE_CHANNEL),
-               ext("messageflood", Creator) { }
+       MsgFlood(Module* Creator)
+               : ParamMode<MsgFlood, SimpleExtItem<floodsettings> >(Creator, "flood", 'f')
+       {
+       }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
        {
-               if (adding)
+               std::string::size_type colon = parameter.find(':');
+               if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
                {
-                       std::string::size_type colon = parameter.find(':');
-                       if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
-                       {
-                               source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
-                               return MODEACTION_DENY;
-                       }
-
-                       /* Set up the flood parameters for this channel */
-                       bool ban = (parameter[0] == '*');
-                       unsigned int nlines = ConvToInt(parameter.substr(ban ? 1 : 0, ban ? colon-1 : colon));
-                       unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
-
-                       if ((nlines<2) || (nsecs<1))
-                       {
-                               source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
-                               return MODEACTION_DENY;
-                       }
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+                       return MODEACTION_DENY;
+               }
 
-                       floodsettings* f = ext.get(channel);
-                       if ((f) && (nlines == f->lines) && (nsecs == f->secs) && (ban == f->ban))
-                               // mode params match
-                               return MODEACTION_DENY;
+               /* Set up the flood parameters for this channel */
+               bool ban = (parameter[0] == '*');
+               unsigned int nlines = ConvToNum<unsigned int>(parameter.substr(ban ? 1 : 0, ban ? colon-1 : colon));
+               unsigned int nsecs = ConvToNum<unsigned int>(parameter.substr(colon+1));
 
-                       ext.set(channel, new floodsettings(ban, nsecs, nlines));
-                       parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" + ConvToStr(nsecs);
-                       channel->SetModeParam('f', parameter);
-                       return MODEACTION_ALLOW;
-               }
-               else
+               if ((nlines<2) || (nsecs<1))
                {
-                       if (!channel->IsModeSet('f'))
-                               return MODEACTION_DENY;
-
-                       ext.unset(channel);
-                       channel->SetModeParam('f', "");
-                       return MODEACTION_ALLOW;
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+                       return MODEACTION_DENY;
                }
+
+               ext.set(channel, new floodsettings(ban, nsecs, nlines));
+               return MODEACTION_ALLOW;
+       }
+
+       void SerializeParam(Channel* chan, const floodsettings* fs, std::string& out)
+       {
+               if (fs->ban)
+                       out.push_back('*');
+               out.append(ConvToStr(fs->lines)).push_back(':');
+               out.append(ConvToStr(fs->secs));
        }
 };
 
-class ModuleMsgFlood : public Module
+class ModuleMsgFlood
+       : public Module
+       , public CTCTags::EventListener
 {
+private:
+       CheckExemption::EventProvider exemptionprov;
        MsgFlood mf;
 
  public:
-
        ModuleMsgFlood()
-               : mf(this)
+               : CTCTags::EventListener(this)
+               , exemptionprov(this)
+               , mf(this)
        {
        }
 
-       void init()
+       ModResult HandleMessage(User* user, const MessageTarget& target)
        {
-               ServerInstance->Modules->AddService(mf);
-               ServerInstance->Modules->AddService(mf.ext);
-               Implementation eventlist[] = { I_OnUserPreNotice, I_OnUserPreMessage };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
+               if (target.type != MessageTarget::TYPE_CHANNEL)
+                       return MOD_RES_PASSTHRU;
 
-       ModResult ProcessMessages(User* user,Channel* dest, const std::string &text)
-       {
-               if ((!IS_LOCAL(user)) || !dest->IsModeSet('f'))
+               Channel* dest = target.Get<Channel>();
+               if ((!IS_LOCAL(user)) || !dest->IsModeSet(mf))
                        return MOD_RES_PASSTHRU;
 
-               if (ServerInstance->OnCheckExemption(user,dest,"flood") == MOD_RES_ALLOW)
+               ModResult res = CheckExemption::Call(exemptionprov, user, dest, "flood");
+               if (res == MOD_RES_ALLOW)
                        return MOD_RES_PASSTHRU;
 
                floodsettings *f = mf.ext.get(dest);
@@ -153,17 +144,15 @@ 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->MakeWildHost());
-                                       ServerInstance->SendGlobalMode(parameters, ServerInstance->FakeClient);
+                                       Modes::ChangeList changelist;
+                                       changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->GetDisplayedHost());
+                                       ServerInstance->Modes->Process(ServerInstance->FakeClient, dest, NULL, changelist);
                                }
 
-                               char kickmessage[MAXBUF];
-                               snprintf(kickmessage, MAXBUF, "Channel flood triggered (limit is %u lines in %u secs)", f->lines, f->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);
+                               dest->KickUser(ServerInstance->FakeClient, user, kickMessage);
 
                                return MOD_RES_DENY;
                        }
@@ -172,32 +161,25 @@ class ModuleMsgFlood : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnUserPreMessage(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               if (target_type == TYPE_CHANNEL)
-                       return ProcessMessages(user,(Channel*)dest,text);
-
-               return MOD_RES_PASSTHRU;
+               return HandleMessage(user, target);
        }
 
-       ModResult OnUserPreNotice(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
        {
-               if (target_type == TYPE_CHANNEL)
-                       return ProcessMessages(user,(Channel*)dest,text);
-
-               return MOD_RES_PASSTHRU;
+               return HandleMessage(user, target);
        }
 
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                // we want to be after all modules that might deny the message (e.g. m_muteban, m_noctcp, m_blockcolor, etc.)
                ServerInstance->Modules->SetPriority(this, I_OnUserPreMessage, PRIORITY_LAST);
-               ServerInstance->Modules->SetPriority(this, I_OnUserPreNotice, PRIORITY_LAST);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides channel mode +f (message flood protection)", VF_VENDOR);
+               return Version("Provides channel mode +f, message flood protection", VF_VENDOR);
        }
 };
 
index d1df81354b65e7ce9c64b5b9cbbbe1b4026488e4..59748048e6fb7174b2150f63a1f0ac1c87b21f67 100644 (file)
  */
 
 
-/* $ModDesc: Implements the ability to have server-side MLOCK enforcement. */
-
 #include "inspircd.h"
 
+enum
+{
+       // From Charybdis.
+       ERR_MLOCKRESTRICTED = 742
+};
+
 class ModuleMLock : public Module
 {
-private:
        StringExtItem mlock;
 
-public:
-       ModuleMLock() : mlock("mlock", this) {};
-
-       void init()
+ public:
+       ModuleMLock()
+               : mlock("mlock", ExtensionItem::EXT_CHANNEL, this)
        {
-               ServerInstance->Modules->Attach(I_OnRawMode, this);
-               ServerInstance->Modules->AddService(this->mlock);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Implements the ability to have server-side MLOCK enforcement.", VF_VENDOR);
+               return Version("Implements the ability to have server-side MLOCK enforcement", VF_VENDOR);
        }
 
-       ModResult OnRawMode(User* source, Channel* channel, const char mode, const std::string& parameter, bool adding, int pcnt)
+       ModResult OnRawMode(User* source, Channel* channel, ModeHandler* mh, const std::string& parameter, bool adding) CXX11_OVERRIDE
        {
                if (!channel)
                        return MOD_RES_PASSTHRU;
@@ -52,17 +52,16 @@ public:
                if (!mlock_str)
                        return MOD_RES_PASSTHRU;
 
+               const char mode = mh->GetModeChar();
                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(ERR_MLOCKRESTRICTED, channel->name, mode, *mlock_str, "MODE cannot be set due to the channel having an active MLOCK restriction policy");
                        return MOD_RES_DENY;
                }
 
                return MOD_RES_PASSTHRU;
        }
-
 };
 
 MODULE_INIT(ModuleMLock)
diff --git a/src/modules/m_modenotice.cpp b/src/modules/m_modenotice.cpp
new file mode 100644 (file)
index 0000000..0619333
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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 CommandModeNotice : public Command
+{
+ public:
+       CommandModeNotice(Module* parent) : Command(parent,"MODENOTICE",2,2)
+       {
+               syntax = "<modeletters> :<message>";
+               flags_needed = 'o';
+       }
+
+       CmdResult Handle(User* src, const Params& parameters) CXX11_OVERRIDE
+       {
+               std::string msg = "*** From " + src->nick + ": " + parameters[1];
+               int mlen = parameters[0].length();
+               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++)
+                       {
+                               if (!user->IsModeSet(parameters[0][n]))
+                                       goto next_user;
+                       }
+                       user->WriteNotice(msg);
+next_user:     ;
+               }
+               return CMD_SUCCESS;
+       }
+
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               return ROUTE_OPT_BCAST;
+       }
+};
+
+class ModuleModeNotice : public Module
+{
+       CommandModeNotice cmd;
+
+ public:
+       ModuleModeNotice()
+               : cmd(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the MODENOTICE command", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleModeNotice)
diff --git a/src/modules/m_monitor.cpp b/src/modules/m_monitor.cpp
new file mode 100644 (file)
index 0000000..b82dbcc
--- /dev/null
@@ -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(container, unset_raw(container));
+               }
+
+               std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
+               {
+                       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) CXX11_OVERRIDE;
+
+               void free(Extensible* container, void* item) CXX11_OVERRIDE
+               {
+                       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|(+|-) <nick>[,<nick>]+";
+       }
+
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               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->getUInt("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
index 767af29017b7dde4751342f3918a479e3fac4564..acfcb1801260b7a6b145a59393ea2b1524bfcff6 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/ctctags.h"
 
-/* $ModDesc: Implements extban +b m: - mute bans */
-
-class ModuleQuietBan : public Module
+class ModuleQuietBan
+       : public Module
+       , public CTCTags::EventListener
 {
  private:
+       bool notifyuser;
+
  public:
-       void init()
+       ModuleQuietBan()
+               : CTCTags::EventListener(this)
        {
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       virtual ~ModuleQuietBan()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("muteban");
+               notifyuser = tag->getBool("notifyuser", true);
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Implements extban +b m: - mute bans",VF_OPTCOMMON|VF_VENDOR);
+               return Version("Provides extban 'm', mute bans", VF_OPTCOMMON|VF_VENDOR);
        }
 
-       virtual ModResult OnUserPreMessage(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult HandleMessage(User* user, const MessageTarget& target, bool& echo_original)
        {
-               if (!IS_LOCAL(user) || target_type != TYPE_CHANNEL)
+               if (!IS_LOCAL(user) || target.type != MessageTarget::TYPE_CHANNEL)
                        return MOD_RES_PASSTHRU;
 
-               Channel* chan = static_cast<Channel*>(dest);
+               Channel* chan = target.Get<Channel>();
                if (chan->GetExtBanStatus(user, 'm') == MOD_RES_DENY && chan->GetPrefixValue(user) < VOICE_VALUE)
                {
-                       user->WriteNumeric(404, "%s %s :Cannot send to channel (you're muted)", user->nick.c_str(), chan->name.c_str());
+                       if (!notifyuser)
+                       {
+                               echo_original = true;
+                               return MOD_RES_DENY;
+                       }
+
+                       user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're muted)");
                        return MOD_RES_DENY;
                }
 
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ModResult OnUserPreNotice(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
+               return HandleMessage(user, target, details.echo_original);
        }
 
-       virtual void On005Numeric(std::string &output)
+       ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('m');
+               return HandleMessage(user, target, details.echo_original);
        }
-};
 
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+       {
+               tokens["EXTBAN"].push_back('m');
+       }
+};
 
 MODULE_INIT(ModuleQuietBan)
-
index 46710946b67a7adde9193e01aa4a14733a8d93d3..2fbdca26561856ca5ecc29ac6ae66b12a73d9248 100644 (file)
  */
 
 
-/* $ModDesc: Provides the ability to manipulate modes via long names. */
-
 #include "inspircd.h"
 
-static void DisplayList(User* user, Channel* channel)
+enum
 {
-       std::stringstream items;
-       for(char letter = 'A'; letter <= 'z'; letter++)
+       // InspIRCd-specific.
+       RPL_ENDOFPROPLIST = 960,
+       RPL_PROPLIST = 961
+};
+
+static void DisplayList(LocalUser* user, Channel* channel)
+{
+       Numeric::ParamBuilder<1> numeric(user, RPL_PROPLIST);
+       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 = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
-               if (!mh || mh->IsListMode())
+               ModeHandler* mh = i->second;
+               if (!channel->IsModeSet(mh))
                        continue;
-               if (!channel->IsModeSet(letter))
-                       continue;
-               items << " +" << mh->name;
-               if (mh->GetNumParams(true))
+               numeric.Add("+" + mh->name);
+               ParamModeBase* pm = mh->IsParameterMode();
+               if (pm)
                {
-                       if ((letter == 'k') && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
-                               items << " <key>";
+                       if ((pm->IsParameterSecret()) && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
+                               numeric.Add("<" + mh->name + ">");
                        else
-                               items << " " << channel->GetModeParameter(letter);
+                               numeric.Add(channel->GetModeParameter(mh));
                }
        }
-       char pfx[MAXBUF];
-       snprintf(pfx, MAXBUF, ":%s 961 %s %s", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), channel->name.c_str());
-       user->SendText(std::string(pfx), items);
-       user->WriteNumeric(960, "%s %s :End of mode list", user->nick.c_str(), channel->name.c_str());
+       numeric.Flush();
+       user->WriteNumeric(RPL_ENDOFPROPLIST, 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>]}*";
+               syntax = "<channel> [[(+|-)]<mode> [<value>]]";
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *src)
+       CmdResult HandleLocal(LocalUser* src, const Params& parameters) CXX11_OVERRIDE
        {
+               Channel* const chan = ServerInstance->FindChan(parameters[0]);
+               if (!chan)
+               {
+                       src->WriteNumeric(Numerics::NoSuchChannel(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++];
@@ -76,21 +85,19 @@ class CommandProp : public Command
                        if (prop[0] == '+' || prop[0] == '-')
                                prop.erase(prop.begin());
 
-                       for(char letter = 'A'; letter <= 'z'; letter++)
+                       ModeHandler* mh = ServerInstance->Modes->FindMode(prop, MODETYPE_CHANNEL);
+                       if (mh)
                        {
-                               ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
-                               if (mh && mh->name == prop)
+                               if (mh->NeedsParam(plus))
                                {
-                                       modes[1].append((plus ? "+" : "-") + std::string(1, letter));
-                                       if (mh->GetNumParams(plus))
-                                       {
-                                               if (i != parameters.size())
-                                                       modes.push_back(parameters[i++]);
-                                       }
+                                       if (i != parameters.size())
+                                               modes.push(mh, plus, parameters[i++]);
                                }
+                               else
+                                       modes.push(mh, plus);
                        }
                }
-               ServerInstance->SendGlobalMode(modes, src);
+               ServerInstance->Modes->ProcessSingle(src, chan, NULL, modes, ModeParser::MODE_CHECKACCESS);
                return CMD_SUCCESS;
        }
 };
@@ -102,6 +109,13 @@ class DummyZ : public ModeHandler
        {
                list = true;
        }
+
+       // Handle /MODE #chan Z
+       void DisplayList(User* user, Channel* chan) CXX11_OVERRIDE
+       {
+               if (IS_LOCAL(user))
+                       ::DisplayList(static_cast<LocalUser*>(user), chan);
+       }
 };
 
 class ModuleNamedModes : public Module
@@ -113,99 +127,69 @@ class ModuleNamedModes : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-               ServerInstance->Modules->AddService(dummyZ);
-
-               Implementation eventlist[] = { I_OnPreMode };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides the ability to manipulate modes via long names.",VF_VENDOR);
+               return Version("Provides the ability to manipulate modes via long names", VF_VENDOR);
        }
 
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_FIRST);
        }
 
-       ModResult OnPreMode(User* source, User* dest, Channel* channel, const std::vector<std::string>& parameters)
+       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 == '-')
+                       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)
                        {
-                               adding = (modechar == '+');
-                               continue;
-                       }
-                       ModeHandler *mh = ServerInstance->Modes->FindMode(modechar, MODETYPE_CHANNEL);
-                       if (modechar == 'Z')
-                       {
-                               modechar = 0;
-                               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);
+                               }
+
+                               ModeHandler* mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL);
+                               if (!mh)
+                               {
+                                       // Mode handler not found
+                                       i = list.erase(i);
+                                       continue;
                                }
-                               for(char letter = 'A'; modechar == 0 && letter <= 'z'; letter++)
+
+                               curr.param.clear();
+                               if (mh->NeedsParam(curr.adding))
                                {
-                                       mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
-                                       if (mh && mh->name == name)
+                                       if (value.empty())
                                        {
-                                               if (mh->GetNumParams(adding))
-                                               {
-                                                       if (!value.empty())
-                                                       {
-                                                               newparms.push_back(value);
-                                                               modechar = letter;
-                                                               break;
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       modechar = letter;
-                                                       break;
-                                               }
+                                               // Mode needs a parameter but there wasn't one
+                                               i = list.erase(i);
+                                               continue;
                                        }
+
+                                       // Change parameter to the text after the '='
+                                       curr.param = value;
                                }
-                               if (modechar)
-                                       modelist[i] = modechar;
-                               else
-                                       modelist.erase(i--, 1);
-                       }
-                       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, false);
-               return MOD_RES_DENY;
+
+               return MOD_RES_PASSTHRU;
        }
 };
 
index 82d311773c2b52ba0d7ce14b50140e65efe868dd..2b4fd87b4daf50c87b209a9832500a122cc77509 100644 (file)
 
 
 #include "inspircd.h"
-#include "m_cap.h"
-
-/* $ModDesc: Provides the NAMESX (CAP multi-prefix) capability. */
-
-class ModuleNamesX : public Module
+#include "modules/cap.h"
+#include "modules/names.h"
+#include "modules/who.h"
+
+class ModuleNamesX
+       : public Module
+       , public Names::EventListener
+       , public Who::EventListener
 {
- public:
-       GenericCap cap;
-       ModuleNamesX() : cap(this, "multi-prefix")
-       {
-       }
+ private:
+       Cap::Capability cap;
 
-       void init()
-       {
-               Implementation eventlist[] = { I_OnPreCommand, I_OnNamesListItem, I_On005Numeric, I_OnEvent, I_OnSendWhoLine };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-
-       ~ModuleNamesX()
+ public:
+       ModuleNamesX()
+               : Names::EventListener(this)
+               , Who::EventListener(this)
+               , cap(this, "multi-prefix")
        {
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides the NAMESX (CAP multi-prefix) capability.",VF_VENDOR);
+               return Version("Provides the NAMESX (CAP multi-prefix) capability", VF_VENDOR);
        }
 
-       void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               output.append(" NAMESX");
+               tokens["NAMESX"];
        }
 
-       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
        {
                /* We don't actually create a proper command handler class for PROTOCTL,
                 * because other modules might want to have PROTOCTL hooks too.
@@ -65,66 +62,41 @@ 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;
                        }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+       ModResult OnNamesListItem(LocalUser* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
        {
-               if (!cap.ext.get(issuer))
-                       return;
+               if (cap.get(issuer))
+                       prefixes = memb->GetAllPrefixChars();
 
-               /* Some module hid this from being displayed, dont bother */
-               if (nick.empty())
-                       return;
-
-               prefixes = memb->chan->GetAllPrefixChars(memb->user);
+               return MOD_RES_PASSTHRU;
        }
 
-       void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
+       ModResult OnWhoLine(const Who::Request& request, LocalUser* source, User* user, Membership* memb, Numeric::Numeric& numeric) CXX11_OVERRIDE
        {
-               if (!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
-               //              a     b                                       pos
-               std::string::size_type a = 4 + source->nick.length() + 1;
-               std::string::size_type b = line.find(' ', a);
-               if (b == std::string::npos)
-                       return;
-
-               // Try to find this channel
-               std::string channame = line.substr(a, b-a);
-               Channel* chan = ServerInstance->FindChan(channame);
-               if (!chan)
-                       return;
+               if ((!memb) || (!cap.get(source)))
+                       return MOD_RES_PASSTHRU;
 
                // Don't do anything if the user has only one prefix
-               std::string prefixes = chan->GetAllPrefixChars(user);
+               std::string prefixes = memb->GetAllPrefixChars();
                if (prefixes.length() <= 1)
-                       return;
+                       return MOD_RES_PASSTHRU;
 
-               line.erase(pos, 1);
-               line.insert(pos, prefixes);
-       }
+               size_t flag_index;
+               if (!request.GetFieldIndex('f', flag_index))
+                       return MOD_RES_PASSTHRU;
 
-       void OnEvent(Event& ev)
-       {
-               cap.HandleEvent(ev);
+               // #chan ident localhost insp22.test nick H@ :0 Attila
+               if (numeric.GetParams().size() <= flag_index)
+                       return MOD_RES_PASSTHRU;
+
+               numeric.GetParams()[flag_index].append(prefixes, 1, std::string::npos);
+               return MOD_RES_PASSTHRU;
        }
 };
 
index bc90c9fad5a620ce6050cbd83430f59a8b5a3c5c..2b7e66a5038e2bcf9a6791331312de0cebbda0c0 100644 (file)
  */
 
 
-/* Contains a code of Unreal IRCd + Bynets patch ( http://www.unrealircd.com/ and http://www.bynets.org/ )
-   Original patch is made by Dmitry "Killer{R}" Kononko. ( http://killprog.com/ )
+/* Contains a code of Unreal IRCd + Bynets patch (https://www.unrealircd.org and https://bynets.org)
+   Original patch is made by Dmitry "Killer{R}" Kononko. (http://killprog.com)
    Changed at 2008-06-15 - 2009-02-11
    by Chernov-Phoenix Alexey (Phoenix@RusNet) mailto:phoenix /email address separator/ pravmail.ru */
 
 #include "inspircd.h"
-#include "caller.h"
 #include <fstream>
 
-/* $ModDesc: Provides an ability to have non-RFC1459 nicks & support for national CASEMAPPING */
-
-class lwbNickHandler : public HandlerBase2<bool, const char*, size_t>
+class lwbNickHandler
 {
  public:
-       lwbNickHandler() { }
-       virtual ~lwbNickHandler() { }
-       virtual bool Call(const char*, size_t);
+       static bool Call(const std::string&);
 };
 
                                                                 /*,m_reverse_additionalUp[256];*/
@@ -71,11 +66,12 @@ char utf8size(unsigned char * mb)
 
 
 /* Conditions added */
-bool lwbNickHandler::Call(const char* n, size_t max)
+bool lwbNickHandler::Call(const std::string& nick)
 {
-       if (!n || !*n)
+       if (nick.empty())
                return false;
 
+       const char* n = nick.c_str();
        unsigned int p = 0;
        for (const char* i = n; *i; i++, p++)
        {
@@ -215,21 +211,28 @@ bool lwbNickHandler::Call(const char* n, size_t max)
        }
 
        /* too long? or not -- pointer arithmetic rocks */
-       return (p < max);
+       return (p < ServerInstance->Config->Limits.NickMax);
 }
 
 
 class ModuleNationalChars : public Module
 {
- private:
-       lwbNickHandler myhandler;
-       std::string charset, casemapping;
+       std::string charset;
        unsigned char m_additional[256], m_additionalUp[256], m_lower[256], m_upper[256];
-       caller2<bool, const char*, size_t> rememberer;
+       TR1NS::function<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
@@ -238,20 +241,9 @@ class ModuleNationalChars : public Module
 
                memcpy(prev_map, national_case_insensitive_map, sizeof(prev_map));
 
-               ServerInstance->RehashUsersAndChans();
-
-               // The OnGarbageCollect() method in m_watch rebuilds the hashmap used by it
-               Module* mod = ServerInstance->Modules->Find("m_watch.so");
-               if (mod)
-                       mod->OnGarbageCollect();
-
-               // Send a Request to m_spanningtree asking it to rebuild its hashmaps
-               mod = ServerInstance->Modules->Find("m_spanningtree.so");
-               if (mod)
-               {
-                       Request req(this, mod, "rehash");
-                       req.Send();
-               }
+               RehashHashmap(ServerInstance->Users.clientlist);
+               RehashHashmap(ServerInstance->Users.uuidlist);
+               RehashHashmap(ServerInstance->chanlist);
        }
 
  public:
@@ -261,34 +253,24 @@ class ModuleNationalChars : public Module
                memcpy(prev_map, national_case_insensitive_map, sizeof(prev_map));
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
                memcpy(m_lower, rfc_case_insensitive_map, 256);
                national_case_insensitive_map = m_lower;
 
-               ServerInstance->IsNick = &myhandler;
-
-               Implementation eventlist[] = { I_OnRehash, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
-       }
-
-       virtual void On005Numeric(std::string &output)
-       {
-               std::string tmp(casemapping);
-               tmp.insert(0, "CASEMAPPING=");
-               SearchAndReplace(output, std::string("CASEMAPPING=rfc1459"), tmp);
+               ServerInstance->IsNick = &lwbNickHandler::Call;
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("nationalchars");
                charset = tag->getString("file");
-               casemapping = tag->getString("casemapping", ServerConfig::CleanFilename(charset.c_str()));
+               std::string casemapping = tag->getString("casemapping", FileSystem::GetFileName(charset));
                if (casemapping.find(' ') != std::string::npos)
                        throw ModuleException("<nationalchars:casemapping> must not contain any spaces!");
+               ServerInstance->Config->CaseMapping = casemapping;
 #if defined _WIN32
-               if (!ServerInstance->Config->StartsWithWindowsDriveLetter(charset))
+               if (!FileSystem::StartsWithWindowsDriveLetter(charset))
                        charset.insert(0, "./locales/");
 #else
                if(charset[0] != '/')
@@ -307,16 +289,19 @@ 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;
-                       if (!isdigit(n->nick[0]) && !ServerInstance->IsNick(n->nick.c_str(), ServerInstance->Config->Limits.NickMax))
+                       ++iter;
+                       if (!isdigit(n->nick[0]) && !ServerInstance->IsNick(n->nick))
                                ServerInstance->Users->QuitUser(n, message);
                }
        }
 
-       virtual ~ModuleNationalChars()
+       ~ModuleNationalChars()
        {
                ServerInstance->IsNick = rememberer;
                national_case_insensitive_map = lowermap_rememberer;
@@ -324,7 +309,7 @@ class ModuleNationalChars : public Module
                CheckRehash();
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides an ability to have non-RFC1459 nicks & support for national CASEMAPPING", VF_VENDOR | VF_COMMON, charset);
        }
@@ -340,10 +325,10 @@ class ModuleNationalChars : public Module
        /*so Bynets Unreal distribution stuff*/
        bool loadtables(std::string filename, unsigned char ** tables, unsigned char cnt, char faillimit)
        {
-               std::ifstream ifs(filename.c_str());
+               std::ifstream ifs(ServerInstance->Config->Paths.PrependConfig(filename).c_str());
                if (ifs.fail())
                {
-                       ServerInstance->Logs->Log("m_nationalchars",DEFAULT,"loadtables() called for missing file: %s", filename.c_str());
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "loadtables() called for missing file: %s", filename.c_str());
                        return false;
                }
 
@@ -358,7 +343,7 @@ class ModuleNationalChars : public Module
                {
                        if (loadtable(ifs, tables[n], 255) && (n < faillimit))
                        {
-                               ServerInstance->Logs->Log("m_nationalchars",DEFAULT,"loadtables() called for illegal file: %s (line %d)", filename.c_str(), n+1);
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "loadtables() called for illegal file: %s (line %d)", filename.c_str(), n+1);
                                return false;
                        }
                }
index 04d7c8b5e2cc86d28e812cf578bcc4a64639f282..17d6db9560184802169389466bb70122bc64e580 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/exemption.h"
 
-/* $ModDesc: Provides channel mode +F (nick flood protection) */
+// The number of seconds nickname changing will be blocked for.
+static unsigned int duration;
 
 /** Holds settings and state associated with channel mode +F
  */
@@ -74,101 +76,85 @@ class nickfloodsettings
 
        void lock()
        {
-               unlocktime = ServerInstance->Time() + 60;
+               unlocktime = ServerInstance->Time() + duration;
        }
 };
 
 /** Handles channel mode +F
  */
-class NickFlood : public ModeHandler
+class NickFlood : public ParamMode<NickFlood, SimpleExtItem<nickfloodsettings> >
 {
  public:
-       SimpleExtItem<nickfloodsettings> ext;
-       NickFlood(Module* Creator) : ModeHandler(Creator, "nickflood", 'F', PARAM_SETONLY, MODETYPE_CHANNEL),
-               ext("nickflood", Creator) { }
+       NickFlood(Module* Creator)
+               : ParamMode<NickFlood, SimpleExtItem<nickfloodsettings> >(Creator, "nickflood", 'F')
+       {
+       }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
        {
-               if (adding)
+               std::string::size_type colon = parameter.find(':');
+               if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
                {
-                       std::string::size_type colon = parameter.find(':');
-                       if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
-                       {
-                               source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
-                               return MODEACTION_DENY;
-                       }
-
-                       /* Set up the flood parameters for this channel */
-                       unsigned int nnicks = ConvToInt(parameter.substr(0, colon));
-                       unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
-
-                       if ((nnicks<1) || (nsecs<1))
-                       {
-                               source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
-                               return MODEACTION_DENY;
-                       }
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+                       return MODEACTION_DENY;
+               }
 
-                       nickfloodsettings* f = ext.get(channel);
-                       if ((f) && (nnicks == f->nicks) && (nsecs == f->secs))
-                               // mode params match
-                               return MODEACTION_DENY;
+               /* Set up the flood parameters for this channel */
+               unsigned int nnicks = ConvToNum<unsigned int>(parameter.substr(0, colon));
+               unsigned int nsecs = ConvToNum<unsigned int>(parameter.substr(colon+1));
 
-                       ext.set(channel, new nickfloodsettings(nsecs, nnicks));
-                       parameter = ConvToStr(nnicks) + ":" + ConvToStr(nsecs);
-                       channel->SetModeParam('F', parameter);
-                       return MODEACTION_ALLOW;
-               }
-               else
+               if ((nnicks<1) || (nsecs<1))
                {
-                       if (!channel->IsModeSet('F'))
-                               return MODEACTION_DENY;
-
-                       ext.unset(channel);
-                       channel->SetModeParam('F', "");
-                       return MODEACTION_ALLOW;
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
+                       return MODEACTION_DENY;
                }
+
+               ext.set(channel, new nickfloodsettings(nsecs, nnicks));
+               return MODEACTION_ALLOW;
+       }
+
+       void SerializeParam(Channel* chan, const nickfloodsettings* nfs, std::string& out)
+       {
+               out.append(ConvToStr(nfs->nicks)).push_back(':');
+               out.append(ConvToStr(nfs->secs));
        }
 };
 
 class ModuleNickFlood : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        NickFlood nf;
 
  public:
-
        ModuleNickFlood()
-               : nf(this)
+               : exemptionprov(this)
+               , nf(this)
        {
        }
 
-       void init()
+       void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(nf);
-               ServerInstance->Modules->AddService(nf.ext);
-               Implementation eventlist[] = { I_OnUserPreNick, I_OnUserPostNick };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               ConfigTag* tag = ServerInstance->Config->ConfValue("nickflood");
+               duration = tag->getDuration("duration", 60, 10, 600);
        }
 
-       ModResult OnUserPreNick(User* user, const std::string &newnick)
+       ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
        {
-               if (ServerInstance->NICKForced.get(user)) /* Allow forced nick changes */
-                       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 *channel = *i;
+                       Channel* channel = (*i)->chan;
                        ModResult res;
 
                        nickfloodsettings *f = nf.ext.get(channel);
                        if (f)
                        {
-                               res = ServerInstance->OnCheckExemption(user,channel,"nickflood");
+                               res = CheckExemption::Call(exemptionprov, user, channel, "nickflood");
                                if (res == MOD_RES_ALLOW)
                                        continue;
 
                                if (f->islocked())
                                {
-                                       user->WriteNumeric(447, "%s :%s has been locked for nickchanges for 60 seconds because there have been more than %u nick changes in %u seconds", user->nick.c_str(), 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;
                                }
 
@@ -176,7 +162,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;
                                }
                        }
@@ -188,20 +174,20 @@ class ModuleNickFlood : public Module
        /*
         * XXX: HACK: We do the increment on the *POST* event here (instead of all together) because we have no way of knowing whether other modules would block a nickchange.
         */
-       void OnUserPostNick(User* user, const std::string &oldnick)
+       void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
        {
                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;
+                       Channel* channel = (*i)->chan;
                        ModResult res;
 
                        nickfloodsettings *f = nf.ext.get(channel);
                        if (f)
                        {
-                               res = ServerInstance->OnCheckExemption(user,channel,"nickflood");
+                               res = CheckExemption::Call(exemptionprov, user, channel, "nickflood");
                                if (res == MOD_RES_ALLOW)
                                        return;
 
@@ -214,13 +200,9 @@ class ModuleNickFlood : public Module
                }
        }
 
-       ~ModuleNickFlood()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Channel mode F - nick flood protection", VF_VENDOR);
+               return Version("Provides channel mode +F, nick flood protection", VF_VENDOR);
        }
 };
 
index 4f5e5941cea5d653bb2453e19e154b4fcb68d82b..86cf6245a75402b1b2d7865c54cce3a46681c33e 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides the NICKLOCK command, allows an oper to change a users nick and lock them to it until they quit */
+enum
+{
+       // InspIRCd-specific.
+       ERR_NICKNOTLOCKED = 946,
+       RPL_NICKLOCKON = 947,
+       RPL_NICKLOCKOFF = 945
+};
 
 /** Handle /NICKLOCK
  */
@@ -34,30 +40,30 @@ class CommandNicklock : public Command
                locked(ext)
        {
                flags_needed = 'o';
-               syntax = "<oldnick> <newnick>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               syntax = "<nick> <newnick>";
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle(const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                User* target = ServerInstance->FindNick(parameters[0]);
 
                if ((!target) || (target->registered != REG_ALL))
                {
-                       user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNotice("*** No such nickname: '" + parameters[0] + "'");
                        return CMD_FAILURE;
                }
 
                /* Do local sanity checks and bails */
                if (IS_LOCAL(user))
                {
-                       if (!ServerInstance->IsNick(parameters[1].c_str(), ServerInstance->Config->Limits.NickMax))
+                       if (!ServerInstance->IsNick(parameters[1]))
                        {
-                               user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick.c_str(), parameters[1].c_str());
+                               user->WriteNotice("*** Invalid nickname '" + parameters[1] + "'");
                                return CMD_FAILURE;
                        }
 
-                       user->WriteServ("947 %s %s :Nickname now locked.", user->nick.c_str(), parameters[1].c_str());
+                       user->WriteNumeric(RPL_NICKLOCKON, parameters[1], "Nickname now locked.");
                }
 
                /* If we made it this far, extend the user */
@@ -66,7 +72,7 @@ class CommandNicklock : public Command
                        locked.set(target, 1);
 
                        std::string oldnick = target->nick;
-                       if (target->ForceNickChange(parameters[1].c_str()))
+                       if (target->ChangeNick(parameters[1]))
                                ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used NICKLOCK to change and hold "+oldnick+" to "+parameters[1]);
                        else
                        {
@@ -78,12 +84,9 @@ class CommandNicklock : public Command
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -97,17 +100,17 @@ class CommandNickunlock : public Command
                locked(ext)
        {
                flags_needed = 'o';
-               syntax = "<locked-nick>";
-               TRANSLATE2(TR_NICK, TR_END);
+               syntax = "<nick>";
+               TRANSLATE1(TR_NICK);
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                User* target = ServerInstance->FindNick(parameters[0]);
 
                if (!target)
                {
-                       user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNotice("*** No such nickname: '" + parameters[0] + "'");
                        return CMD_FAILURE;
                }
 
@@ -116,13 +119,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(RPL_NICKLOCKOFF, 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(ERR_NICKNOTLOCKED, target->nick, "This user's nickname is not locked.");
                                return CMD_FAILURE;
                        }
                }
@@ -130,16 +131,12 @@ class CommandNickunlock : public Command
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
-
 class ModuleNickLock : public Module
 {
        LocalIntExt locked;
@@ -147,47 +144,31 @@ class ModuleNickLock : public Module
        CommandNickunlock cmd2;
  public:
        ModuleNickLock()
-               : locked("nick_locked", this), cmd1(this, locked), cmd2(this, locked)
-       {
-       }
-
-       void init()
+               : locked("nick_locked", ExtensionItem::EXT_USER, this)
+               , cmd1(this, locked)
+               , cmd2(this, locked)
        {
-               ServerInstance->Modules->AddService(cmd1);
-               ServerInstance->Modules->AddService(cmd2);
-               ServerInstance->Modules->AddService(locked);
-               ServerInstance->Modules->Attach(I_OnUserPreNick, this);
        }
 
-       ~ModuleNickLock()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                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)
+       ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
        {
-               if (!IS_LOCAL(user))
-                       return MOD_RES_PASSTHRU;
-
-               if (ServerInstance->NICKForced.get(user)) /* Allow forced nick changes */
-                       return MOD_RES_PASSTHRU;
-
                if (locked.get(user))
                {
-                       user->WriteNumeric(447, "%s :You cannot change your nickname (your nick is locked)",user->nick.c_str());
+                       user->WriteNumeric(ERR_CANTCHANGENICK, "You cannot change your nickname (your nick is locked)");
                        return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                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);
        }
 };
 
index c934a05c6edbfbe254d17b28f27d8c84d7a1fb5d..c73f8308e08005e1fbb25010d065f942a4263ae7 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/exemption.h"
 
-/* $ModDesc: Provides channel mode +C to block CTCPs */
-
-class NoCTCP : public SimpleChannelModeHandler
+class NoCTCPUser : public SimpleUserModeHandler
 {
- public:
-       NoCTCP(Module* Creator) : SimpleChannelModeHandler(Creator, "noctcp", 'C') { }
+public:
+       NoCTCPUser(Module* Creator)
+               : SimpleUserModeHandler(Creator, "u_noctcp", 'T')
+       {
+               if (!ServerInstance->Config->ConfValue("noctcp")->getBool("enableumode"))
+                       DisableAutoRegister();
+       }
 };
 
 class ModuleNoCTCP : public Module
 {
-
-       NoCTCP nc;
+       CheckExemption::EventProvider exemptionprov;
+       SimpleChannelModeHandler nc;
+       NoCTCPUser ncu;
 
  public:
-
        ModuleNoCTCP()
-               : nc(this)
+               : exemptionprov(this)
+               , nc(this, "noctcp", 'C')
+               , ncu(this)
        {
        }
 
-       void init()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(nc);
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               return Version("Provides user mode +T and channel mode +C to block CTCPs", VF_VENDOR);
        }
 
-       virtual ~ModuleNoCTCP()
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides channel mode +C to block CTCPs", VF_VENDOR);
-       }
+               if (!IS_LOCAL(user))
+                       return MOD_RES_PASSTHRU;
 
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
-       }
+               std::string ctcpname;
+               if (!details.IsCTCP(ctcpname) || irc::equals(ctcpname, "ACTION"))
+                       return MOD_RES_PASSTHRU;
 
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+               if (target.type == MessageTarget::TYPE_CHANNEL)
                {
-                       Channel* c = (Channel*)dest;
-                       if ((text.empty()) || (text[0] != '\001') || (!strncmp(text.c_str(),"\1ACTION ", 8)) || (text == "\1ACTION\1") || (text == "\1ACTION"))
+                       if (user->HasPrivPermission("channels/ignore-noctcp"))
                                return MOD_RES_PASSTHRU;
 
-                       ModResult res = ServerInstance->OnCheckExemption(user,c,"noctcp");
+                       Channel* c = target.Get<Channel>();
+                       ModResult res = CheckExemption::Call(exemptionprov, user, c, "noctcp");
                        if (res == MOD_RES_ALLOW)
                                return MOD_RES_PASSTHRU;
 
-                       if (!c->GetExtBanStatus(user, 'C').check(!c->IsModeSet('C')))
+                       if (!c->GetExtBanStatus(user, 'C').check(!c->IsModeSet(nc)))
+                       {
+                               user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send CTCP to channel (+C is set)");
+                               return MOD_RES_DENY;
+                       }
+               }
+               else if (target.type == MessageTarget::TYPE_USER)
+               {
+                       if (user->HasPrivPermission("users/ignore-noctcp"))
+                               return MOD_RES_PASSTHRU;
+
+                       User* u = target.Get<User>();
+                       if (u->IsModeSet(ncu))
                        {
-                               user->WriteNumeric(ERR_NOCTCPALLOWED, "%s %s :Can't send CTCP to channel (+C set)",user->nick.c_str(), c->name.c_str());
+                               user->WriteNumeric(ERR_CANTSENDTOUSER, u->nick, "Can't send CTCP to user (+T is set)");
                                return MOD_RES_DENY;
                        }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       virtual void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('C');
+               tokens["EXTBAN"].push_back('C');
        }
 };
 
index 1f58a2e08b71a7663ef052acd14b89e92a2610b4..6cd91c55bc06143baa53b2a21e3bca9301050eaa 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +Q to prevent kicks on the channel. */
-
-class NoKicks : public SimpleChannelModeHandler
-{
- public:
-       NoKicks(Module* Creator) : SimpleChannelModeHandler(Creator, "nokick", 'Q') { }
-};
-
 class ModuleNoKicks : public Module
 {
-       NoKicks nk;
+       SimpleChannelModeHandler nk;
 
  public:
        ModuleNoKicks()
-               : nk(this)
-       {
-       }
-
-       void init()
+               : nk(this, "nokick", 'Q')
        {
-               ServerInstance->Modules->AddService(nk);
-               Implementation eventlist[] = { I_OnUserPreKick, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('Q');
+               tokens["EXTBAN"].push_back('Q');
        }
 
-       ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason)
+       ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) CXX11_OVERRIDE
        {
-               if (!memb->chan->GetExtBanStatus(source, 'Q').check(!memb->chan->IsModeSet('Q')))
+               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 %s :Can't kick user %s from channel (+Q set)",source->nick.c_str(), 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 is set)", memb->user->nick.c_str()));
                        return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       ~ModuleNoKicks()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides channel mode +Q to prevent kicks on the channel.", VF_VENDOR);
+               return Version("Provides channel mode +Q to prevent kicks on the channel", VF_VENDOR);
        }
 };
 
-
 MODULE_INIT(ModuleNoKicks)
index 672a48f8d9bca45091d980ebb5290efb4f2a64ca..a796495a81d95a0b6e72fb883a85a8efe996251c 100644 (file)
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides support for channel mode +N & extban +b N: which prevents nick changes on channel */
-
-class NoNicks : public SimpleChannelModeHandler
-{
- public:
-       NoNicks(Module* Creator) : SimpleChannelModeHandler(Creator, "nonick", 'N') { }
-};
+#include "modules/exemption.h"
 
 class ModuleNoNickChange : public Module
 {
-       NoNicks nn;
-       bool override;
+       CheckExemption::EventProvider exemptionprov;
+       SimpleChannelModeHandler nn;
  public:
-       ModuleNoNickChange() : nn(this)
+       ModuleNoNickChange()
+               : exemptionprov(this)
+               , nn(this, "nonick", 'N')
        {
        }
 
-       void init()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               OnRehash(NULL);
-               ServerInstance->Modules->AddService(nn);
-               Implementation eventlist[] = { I_OnUserPreNick, I_On005Numeric, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               return Version("Provides channel mode +N and extban 'N' which prevents nick changes on the channel", VF_VENDOR);
        }
 
-       virtual ~ModuleNoNickChange()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
+               tokens["EXTBAN"].push_back('N');
        }
 
-       virtual Version GetVersion()
-       {
-               return Version("Provides support for channel mode +N & extban +b N: which prevents nick changes on channel", VF_VENDOR);
-       }
-
-
-       virtual void On005Numeric(std::string &output)
+       ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('N');
-       }
-
-       virtual ModResult OnUserPreNick(User* user, const std::string &newnick)
-       {
-               if (!IS_LOCAL(user))
-                       return MOD_RES_PASSTHRU;
-
-               // Allow forced nick changes.
-               if (ServerInstance->NICKForced.get(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;
+                       Channel* curr = (*i)->chan;
 
-                       ModResult res = ServerInstance->OnCheckExemption(user,curr,"nonick");
+                       ModResult res = CheckExemption::Call(exemptionprov, user, curr, "nonick");
 
                        if (res == MOD_RES_ALLOW)
                                continue;
 
-                       if (override && IS_OPER(user))
+                       if (user->HasPrivPermission("channels/ignore-nonicks"))
                                continue;
 
-                       if (!curr->GetExtBanStatus(user, 'N').check(!curr->IsModeSet('N')))
+                       if (!curr->GetExtBanStatus(user, 'N').check(!curr->IsModeSet(nn)))
                        {
-                               user->WriteNumeric(ERR_CANTCHANGENICK, "%s :Can't change nickname while on %s (+N is set)",
-                                       user->nick.c_str(), curr->name.c_str());
+                               user->WriteNumeric(ERR_CANTCHANGENICK, InspIRCd::Format("Cannot change nickname while on %s (+N is set)",
+                                       curr->name.c_str()));
                                return MOD_RES_DENY;
                        }
                }
 
                return MOD_RES_PASSTHRU;
        }
-
-       virtual void OnRehash(User* user)
-       {
-               override = ServerInstance->Config->ConfValue("nonicks")->getBool("operoverride", false);
-       }
 };
 
 MODULE_INIT(ModuleNoNickChange)
index c5b9f3a1c784f6dd894a4ec6b6d5237705826f73..730b0271693dffc199aa36e352816816d3f21df1 100644 (file)
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides channel mode +T to block notices to the channel */
-
-class NoNotice : public SimpleChannelModeHandler
-{
- public:
-       NoNotice(Module* Creator) : SimpleChannelModeHandler(Creator, "nonotice", 'T') { }
-};
+#include "modules/exemption.h"
 
 class ModuleNoNotice : public Module
 {
-       NoNotice nt;
+       CheckExemption::EventProvider exemptionprov;
+       SimpleChannelModeHandler nt;
  public:
 
        ModuleNoNotice()
-               : nt(this)
+               : exemptionprov(this)
+               , nt(this, "nonotice", 'T')
        {
        }
 
-       void init()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(nt);
-               Implementation eventlist[] = { I_OnUserPreNotice, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               tokens["EXTBAN"].push_back('T');
        }
 
-       virtual void On005Numeric(std::string &output)
-       {
-               ServerInstance->AddExtBanChar('T');
-       }
-
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
                ModResult res;
-               if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+               if ((details.type == MSG_NOTICE) && (target.type == MessageTarget::TYPE_CHANNEL) && (IS_LOCAL(user)))
                {
-                       Channel* c = (Channel*)dest;
-                       if (!c->GetExtBanStatus(user, 'T').check(!c->IsModeSet('T')))
+                       Channel* c = target.Get<Channel>();
+                       if (!c->GetExtBanStatus(user, 'T').check(!c->IsModeSet(nt)))
                        {
-                               res = ServerInstance->OnCheckExemption(user,c,"nonotice");
+                               res = CheckExemption::Call(exemptionprov, user, c, "nonotice");
                                if (res == MOD_RES_ALLOW)
                                        return MOD_RES_PASSTHRU;
                                else
                                {
-                                       user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s %s :Can't send NOTICE to channel (+T set)",user->nick.c_str(), c->name.c_str());
+                                       user->WriteNumeric(ERR_CANNOTSENDTOCHAN, c->name, "Can't send NOTICE to channel (+T is set)");
                                        return MOD_RES_DENY;
                                }
                        }
@@ -72,11 +60,7 @@ class ModuleNoNotice : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ~ModuleNoNotice()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides channel mode +T to block notices to the channel", VF_VENDOR);
        }
index ad3413101441a8f04a59a722bbd5853d0fb5813e..2247226954bfeb1e51b93045d450830c15b75875 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements extban +b p: - part message bans */
-
 class ModulePartMsgBan : public Module
 {
- private:
  public:
-       void init()
-       {
-               Implementation eventlist[] = { I_OnUserPart, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModulePartMsgBan()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Provides extban 'p', part message bans", VF_OPTCOMMON|VF_VENDOR);
        }
 
-       virtual Version GetVersion()
-       {
-               return Version("Implements extban +b p: - part message bans", VF_OPTCOMMON|VF_VENDOR);
-       }
-
-
-       virtual void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
+       void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(memb->user))
                        return;
 
                if (memb->chan->GetExtBanStatus(memb->user, 'p') == MOD_RES_DENY)
                        partmessage.clear();
-
-               return;
        }
 
-       virtual void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('p');
+               tokens["EXTBAN"].push_back('p');
        }
 };
 
-
 MODULE_INIT(ModulePartMsgBan)
-
index 026d98f869032be75394fdedb528e109a1b9702f..c0626ec695357189dd4e4f6e343259176fe35349 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2009 Taros <taros34@hotmail.com>
  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
  *
  * This file is part of InspIRCd.  InspIRCd is free software: you can
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
-/*
- * Written for InspIRCd-1.2 by Taros on the Tel'Laerad M&D Team
- * <http://tellaerad.net>
- */
-
 #include "inspircd.h"
 
-/* $ModConfig: <ojoin prefix="!" notice="yes" op="yes">
- *  Specify the prefix that +Y will grant here, it should be unused.
- *  Leave prefix empty if you do not wish +Y to grant a prefix
- *  If notice is set to on, upon ojoin, the server will notice
- *  the channel saying that the oper is joining on network business
- *  If op is set to on, it will give them +o along with +Y */
-/* $ModDesc: Provides the /ojoin command, which joins a user to a channel on network business, and gives them +Y, which makes them immune to kick / deop and so on. */
-/* $ModAuthor: Taros */
-/* $ModAuthorMail: taros34@hotmail.com */
-
-/* A note: This will not protect against kicks from services,
- * ulines, or operoverride. */
-
 #define NETWORK_VALUE 9000000
 
-char NPrefix;
-bool notice;
-bool op;
-
 /** Handle /OJOIN
  */
-class CommandOjoin : public Command
+class CommandOjoin : public SplitCommand
 {
  public:
        bool active;
-       CommandOjoin(Module* parent) : Command(parent,"OJOIN", 1)
-       {
-               flags_needed = 'o'; Penalty = 0; syntax = "<channel>";
+       bool notice;
+       bool op;
+       ModeHandler* npmh;
+       CommandOjoin(Module* parent, ModeHandler& mode)
+               : SplitCommand(parent, "OJOIN", 1)
+               , npmh(&mode)
+       {
+               flags_needed = 'o'; syntax = "<channel>";
                active = false;
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
        {
                // Make sure the channel name is allowable.
-               if (!ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
+               if (!ServerInstance->IsChannel(parameters[0]))
                {
-                       user->WriteServ("NOTICE "+user->nick+" :*** Invalid characters in channel name or name too long");
+                       user->WriteNotice("*** Invalid characters in channel name or name too long");
                        return CMD_FAILURE;
                }
 
                active = true;
-               Channel* channel = Channel::JoinUser(user, parameters[0].c_str(), false, "", false);
+               // override is false because we want OnUserPreJoin to run
+               Channel* channel = Channel::JoinUser(user, parameters[0], false);
                active = false;
 
                if (channel)
@@ -75,22 +58,24 @@ class CommandOjoin : public Command
 
                        if (notice)
                        {
-                               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(op ? "+Yo" : "+Y");
-                       modes.push_back(user->nick);
+                       Modes::ChangeList changelist;
+                       changelist.push_add(npmh, user->nick);
                        if (op)
-                               modes.push_back(user->nick);
-                       ServerInstance->SendGlobalMode(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;
        }
@@ -98,57 +83,16 @@ class CommandOjoin : public Command
 
 /** channel mode +Y
  */
-class NetworkPrefix : public ModeHandler
+class NetworkPrefix : public PrefixMode
 {
  public:
-       NetworkPrefix(Module* parent) : ModeHandler(parent, "official-join", 'Y', PARAM_ALWAYS, MODETYPE_CHANNEL)
-       {
-               list = true;
-               prefix = NPrefix;
-               levelrequired = INT_MAX;
-               m_paramtype = TR_NICK;
-       }
-
-       void RemoveMode(Channel* channel, irc::modestacker* stack)
-       {
-               const UserMembList* cl = channel->GetUsers();
-               std::vector<std::string> mode_junk;
-               mode_junk.push_back(channel->name);
-               irc::modestacker modestack(false);
-               std::deque<std::string> stackresult;
-
-               for (UserMembCIter i = cl->begin(); i != cl->end(); i++)
-               {
-                       if (i->second->hasMode('Y'))
-                       {
-                               if (stack)
-                                       stack->Push(this->GetModeChar(), i->first->nick);
-                               else
-                                       modestack.Push(this->GetModeChar(), i->first->nick);
-                       }
-               }
-
-               if (stack)
-                       return;
-
-               while (modestack.GetStackedLine(stackresult))
-               {
-                       mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
-                       ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
-                       mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
-               }
-       }
-
-       unsigned int GetPrefixRank()
+       NetworkPrefix(Module* parent, char NPrefix)
+               : PrefixMode(parent, "official-join", 'Y', NETWORK_VALUE, NPrefix)
        {
-               return NETWORK_VALUE;
+               ranktoset = ranktounset = UINT_MAX;
        }
 
-       void RemoveMode(User* user, irc::modestacker* stack)
-       {
-       }
-
-       ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding)
+       ModResult AccessCheck(User* source, Channel* channel, std::string &parameter, bool adding) CXX11_OVERRIDE
        {
                User* theuser = ServerInstance->FindNick(parameter);
                // remove own privs?
@@ -157,47 +101,27 @@ class NetworkPrefix : public ModeHandler
 
                return MOD_RES_PASSTHRU;
        }
-
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
-       {
-               return MODEACTION_ALLOW;
-       }
-
 };
 
 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()
-       {
-               /* Load config stuff */
-               OnRehash(NULL);
-
-               /* Initialise module variables */
-               np = new NetworkPrefix(this);
-
-               ServerInstance->Modules->AddService(*np);
-               ServerInstance->Modules->AddService(mycommand);
-
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserPreKick, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
                if (mycommand.active)
                {
-                       privs += 'Y';
-                       if (op)
+                       privs += np.GetModeChar();
+                       if (mycommand.op)
                                privs += 'o';
                        return MOD_RES_ALLOW;
                }
@@ -205,53 +129,36 @@ class ModuleOjoin : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* Conf = ServerInstance->Config->ConfValue("ojoin");
-
-               if (!np)
-               {
-                       // This is done on module load only
-                       std::string npre = Conf->getString("prefix");
-                       NPrefix = npre.empty() ? 0 : npre[0];
-
-                       if (NPrefix && ServerInstance->Modes->FindPrefix(NPrefix))
-                               throw ModuleException("Looks like the +Y prefix you picked for m_ojoin is already in use. Pick another.");
-               }
-
-               notice = Conf->getBool("notice", true);
-               op = Conf->getBool("op", true);
+               mycommand.notice = Conf->getBool("notice", true);
+               mycommand.op = Conf->getBool("op", true);
        }
 
-       ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason)
+       ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) CXX11_OVERRIDE
        {
                // Don't do anything if they're not +Y
-               if (!memb->hasMode('Y'))
+               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(484, source->nick+" "+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()
+       void Prioritize() CXX11_OVERRIDE
        {
                ServerInstance->Modules->SetPriority(this, I_OnUserPreJoin, PRIORITY_FIRST);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Network Business Join", VF_VENDOR);
+               return Version("Provides the OJOIN command, allows an oper to join a channel and be immune to kicks", VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleOjoin)
-
index ca948d95b3c195e955ffa24bfd84d7a5aad7bc74..8484d7dcc25cc658362d4d63768b5db0330a1b4e 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for oper-only chans via the +O channel mode */
+enum
+{
+       // From UnrealIRCd.
+       ERR_CANTJOINOPERSONLY = 520
+};
 
 class OperChans : public SimpleChannelModeHandler
 {
@@ -42,46 +46,34 @@ class ModuleOperChans : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(oc);
-               Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric, I_OnUserPreJoin };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
-               if (chan && chan->IsModeSet('O') && !IS_OPER(user))
+               if (chan && chan->IsModeSet(oc) && !user->IsOper())
                {
-                       user->WriteNumeric(ERR_CANTJOINOPERSONLY, "%s %s :Only IRC operators may join %s (+O is set)",
-                               user->nick.c_str(), chan->name.c_str(), chan->name.c_str());
+                       user->WriteNumeric(ERR_CANTJOINOPERSONLY, chan->name, InspIRCd::Format("Only server operators may join %s (+O is set)", chan->name.c_str()));
                        return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+       ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
        {
                if ((mask.length() > 2) && (mask[0] == 'O') && (mask[1] == ':'))
                {
-                       if (IS_OPER(user) && InspIRCd::Match(user->oper->name, mask.substr(2)))
+                       if (user->IsOper() && InspIRCd::Match(user->oper->name, mask.substr(2)))
                                return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void On005Numeric(std::string &output)
-       {
-               ServerInstance->AddExtBanChar('O');
-       }
-
-       ~ModuleOperChans()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
+               tokens["EXTBAN"].push_back('O');
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for oper-only chans via the +O channel mode and 'O' extban", VF_VENDOR);
+               return Version("Provides support for oper-only channels via channel mode +O and extban 'O'", VF_VENDOR);
        }
 };
 
index bd77384a604eb105d1d17fef1378f2a37c7fc2cc..dd80d99bafc748063d02282bd3f2f7359c32ce91 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Forces opers to join the specified channel(s) on oper-up */
-
 class ModuleOperjoin : public Module
 {
-       private:
-               std::string operChan;
                std::vector<std::string> operChans;
                bool override;
 
-               int tokenize(const std::string &str, std::vector<std::string> &tokens)
-               {
-                       // skip delimiters at beginning.
-                       std::string::size_type lastPos = str.find_first_not_of(",", 0);
-                       // find first "non-delimiter".
-                       std::string::size_type pos = str.find_first_of(",", lastPos);
-
-                       while (std::string::npos != pos || std::string::npos != lastPos)
-                       {
-                               // found a token, add it to the vector.
-                               tokens.push_back(str.substr(lastPos, pos - lastPos));
-                               // skip delimiters. Note the "not_of"
-                               lastPos = str.find_first_not_of(",", pos);
-                               // find next "non-delimiter"
-                               pos = str.find_first_of(",", lastPos);
-                       }
-                       return tokens.size();
-               }
-
        public:
-               void init()
-               {
-                       OnRehash(NULL);
-                       Implementation eventlist[] = { I_OnPostOper, I_OnRehash };
-                       ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               }
-
-
-               virtual void OnRehash(User* user)
+               void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
                {
                        ConfigTag* tag = ServerInstance->Config->ConfValue("operjoin");
 
-                       operChan = tag->getString("channel");
                        override = tag->getBool("override", false);
+                       irc::commasepstream ss(tag->getString("channel"));
                        operChans.clear();
-                       if (!operChan.empty())
-                               tokenize(operChan,operChans);
-               }
 
-               virtual ~ModuleOperjoin()
-               {
+                       for (std::string channame; ss.GetToken(channame); )
+                               operChans.push_back(channame);
                }
 
-               virtual Version GetVersion()
+               Version GetVersion() CXX11_OVERRIDE
                {
                        return Version("Forces opers to join the specified channel(s) on oper-up", VF_VENDOR);
                }
 
-               virtual void OnPostOper(User* user, const std::string &opertype, const std::string &opername)
+               void OnPostOper(User* user, const std::string &opertype, const std::string &opername) CXX11_OVERRIDE
                {
-                       if (!IS_LOCAL(user))
+                       LocalUser* localuser = IS_LOCAL(user);
+                       if (!localuser)
                                return;
 
-                       for(std::vector<std::string>::iterator it = operChans.begin(); it != operChans.end(); it++)
-                               if (ServerInstance->IsChannel(it->c_str(), ServerInstance->Config->Limits.ChanMax))
-                                       Channel::JoinUser(user, it->c_str(), override, "", false, ServerInstance->Time());
+                       for (std::vector<std::string>::const_iterator i = operChans.begin(); i != operChans.end(); ++i)
+                               if (ServerInstance->IsChannel(*i))
+                                       Channel::JoinUser(localuser, *i, override);
 
-                       std::string chanList = IS_OPER(user)->getConfig("autojoin");
-                       if (!chanList.empty())
+                       irc::commasepstream ss(localuser->oper->getConfig("autojoin"));
+                       for (std::string channame; ss.GetToken(channame); )
                        {
-                               std::vector<std::string> typechans;
-                               tokenize(chanList, typechans);
-                               for (std::vector<std::string>::const_iterator it = typechans.begin(); it != typechans.end(); ++it)
-                               {
-                                       if (ServerInstance->IsChannel(it->c_str(), ServerInstance->Config->Limits.ChanMax))
-                                       {
-                                               Channel::JoinUser(user, it->c_str(), override, "", false, ServerInstance->Time());
-                                       }
-                               }
+                               if (ServerInstance->IsChannel(channame))
+                                       Channel::JoinUser(localuser, channame, override);
                        }
                }
 };
index 569defd498af9377a9dd5949bddf0b33a1dad357..11d5d0ceead8d5aefb6900d47117002ad2c22d5e 100644 (file)
  */
 
 
-/* $ModDesc: Gives each oper type a 'level', cannot kill opers 'above' your level. */
-
 #include "inspircd.h"
 
 class ModuleOperLevels : public Module
 {
        public:
-               void init()
+               Version GetVersion() CXX11_OVERRIDE
                {
-                       ServerInstance->Modules->Attach(I_OnKill, this);
+                       return Version("Gives each oper type a 'level', cannot kill opers 'above' your level", VF_VENDOR);
                }
 
-               virtual Version GetVersion()
-               {
-                       return Version("Gives each oper type a 'level', cannot kill opers 'above' your level.", VF_VENDOR);
-               }
-
-               virtual ModResult OnKill(User* source, User* dest, const std::string &reason)
+               ModResult OnKill(User* source, User* dest, const std::string &reason) CXX11_OVERRIDE
                {
                        // oper killing an oper?
-                       if (IS_OPER(dest) && IS_OPER(source))
+                       if (dest->IsOper() && source->IsOper())
                        {
-                               std::string level = dest->oper->getConfig("level");
-                               long dest_level = atol(level.c_str());
-                               level = source->oper->getConfig("level");
-                               long source_level = atol(level.c_str());
+                               unsigned long dest_level = ConvToNum<unsigned long>(dest->oper->getConfig("level"));
+                               unsigned long source_level = ConvToNum<unsigned long>(source->oper->getConfig("level"));
 
                                if (dest_level > source_level)
                                {
-                                       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->WriteServ("NOTICE %s :*** Oper %s attempted to /kill you!",dest->nick.c_str(),source->nick.c_str());
-                                       source->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper %s is a higher level than you",source->nick.c_str(),dest->nick.c_str());
+                                       if (IS_LOCAL(source))
+                                       {
+                                               ServerInstance->SNO->WriteGlobalSno('a', "Oper %s (level %lu) attempted to /KILL a higher level oper: %s (level %lu), 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, InspIRCd::Format("Permission Denied - Oper %s is a higher level than you", dest->nick.c_str()));
                                        return MOD_RES_DENY;
                                }
                        }
@@ -60,4 +55,3 @@ class ModuleOperLevels : public Module
 };
 
 MODULE_INIT(ModuleOperLevels)
-
index edb9109e89f246d0cde5089655c1ae7279d0c7c6..c0deb81eda0640a7d5b357a6e7a70ba369c71fa1 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: A module which logs all oper commands to the ircd log at default loglevel. */
-
 class ModuleOperLog : public Module
 {
        bool tosnomask;
 
  public:
-       void init()
+       void init() CXX11_OVERRIDE
        {
-               Implementation eventlist[] = { I_OnPreCommand, I_On005Numeric, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
                ServerInstance->SNO->EnableSnomask('r', "OPERLOG");
-               OnRehash(NULL);
        }
 
-       virtual ~ModuleOperLog()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Provides logging of all oper commands to the ircd log at the default loglevel", VF_VENDOR);
        }
 
-       virtual Version GetVersion()
-       {
-               return Version("A module which logs all oper commands to the ircd log at default loglevel.", VF_VENDOR);
-       }
-
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                tosnomask = ServerInstance->Config->ConfValue("operlog")->getBool("tosnomask", false);
        }
 
-       virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
        {
                /* If the command doesnt appear to be valid, we dont want to mess with it. */
                if (!validated)
                        return MOD_RES_PASSTHRU;
 
-               if ((IS_OPER(user)) && (IS_LOCAL(user)) && (user->HasPermission(command)))
+               if ((user->IsOper()) && (user->HasCommandPermission(command)))
                {
-                       Command* thiscommand = ServerInstance->Parser->GetHandler(command);
+                       Command* thiscommand = ServerInstance->Parser.GetHandler(command);
                        if ((thiscommand) && (thiscommand->flags_needed == 'o'))
                        {
-                               std::string line;
-                               if (!parameters.empty())
-                                       line = irc::stringjoiner(" ", parameters, 0, parameters.size() - 1).GetJoined();
-                               std::string msg = "[" + user->GetFullRealHost() + "] " + command + " " + line;
-                               ServerInstance->Logs->Log("m_operlog", DEFAULT, "OPERLOG: " + msg);
+                               std::string msg = "[" + user->GetFullRealHost() + "] " + command + " " + stdalgo::string::join(parameters);
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "OPERLOG: " + msg);
                                if (tosnomask)
                                        ServerInstance->SNO->WriteGlobalSno('r', msg);
                        }
@@ -74,12 +62,11 @@ class ModuleOperLog : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               output.append(" OPERLOG");
+               tokens["OPERLOG"];
        }
 
 };
 
-
 MODULE_INIT(ModuleOperLog)
index 8b49f685e73b5eef372a5f11ce031a240a57f24a..475d561ba2028a1f2de837699b5680220111aff7 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Sets (and unsets) modes on opers when they oper up */
-
 class ModuleModesOnOper : public Module
 {
  public:
-       void init()
-       {
-               ServerInstance->Modules->Attach(I_OnPostOper, this);
-       }
-
-       virtual ~ModuleModesOnOper()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Sets (and unsets) modes on opers when they oper up", VF_VENDOR);
        }
 
-       virtual void OnPostOper(User* user, const std::string &opertype, const std::string &opername)
+       void OnPostOper(User* user, const std::string &opertype, const std::string &opername) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return;
@@ -63,15 +52,15 @@ class ModuleModesOnOper : public Module
                        smodes = "+" + smodes;
 
                std::string buf;
-               std::stringstream ss(smodes);
-               std::vector<std::string> modes;
+               irc::spacesepstream ss(smodes);
+               CommandBase::Params modes;
 
                modes.push_back(u->nick);
                // split into modes and mode params
-               while (ss >> buf)
+               while (ss.GetToken(buf))
                        modes.push_back(buf);
 
-               ServerInstance->SendMode(modes, u);
+               ServerInstance->Parser.CallHandler("MODE", modes, u);
        }
 };
 
index 989f97689d20c45f45aa41e8340520e52d5b2043..afce073a85bd04fa645dc1835732e43aaf420ac7 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Shows a message to opers after oper-up, adds /opermotd */
+enum
+{
+       // From UnrealIRCd.
+       ERR_NOOPERMOTD = 425,
+
+       // From ircd-ratbox.
+       RPL_OMOTDSTART = 720,
+       RPL_OMOTD = 721,
+       RPL_ENDOFOMOTD = 722
+};
 
 /** Handle /OPERMOTD
  */
@@ -36,37 +45,37 @@ class CommandOpermotd : public Command
                flags_needed = 'o'; syntax = "[<servername>]";
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User* user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               if ((parameters.empty()) || (parameters[0] == ServerInstance->Config->ServerName))
-                       ShowOperMOTD(user);
+               if ((parameters.empty()) || (irc::equals(parameters[0], ServerInstance->Config->ServerName)))
+                       ShowOperMOTD(user, true);
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               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)
+       void ShowOperMOTD(User* user, bool show_missing)
        {
-               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());
+                       if (show_missing)
+                               user->WriteRemoteNumeric(ERR_NOOPERMOTD, "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(RPL_OMOTDSTART, "Server 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(RPL_OMOTD, InspIRCd::Format("- %s", i->c_str()));
                }
 
-               user->SendText(":%s 376 %s :- End of OPERMOTD", servername.c_str(), user->nick.c_str());
+               user->WriteRemoteNumeric(RPL_ENDOFOMOTD, "End of OPERMOTD");
        }
 };
 
@@ -82,37 +91,33 @@ class ModuleOpermotd : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnRehash, I_OnOper };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Shows a message to opers after oper-up, adds /opermotd", VF_VENDOR | VF_OPTCOMMON);
+               return Version("Shows a message to opers after oper-up and adds the OPERMOTD command", VF_VENDOR | VF_OPTCOMMON);
        }
 
-       virtual void OnOper(User* user, const std::string &opertype)
+       void OnOper(User* user, const std::string &opertype) CXX11_OVERRIDE
        {
                if (onoper && IS_LOCAL(user))
-                       cmd.ShowOperMOTD(user);
+                       cmd.ShowOperMOTD(user, false);
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                cmd.opermotd.clear();
                ConfigTag* conf = ServerInstance->Config->ConfValue("opermotd");
                onoper = conf->getBool("onoper", true);
 
-               FileReader f(conf->getString("file", "opermotd"));
-               for (int i=0, filesize = f.FileSize(); i < filesize; i++)
-                       cmd.opermotd.push_back(f.GetLine(i));
-
-               if (conf->getBool("processcolors"))
+               try
+               {
+                       FileReader reader(conf->getString("file", "opermotd"));
+                       cmd.opermotd = reader.GetVector();
                        InspIRCd::ProcessColors(cmd.opermotd);
+               }
+               catch (CoreException&)
+               {
+                       // Nothing happens here as we do the error handling in ShowOperMOTD.
+               }
        }
 };
 
index 7d5e6d1181151f0cfae5c7b243205fed2d910248..dbc0a3b5aa1b6d9481cb9dcda87398f01f30ea21 100644 (file)
  * Originally by Chernov-Phoenix Alexey (Phoenix@RusNet) mailto:phoenix /email address separator/ pravmail.ru
  */
 
-/* $ModDesc: Gives opers cmode +y which provides a staff prefix. */
-
 #include "inspircd.h"
 
 #define OPERPREFIX_VALUE 1000000
 
-class OperPrefixMode : public ModeHandler
+class OperPrefixMode : public PrefixMode
 {
        public:
-               OperPrefixMode(Module* Creator) : ModeHandler(Creator, "operprefix", 'y', PARAM_ALWAYS, MODETYPE_CHANNEL)
-               {
-                       std::string pfx = ServerInstance->Config->ConfValue("operprefix")->getString("prefix", "!");
-                       list = true;
-                       prefix = pfx.empty() ? '!' : pfx[0];
-                       levelrequired = OPERPREFIX_VALUE;
-                       m_paramtype = TR_NICK;
-               }
-
-               unsigned int GetPrefixRank()
-               {
-                       return OPERPREFIX_VALUE;
-               }
-
-               ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
-               {
-                       if (IS_SERVER(source) || ServerInstance->ULine(source->server))
-                               return MODEACTION_ALLOW;
-                       else
-                       {
-                               if (channel)
-                                       source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only servers are permitted to change channel mode '%c'", source->nick.c_str(), channel->name.c_str(), 'y');
-                               return MODEACTION_DENY;
-                       }
-               }
-
-               bool NeedsOper() { return true; }
-
-               void RemoveMode(Channel* chan, irc::modestacker* stack)
-               {
-                       irc::modestacker modestack(false);
-                       const UserMembList* users = chan->GetUsers();
-                       for (UserMembCIter i = users->begin(); i != users->end(); ++i)
-                       {
-                               if (i->second->hasMode(mode))
-                               {
-                                       if (stack)
-                                               stack->Push(this->GetModeChar(), i->first->nick);
-                                       else
-                                               modestack.Push(this->GetModeChar(), i->first->nick);
-                               }
-                       }
-
-                       if (stack)
-                               return;
-
-                       std::deque<std::string> stackresult;
-                       std::vector<std::string> mode_junk;
-                       mode_junk.push_back(chan->name);
-                       while (modestack.GetStackedLine(stackresult))
-                       {
-                               mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
-                               ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
-                               mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
-                       }
-               }
-
-               void RemoveMode(User* user, irc::modestacker* stack)
+               OperPrefixMode(Module* Creator)
+                       : PrefixMode(Creator, "operprefix", 'y', OPERPREFIX_VALUE)
                {
+                       prefix = ServerInstance->Config->ConfValue("operprefix")->getString("prefix", "!", 1, 1)[0];
+                       ranktoset = ranktounset = UINT_MAX;
                }
 };
 
@@ -97,111 +41,70 @@ class ModuleOperPrefixMode;
 class HideOperWatcher : public ModeWatcher
 {
        ModuleOperPrefixMode* parentmod;
+
  public:
-       HideOperWatcher(ModuleOperPrefixMode* parent) : ModeWatcher((Module*) parent, 'H', MODETYPE_USER), parentmod(parent) {}
-       void AfterMode(User* source, User* dest, Channel* channel, const std::string &parameter, bool adding, ModeType type);
+       HideOperWatcher(ModuleOperPrefixMode* parent);
+       void AfterMode(User* source, User* dest, Channel* channel, const std::string &parameter, bool adding) CXX11_OVERRIDE;
 };
 
 class ModuleOperPrefixMode : public Module
 {
- private:
        OperPrefixMode opm;
-       bool mw_added;
        HideOperWatcher hideoperwatcher;
+       UserModeReference hideopermode;
+
  public:
        ModuleOperPrefixMode()
-               : opm(this), mw_added(false), hideoperwatcher(this)
-       {
-       }
-
-       void init()
+               : opm(this), hideoperwatcher(this)
+               , hideopermode(this, "hideoper")
        {
-               ServerInstance->Modules->AddService(opm);
-
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnPostOper, I_OnLoadModule, I_OnUnloadModule, I_OnPostJoin };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
                /* To give clients a chance to learn about the new prefix we don't give +y to opers
                 * right now. That means if the module was loaded after opers have joined channels
                 * they need to rejoin them in order to get the oper prefix.
                 */
-
-               if (ServerInstance->Modules->Find("m_hideoper.so"))
-                       mw_added = ServerInstance->Modes->AddModeWatcher(&hideoperwatcher);
        }
 
-       ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string& privs, const std::string& keygiven)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
-               /* The user may have the +H umode on himself, but +H does not necessarily correspond
-                * to the +H of m_hideoper.
-                * However we only add the modewatcher when m_hideoper is loaded, so these
-                * conditions (mw_added and the user being +H) together mean the user is a hidden oper.
-                */
-
-               if (IS_OPER(user) && (!mw_added || !user->IsModeSet('H')))
+               if ((user->IsOper()) && (!user->IsModeSet(hideopermode)))
                        privs.push_back('y');
                return MOD_RES_PASSTHRU;
        }
 
-       void OnPostJoin(Membership* memb)
+       void OnPostJoin(Membership* memb) CXX11_OVERRIDE
        {
-               if ((!IS_LOCAL(memb->user)) || (!IS_OPER(memb->user)) || (((mw_added) && (memb->user->IsModeSet('H')))))
+               if ((!IS_LOCAL(memb->user)) || (!memb->user->IsOper()) || (memb->user->IsModeSet(hideopermode)))
                        return;
 
-               if (memb->hasMode(opm.GetModeChar()))
+               if (memb->HasMode(&opm))
                        return;
 
                // The user was force joined and OnUserPreJoin() did not run. Set the operprefix now.
-               std::vector<std::string> modechange;
-               modechange.push_back(memb->chan->name);
-               modechange.push_back("+y");
-               modechange.push_back(memb->user->nick);
-               ServerInstance->SendGlobalMode(modechange, ServerInstance->FakeClient);
+               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 ? "+y" : "-y");
-               modechange.push_back(user->nick);
-               for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++)
-               {
-                       modechange[0] = (*v)->name;
-                       ServerInstance->SendGlobalMode(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)
+       void OnPostOper(User* user, const std::string& opername, const std::string& opertype) CXX11_OVERRIDE
        {
-               if (IS_LOCAL(user) && (!mw_added || !user->IsModeSet('H')))
+               if (IS_LOCAL(user) && (!user->IsModeSet(hideopermode)))
                        SetOperPrefix(user, true);
        }
 
-       void OnLoadModule(Module* mod)
-       {
-               if ((!mw_added) && (mod->ModuleSourceFile == "m_hideoper.so"))
-                       mw_added = ServerInstance->Modes->AddModeWatcher(&hideoperwatcher);
-       }
-
-       void OnUnloadModule(Module* mod)
+       Version GetVersion() CXX11_OVERRIDE
        {
-               if ((mw_added) && (mod->ModuleSourceFile == "m_hideoper.so") && (ServerInstance->Modes->DelModeWatcher(&hideoperwatcher)))
-                       mw_added = false;
+               return Version("Gives opers channel mode +y which provides a staff prefix", VF_VENDOR);
        }
 
-       ~ModuleOperPrefixMode()
-       {
-               if (mw_added)
-                       ServerInstance->Modes->DelModeWatcher(&hideoperwatcher);
-       }
-
-       Version GetVersion()
-       {
-               return Version("Gives opers cmode +y which provides a staff prefix.", VF_VENDOR);
-       }
-
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                // m_opermodes may set +H on the oper to hide him, we don't want to set the oper prefix in that case
                Module* opermodes = ServerInstance->Modules->Find("m_opermodes.so");
@@ -209,10 +112,16 @@ class ModuleOperPrefixMode : public Module
        }
 };
 
-void HideOperWatcher::AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding, ModeType type)
+HideOperWatcher::HideOperWatcher(ModuleOperPrefixMode* parent)
+       : ModeWatcher(parent, "hideoper", MODETYPE_USER)
+       , parentmod(parent)
+{
+}
+
+void HideOperWatcher::AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding)
 {
        // If hideoper is being unset because the user is deopering, don't set +y
-       if (IS_LOCAL(dest) && IS_OPER(dest))
+       if (IS_LOCAL(dest) && dest->IsOper())
                parentmod->SetOperPrefix(dest, !adding);
 }
 
index 3266d3eb0734278d6b372800b02d69fea7b53ae7..7155e7e76f797dfbe220a7473276e96fbd78051f 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/invite.h"
 
-/* $ModDesc: Provides support for allowing opers to override certain things. */
+class Override : public SimpleUserModeHandler
+{
+ public:
+       Override(Module* Creator) : SimpleUserModeHandler(Creator, "override", 'O')
+       {
+               oper = true;
+               if (!ServerInstance->Config->ConfValue("override")->getBool("enableumode"))
+                       DisableAutoRegister();
+       }
+};
 
 class ModuleOverride : public Module
 {
        bool RequireKey;
        bool NoisyOverride;
-
-       static bool IsOverride(unsigned int userlevel, const std::string& modeline)
+       bool UmodeEnabled;
+       Override ou;
+       ChanModeReference topiclock;
+       ChanModeReference inviteonly;
+       ChanModeReference key;
+       ChanModeReference limit;
+       Invite::API invapi;
+
+       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;
-
-                       if (mh->GetLevelRequired() > userlevel)
+                       ModeHandler* mh = i->mh;
+                       if (mh->GetLevelRequired(i->adding) > userlevel)
                                return true;
                }
                return false;
        }
 
+       ModResult HandleJoinOverride(LocalUser* user, Channel* chan, const std::string& keygiven, const char* bypasswhat, const char* mode)
+       {
+               if (RequireKey && keygiven != "override")
+               {
+                       // Can't join normally -- must use a special key to bypass restrictions
+                       user->WriteNotice("*** You may not join normally. You must join with a key of 'override' to oper override.");
+                       return MOD_RES_PASSTHRU;
+               }
+
+               if (NoisyOverride)
+                       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;
+       }
+
  public:
+       ModuleOverride()
+               : UmodeEnabled(false)
+               , ou(this)
+               , topiclock(this, "topiclock")
+               , inviteonly(this, "inviteonly")
+               , key(this, "key")
+               , limit(this, "limit")
+               , invapi(this)
+       {
+       }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
-               // read our config options (main config file)
-               OnRehash(NULL);
                ServerInstance->SNO->EnableSnomask('v', "OVERRIDE");
-               Implementation eventlist[] = { I_OnRehash, I_OnPreMode, I_On005Numeric, I_OnUserPreJoin, I_OnUserPreKick, I_OnPreTopicChange };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               UmodeEnabled = ServerInstance->Config->ConfValue("override")->getBool("enableumode");
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               // re-read our config options on a rehash
+               // re-read our config options
                ConfigTag* tag = ServerInstance->Config->ConfValue("override");
                NoisyOverride = tag->getBool("noisy");
                RequireKey = tag->getBool("requirekey");
        }
 
-       void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               output.append(" OVERRIDE");
+               tokens["OVERRIDE"];
        }
 
        bool CanOverride(User* source, const char* token)
        {
-               std::string tokenlist = source->oper->getConfig("override");
+               // If we require oper override umode (+O) but it is not set
+               if (UmodeEnabled && !source->IsModeSet(ou))
+                       return false;
 
+               std::string tokenlist = source->oper->getConfig("override");
                // its defined or * is set, return its value as a boolean for if the token is set
                return ((tokenlist.find(token, 0) != std::string::npos) || (tokenlist.find("*", 0) != std::string::npos));
        }
 
 
-       ModResult OnPreTopicChange(User *source, Channel *channel, const std::string &topic)
+       ModResult OnPreTopicChange(User *source, Channel *channel, const std::string &topic) CXX11_OVERRIDE
        {
-               if (IS_LOCAL(source) && IS_OPER(source) && CanOverride(source, "TOPIC"))
+               if (IS_LOCAL(source) && source->IsOper() && CanOverride(source, "TOPIC"))
                {
-                       if (!channel->HasUser(source) || (channel->IsModeSet('t') && channel->GetPrefixValue(source) < HALFOP_VALUE))
+                       if (!channel->HasUser(source) || (channel->IsModeSet(topiclock) && channel->GetPrefixValue(source) < HALFOP_VALUE))
                        {
                                ServerInstance->SNO->WriteGlobalSno('v',source->nick+" used oper override to change a topic on "+channel->name);
                        }
@@ -96,9 +135,9 @@ class ModuleOverride : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason)
+       ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) CXX11_OVERRIDE
        {
-               if (IS_OPER(source) && CanOverride(source,"KICK"))
+               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) ||
@@ -111,104 +150,75 @@ class ModuleOverride : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters)
+       ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
        {
-               if (!source || !channel)
+               if (!channel)
                        return MOD_RES_PASSTHRU;
-               if (!IS_OPER(source) || !IS_LOCAL(source))
+               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;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
-               if (IS_LOCAL(user) && IS_OPER(user))
+               if (user->IsOper())
                {
                        if (chan)
                        {
-                               if (chan->IsModeSet('i') && (CanOverride(user,"INVITE")))
+                               if (chan->IsModeSet(inviteonly) && (CanOverride(user,"INVITE")))
                                {
-                                       irc::string x(chan->name.c_str());
-                                       if (!IS_LOCAL(user)->IsInvited(x))
-                                       {
-                                               if (RequireKey && keygiven != "override")
-                                               {
-                                                       // Can't join normally -- must use a special key to bypass restrictions
-                                                       user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
-                                                       return MOD_RES_PASSTHRU;
-                                               }
-
-                                               if (NoisyOverride)
-                                                       chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass invite-only", cname, user->nick.c_str());
-                                               ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass +i on "+std::string(cname));
-                                       }
+                                       if (!invapi->IsInvited(user, chan))
+                                               return HandleJoinOverride(user, chan, keygiven, "invite-only", "+i");
                                        return MOD_RES_ALLOW;
                                }
 
-                               if (chan->IsModeSet('k') && (CanOverride(user,"KEY")) && keygiven != chan->GetModeParameter('k'))
-                               {
-                                       if (RequireKey && keygiven != "override")
-                                       {
-                                               // Can't join normally -- must use a special key to bypass restrictions
-                                               user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
-                                               return MOD_RES_PASSTHRU;
-                                       }
-
-                                       if (NoisyOverride)
-                                               chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass the channel key", cname, user->nick.c_str());
-                                       ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass +k on "+std::string(cname));
-                                       return MOD_RES_ALLOW;
-                               }
+                               if (chan->IsModeSet(key) && (CanOverride(user,"KEY")) && keygiven != chan->GetModeParameter(key))
+                                       return HandleJoinOverride(user, chan, keygiven, "the channel key", "+k");
 
-                               if (chan->IsModeSet('l') && (chan->GetUserCounter() >= ConvToInt(chan->GetModeParameter('l'))) && (CanOverride(user,"LIMIT")))
-                               {
-                                       if (RequireKey && keygiven != "override")
-                                       {
-                                               // Can't join normally -- must use a special key to bypass restrictions
-                                               user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
-                                               return MOD_RES_PASSTHRU;
-                                       }
-
-                                       if (NoisyOverride)
-                                               chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass the channel limit", cname, user->nick.c_str());
-                                       ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass +l on "+std::string(cname));
-                                       return MOD_RES_ALLOW;
-                               }
+                               if (chan->IsModeSet(limit) && (chan->GetUserCounter() >= ConvToNum<size_t>(chan->GetModeParameter(limit))) && (CanOverride(user,"LIMIT")))
+                                       return HandleJoinOverride(user, chan, keygiven, "the channel limit", "+l");
 
                                if (chan->IsBanned(user) && CanOverride(user,"BANWALK"))
-                               {
-                                       if (RequireKey && keygiven != "override")
-                                       {
-                                               // Can't join normally -- must use a special key to bypass restrictions
-                                               user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
-                                               return MOD_RES_PASSTHRU;
-                                       }
-
-                                       if (NoisyOverride)
-                                               chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass channel ban", cname, user->nick.c_str());
-                                       ServerInstance->SNO->WriteGlobalSno('v',"%s used oper override to bypass channel ban on %s", user->nick.c_str(), cname);
-                                       return MOD_RES_ALLOW;
-                               }
+                                       return HandleJoinOverride(user, chan, keygiven, "channel ban", "channel ban");
                        }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for allowing opers to override certain things",VF_VENDOR);
        }
index c04b306b1b8482ed0155cd801d8b85a11e932f59..2eaabe247c8393e835f9abe1c05ba2019f606073 100644 (file)
  */
 
 
-/* $ModDesc: Forwards a password users can send on connect (for example for NickServ identification). */
-
 #include "inspircd.h"
+#include "modules/account.h"
 
 class ModulePassForward : public Module
 {
- private:
        std::string nickrequired, forwardmsg, forwardcmd;
 
  public:
-       void init()
-       {
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnPostConnect, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Sends server password to NickServ", VF_VENDOR);
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("passforward");
                nickrequired = tag->getString("nick", "NickServ");
                forwardmsg = tag->getString("forwardmsg", "NOTICE $nick :*** Forwarding PASS to $nickrequired");
-               forwardcmd = tag->getString("cmd", "PRIVMSG $nickrequired :IDENTIFY $pass");
+               forwardcmd = tag->getString("cmd", "SQUERY $nickrequired :IDENTIFY $pass");
        }
 
        void FormatStr(std::string& result, const std::string& format, const LocalUser* user)
@@ -54,22 +45,22 @@ class ModulePassForward : public Module
                        char c = format[i];
                        if (c == '$')
                        {
-                               if (format.substr(i, 13) == "$nickrequired")
+                               if (!format.compare(i, 13, "$nickrequired", 13))
                                {
                                        result.append(nickrequired);
                                        i += 12;
                                }
-                               else if (format.substr(i, 5) == "$nick")
+                               else if (!format.compare(i, 5, "$nick", 5))
                                {
                                        result.append(user->nick);
                                        i += 4;
                                }
-                               else if (format.substr(i, 5) == "$user")
+                               else if (!format.compare(i, 5, "$user", 5))
                                {
                                        result.append(user->ident);
                                        i += 4;
                                }
-                               else if (format.substr(i,5) == "$pass")
+                               else if (!format.compare(i, 5, "$pass", 5))
                                {
                                        result.append(user->password);
                                        i += 4;
@@ -82,27 +73,38 @@ class ModulePassForward : public Module
                }
        }
 
-       virtual void OnPostConnect(User* ruser)
+       void OnPostConnect(User* ruser) CXX11_OVERRIDE
        {
                LocalUser* user = IS_LOCAL(ruser);
                if (!user || user->password.empty())
                        return;
 
+               // If the connect class requires a password, don't forward it
+               if (!user->MyClass->config->getString("password").empty())
+                       return;
+
+               AccountExtItem* actext = GetAccountExtItem();
+               if (actext && actext->get(user))
+               {
+                       // User is logged in already (probably via SASL) don't forward the password
+                       return;
+               }
+
                if (!nickrequired.empty())
                {
                        /* Check if nick exists and its server is ulined */
                        User* u = ServerInstance->FindNick(nickrequired);
-                       if (!u || !ServerInstance->ULine(u->server))
+                       if (!u || !u->server->IsULine())
                                return;
                }
 
                std::string tmp;
-               FormatStr(tmp,forwardmsg, user);
-               user->WriteServ(tmp);
+               FormatStr(tmp, forwardmsg, user);
+               ServerInstance->Parser.ProcessBuffer(user, tmp);
 
                tmp.clear();
                FormatStr(tmp,forwardcmd, user);
-               ServerInstance->Parser->ProcessBuffer(tmp,user);
+               ServerInstance->Parser.ProcessBuffer(user, tmp);
        }
 };
 
index 98462780b09a7fda288759b029d0a9bd327ca845..696c4fe6d4b8ecba877378a07425b5edc23f9568 100644 (file)
  */
 
 
-/* $ModDesc: Allows for hashed oper passwords */
-
 #include "inspircd.h"
-#include "hash.h"
+#include "modules/hash.h"
 
 /* Handle /MKPASSWD
  */
@@ -30,78 +28,75 @@ class CommandMkpasswd : public Command
  public:
        CommandMkpasswd(Module* Creator) : Command(Creator, "MKPASSWD", 2)
        {
-               syntax = "<hashtype> <any-text>";
+               syntax = "<hashtype> <plaintext>";
                Penalty = 5;
        }
 
-       void MakeHash(User* user, const std::string& algo, const std::string& stuff)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               if (algo.substr(0,5) == "hmac-")
+               if (!parameters[0].compare(0, 5, "hmac-", 5))
                {
-                       std::string type = algo.substr(5);
+                       std::string type(parameters[0], 5);
                        HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + type);
                        if (!hp)
                        {
-                               user->WriteServ("NOTICE %s :Unknown hash type", user->nick.c_str());
-                               return;
+                               user->WriteNotice("Unknown hash type");
+                               return CMD_FAILURE;
+                       }
+
+                       if (hp->IsKDF())
+                       {
+                               user->WriteNotice(type + " does not support HMAC");
+                               return CMD_FAILURE;
                        }
-                       std::string salt = ServerInstance->GenRandomStr(6, false);
-                       std::string target = hp->hmac(salt, stuff);
+
+                       std::string salt = ServerInstance->GenRandomStr(hp->out_size, false);
+                       std::string target = hp->hmac(salt, parameters[1]);
                        std::string str = BinToBase64(salt) + "$" + BinToBase64(target, NULL, 0);
 
-                       user->WriteServ("NOTICE %s :%s hashed password for %s is %s",
-                               user->nick.c_str(), algo.c_str(), stuff.c_str(), str.c_str());
-                       return;
-               }
-               HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + algo);
-               if (hp)
-               {
-                       /* Now attempt to generate a hash */
-                       std::string hexsum = hp->hexsum(stuff);
-                       user->WriteServ("NOTICE %s :%s hashed password for %s is %s",
-                               user->nick.c_str(), algo.c_str(), stuff.c_str(), hexsum.c_str());
+                       user->WriteNotice(parameters[0] + " hashed password for " + parameters[1] + " is " + str);
+                       return CMD_SUCCESS;
                }
-               else
+
+               HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + parameters[0]);
+               if (!hp)
                {
-                       user->WriteServ("NOTICE %s :Unknown hash type", user->nick.c_str());
+                       user->WriteNotice("Unknown hash type");
+                       return CMD_FAILURE;
                }
-       }
-
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
-       {
-               MakeHash(user, parameters[0], parameters[1]);
 
+               std::string hexsum = hp->Generate(parameters[1]);
+               user->WriteNotice(parameters[0] + " hashed password for " + parameters[1] + " is " + hexsum);
                return CMD_SUCCESS;
        }
 };
 
-class ModuleOperHash : public Module
+class ModulePasswordHash : public Module
 {
+ private:
        CommandMkpasswd cmd;
- public:
-
-       ModuleOperHash() : cmd(this)
-       {
-       }
 
-       void init()
+ public:
+       ModulePasswordHash()
+               : cmd(this)
        {
-               /* Read the config file first */
-               OnRehash(NULL);
-
-               ServerInstance->Modules->AddService(cmd);
-               Implementation eventlist[] = { I_OnPassCompare };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       virtual ModResult OnPassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype)
+       ModResult OnPassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype) CXX11_OVERRIDE
        {
-               if (hashtype.substr(0,5) == "hmac-")
+               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)
@@ -120,22 +115,21 @@ 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;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Allows for hashed oper passwords",VF_VENDOR);
+               return Version("Provides the ability to hash passwords to other modules", VF_VENDOR);
        }
 };
 
-MODULE_INIT(ModuleOperHash)
+MODULE_INIT(ModulePasswordHash)
diff --git a/src/modules/m_pbkdf2.cpp b/src/modules/m_pbkdf2.cpp
new file mode 100644 (file)
index 0000000..036538a
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * 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 = ConvToNum<unsigned int>(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();
+       }
+};
+
+struct ProviderConfig
+{
+       unsigned long dkey_length;
+       unsigned long iterations;
+};
+
+typedef std::map<std::string, ProviderConfig> ProviderConfigMap;
+
+class ModulePBKDF2 : public Module
+{
+       std::vector<PBKDF2Provider*> providers;
+       ProviderConfig globalconfig;
+       ProviderConfigMap providerconfigs;
+
+       ProviderConfig GetConfigForProvider(const std::string& name) const
+       {
+               ProviderConfigMap::const_iterator it = providerconfigs.find(name);
+               if (it == providerconfigs.end())
+                       return globalconfig;
+
+               return it->second;
+       }
+
+       void ConfigureProviders()
+       {
+               for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
+               {
+                       PBKDF2Provider* pi = *i;
+                       ProviderConfig config = GetConfigForProvider(pi->name);
+                       pi->iterations = config.iterations;
+                       pi->dkey_length = config.dkey_length;
+               }
+       }
+
+       void GetConfig()
+       {
+               // First set the common values
+               ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2");
+               ProviderConfig newglobal;
+               newglobal.iterations = tag->getUInt("iterations", 12288, 1);
+               newglobal.dkey_length = tag->getUInt("length", 32, 1, 1024);
+
+               // Then the specific values
+               ProviderConfigMap newconfigs;
+               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");
+                       ProviderConfig& config = newconfigs[hash_name];
+
+                       config.iterations = tag->getUInt("iterations", newglobal.iterations, 1);
+                       config.dkey_length = tag->getUInt("length", newglobal.dkey_length, 1, 1024);
+               }
+
+               // Config is valid, apply it
+               providerconfigs.swap(newconfigs);
+               std::swap(globalconfig, newglobal);
+               ConfigureProviders();
+       }
+
+ public:
+       ~ModulePBKDF2()
+       {
+               stdalgo::delete_all(providers);
+       }
+
+       void OnServiceAdd(ServiceProvider& provider) CXX11_OVERRIDE
+       {
+               // Check if it's a hash provider
+               if (provider.name.compare(0, 5, "hash/"))
+                       return;
+
+               HashProvider* hp = static_cast<HashProvider*>(&provider);
+               if (hp->IsKDF())
+                       return;
+
+               PBKDF2Provider* prov = new PBKDF2Provider(this, hp);
+               providers.push_back(prov);
+               ServerInstance->Modules.AddService(*prov);
+
+               ConfigureProviders();
+       }
+
+       void OnServiceDel(ServiceProvider& prov) CXX11_OVERRIDE
+       {
+               for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
+               {
+                       PBKDF2Provider* item = *i;
+                       if (item->provider != &prov)
+                               continue;
+
+                       ServerInstance->Modules->DelService(*item);
+                       delete item;
+                       providers.erase(i);
+                       break;
+               }
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               GetConfig();
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Implements PBKDF2 hashing", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModulePBKDF2)
index 74a798356c1a67b8cf05aac577932d9d0afbb11e..766158a21f9705cb51017ef70bf8c21aa3bf3d5f 100644 (file)
 
 
 #include "inspircd.h"
+#include "listmode.h"
+#include <fstream>
 
-/* $ModDesc: Provides support for channel mode +P to provide permanent channels */
 
-struct ListModeData
+/** Handles the +P channel mode
+ */
+class PermChannel : public ModeHandler
 {
-       std::string modes;
-       std::string params;
+ public:
+       PermChannel(Module* Creator)
+               : ModeHandler(Creator, "permanent", 'P', PARAM_NONE, MODETYPE_CHANNEL)
+       {
+               oper = true;
+       }
+
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
+       {
+               if (adding == channel->IsModeSet(this))
+                       return MODEACTION_DENY;
+
+               channel->SetMode(this, adding);
+               if (!adding)
+                       channel->CheckDestroy();
+
+               return MODEACTION_ALLOW;
+       }
 };
 
 // Not in a class due to circular dependancy hell.
 static std::string permchannelsconf;
-static bool WriteDatabase(Module* mod, bool save_listmodes)
+static bool WriteDatabase(PermChannel& permchanmode, Module* mod, bool save_listmodes)
 {
-       FILE *f;
+       ChanModeReference ban(mod, "ban");
+       /*
+        * We need to perform an atomic write so as not to fuck things up.
+        * So, let's write to a temporary file, flush it, then rename the file..
+        *     -- w00t
+        */
 
+       // If the user has not specified a configuration file then we don't write one.
        if (permchannelsconf.empty())
-       {
-               // Fake success.
                return true;
-       }
 
-       std::string tempname = permchannelsconf + ".tmp";
-
-       /*
-        * We need to perform an atomic write so as not to fuck things up.
-        * So, let's write to a temporary file, flush and sync the FD, then rename the file..
-        *              -- w00t
-        */
-       f = fopen(tempname.c_str(), "w");
-       if (!f)
+       std::string permchannelsnewconf = permchannelsconf + ".tmp";
+       std::ofstream stream(permchannelsnewconf.c_str());
+       if (!stream.is_open())
        {
-               ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: 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;
        }
 
-       fputs("# Permchannels DB\n# This file is autogenerated; any changes will be overwritten!\n<config format=\"compat\">\n", f);
-       // Now, let's write.
-       std::string line;
-       for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
+       stream << "# This file is automatically generated by m_permchannels. Any changes will be overwritten." << std::endl
+               << "<config format=\"xml\">" << std::endl;
+
+       const chan_hash& chans = ServerInstance->GetChans();
+       for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
        {
                Channel* chan = i->second;
-               if (!chan->IsModeSet('P'))
+               if (!chan->IsModeSet(permchanmode))
                        continue;
 
                std::string chanmodes = chan->ChanModes(true);
                if (save_listmodes)
                {
-                       ListModeData lm;
+                       std::string modes;
+                       std::string params;
 
-                       // Bans are managed by the core, so we have to process them separately
-                       lm.modes = std::string(chan->bans.size(), 'b');
-                       for (BanList::const_iterator j = chan->bans.begin(); j != chan->bans.end(); ++j)
+                       const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+                       for (ModeParser::ListModeList::const_iterator j = listmodes.begin(); j != listmodes.end(); ++j)
                        {
-                               lm.params += j->data;
-                               lm.params += ' ';
-                       }
+                               ListModeBase* lm = *j;
+                               ListModeBase::ModeList* list = lm->GetList(chan);
+                               if (!list || list->empty())
+                                       continue;
+
+                               size_t n = 0;
+                               // Append the parameters
+                               for (ListModeBase::ModeList::const_iterator k = list->begin(); k != list->end(); ++k, n++)
+                               {
+                                       params += k->mask;
+                                       params += ' ';
+                               }
 
-                       // All other listmodes are managed by modules, so we need to ask them (call their
-                       // OnSyncChannel() handler) to give our ProtoSendMode() a list of modes that are
-                       // set on the channel. The ListModeData struct is passed as an opaque pointer
-                       // that will be passed back to us by the module handling the mode.
-                       FOREACH_MOD(I_OnSyncChannel, OnSyncChannel(chan, mod, &lm));
+                               // Append the mode letters (for example "IIII", "gg")
+                               modes.append(n, lm->GetModeChar());
+                       }
 
-                       if (!lm.modes.empty())
+                       if (!params.empty())
                        {
                                // Remove the last space
-                               lm.params.erase(lm.params.end()-1);
+                               params.erase(params.end()-1);
 
                                // If there is at least a space in chanmodes (that is, a non-listmode has a parameter)
                                // insert the listmode mode letters before the space. Otherwise just append them.
                                std::string::size_type p = chanmodes.find(' ');
                                if (p == std::string::npos)
-                                       chanmodes += lm.modes;
+                                       chanmodes += modes;
                                else
-                                       chanmodes.insert(p, lm.modes);
+                                       chanmodes.insert(p, modes);
 
                                // Append the listmode parameters (the masks themselves)
                                chanmodes += ' ';
-                               chanmodes += lm.params;
+                               chanmodes += params;
                        }
                }
 
-               std::string chants = ConvToStr(chan->age);
-               std::string topicts = ConvToStr(chan->topicset);
-               const char* items[] =
-               {
-                       "<permchannels channel=",
-                       chan->name.c_str(),
-                       " ts=",
-                       chants.c_str(),
-                       " topic=",
-                       chan->topic.c_str(),
-                       " topicts=",
-                       topicts.c_str(),
-                       " topicsetby=",
-                       chan->setby.c_str(),
-                       " modes=",
-                       chanmodes.c_str(),
-                       ">\n"
-               };
-
-               line.clear();
-               int item = 0, ipos = 0;
-               while (item < 13)
-               {
-                       char c = items[item][ipos++];
-                       if (c == 0)
-                       {
-                               // end of this string; hop to next string, insert a quote
-                               item++;
-                               ipos = 0;
-                               c = '"';
-                       }
-                       else if (c == '\\' || c == '"')
-                       {
-                               line += '\\';
-                       }
-                       line += c;
-               }
-
-               // Erase last '"'
-               line.erase(line.end()-1);
-               fputs(line.c_str(), f);
+               stream << "<permchannels channel=\"" << ServerConfig::Escape(chan->name)
+                       << "\" ts=\"" << chan->age
+                       << "\" topic=\"" << ServerConfig::Escape(chan->topic)
+                       << "\" topicts=\"" << chan->topicset
+                       << "\" topicsetby=\"" << ServerConfig::Escape(chan->setby)
+                       << "\" modes=\"" << ServerConfig::Escape(chanmodes)
+                       << "\">" << std::endl;
        }
 
-       int write_error = 0;
-       write_error = ferror(f);
-       write_error |= fclose(f);
-       if (write_error)
+       if (stream.fail())
        {
-               ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: 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
        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(tempname.c_str(), permchannelsconf.c_str()) < 0)
+       if (rename(permchannelsnewconf.c_str(), permchannelsconf.c_str()) < 0)
        {
-               ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: 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;
        }
 
        return true;
 }
 
-
-
-/** Handles the +P channel mode
- */
-class PermChannel : public ModeHandler
-{
- public:
-       PermChannel(Module* Creator) : ModeHandler(Creator, "permanent", 'P', PARAM_NONE, MODETYPE_CHANNEL) { oper = true; }
-
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
-       {
-               if (adding)
-               {
-                       if (!channel->IsModeSet('P'))
-                       {
-                               channel->SetMode('P',true);
-                               return MODEACTION_ALLOW;
-                       }
-               }
-               else
-               {
-                       if (channel->IsModeSet('P'))
-                       {
-                               channel->SetMode(this,false);
-                               if (channel->GetUserCounter() == 0)
-                               {
-                                       channel->DelUser(ServerInstance->FakeClient);
-                               }
-                               return MODEACTION_ALLOW;
-                       }
-               }
-
-               return MODEACTION_DENY;
-       }
-};
-
 class ModulePermanentChannels : public Module
 {
        PermChannel p;
@@ -218,46 +170,14 @@ public:
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(p);
-               Implementation eventlist[] = { I_OnChannelPreDelete, I_OnPostTopicChange, I_OnRawMode, I_OnRehash, I_OnBackgroundTimer };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
-               OnRehash(NULL);
-       }
-
-       CullResult cull()
-       {
-               /*
-                * DelMode can't remove the +P mode on empty channels, or it will break
-                * merging modes with remote servers. Remove the empty channels now as
-                * we know this is not the case.
-                */
-               chan_hash::iterator iter = ServerInstance->chanlist->begin();
-               while (iter != ServerInstance->chanlist->end())
-               {
-                       Channel* c = iter->second;
-                       if (c->GetUserCounter() == 0)
-                       {
-                               chan_hash::iterator at = iter;
-                               iter++;
-                               FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(c));
-                               ServerInstance->chanlist->erase(at);
-                               ServerInstance->GlobalCulls.AddItem(c);
-                       }
-                       else
-                               iter++;
-               }
-               ServerInstance->Modes->DelMode(&p);
-               return Module::cull();
-       }
-
-       virtual void OnRehash(User *user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("permchanneldb");
                permchannelsconf = tag->getString("filename");
                save_listmodes = tag->getBool("listmodes");
+
+               if (!permchannelsconf.empty())
+                       permchannelsconf = ServerInstance->Config->Paths.PrependConfig(permchannelsconf);
        }
 
        void LoadDatabase()
@@ -271,12 +191,11 @@ public:
                {
                        ConfigTag* tag = i->second;
                        std::string channel = tag->getString("channel");
-                       std::string topic = tag->getString("topic");
                        std::string modes = tag->getString("modes");
 
-                       if ((channel.empty()) || (channel.length() > ServerInstance->Config->Limits.ChanMax))
+                       if (!ServerInstance->IsChannel(channel))
                        {
-                               ServerInstance->Logs->Log("m_permchannels", DEFAULT, "Ignoring permchannels tag with empty or too long channel name (\"" + channel + "\")");
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring permchannels tag with invalid channel name (\"" + channel + "\")");
                                continue;
                        }
 
@@ -284,26 +203,24 @@ public:
 
                        if (!c)
                        {
-                               time_t TS = tag->getInt("ts");
-                               c = new Channel(channel, ((TS > 0) ? TS : ServerInstance->Time()));
+                               time_t TS = tag->getInt("ts", ServerInstance->Time(), 1);
+                               c = new Channel(channel, TS);
 
-                               c->SetTopic(NULL, topic, true);
-                               c->setby = tag->getString("topicsetby");
-                               if (c->setby.empty())
-                                       c->setby = ServerInstance->Config->ServerName;
-                               unsigned int topicset = tag->getInt("topicts");
-                               // SetTopic() sets the topic TS to now, if there was no topicts saved then don't overwrite that with a 0
-                               if (topicset > 0)
-                                       c->topicset = topicset;
+                               time_t topicset = tag->getInt("topicts", 0);
+                               std::string topic = tag->getString("topic");
 
-                               ServerInstance->Logs->Log("m_permchannels", DEBUG, "Added %s with topic %s", channel.c_str(), topic.c_str());
-
-                               if (modes.find('P') == std::string::npos)
+                               if ((topicset != 0) || (!topic.empty()))
                                {
-                                       ServerInstance->Logs->Log("m_permchannels", DEFAULT, "%s (%s) does not have +P set in <permchannels:modes>; it will be deleted when empty!",
-                                               c->name.c_str(), tag->getTagLocation().c_str());
+                                       if (topicset == 0)
+                                               topicset = ServerInstance->Time();
+                                       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());
+
                                if (modes.empty())
                                        continue;
 
@@ -319,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();
@@ -327,32 +244,36 @@ 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);
                        }
                }
        }
 
-       virtual ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string &param, bool adding, int pcnt)
+       ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE
        {
-               if (chan && (chan->IsModeSet('P') || mode == 'P'))
+               if (chan && (chan->IsModeSet(p) || mh == &p))
                        dirty = true;
 
                return MOD_RES_PASSTHRU;
        }
 
-       virtual void OnPostTopicChange(User*, Channel *c, const std::string&)
+       void OnPostTopicChange(User*, Channel *c, const std::string&) CXX11_OVERRIDE
        {
-               if (c->IsModeSet('P'))
+               if (c->IsModeSet(p))
                        dirty = true;
        }
 
-       void OnBackgroundTimer(time_t)
+       void OnBackgroundTimer(time_t) CXX11_OVERRIDE
        {
                if (dirty)
-                       WriteDatabase(this, save_listmodes);
+                       WriteDatabase(p, this, save_listmodes);
                dirty = false;
        }
 
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                // XXX: Load the DB here because the order in which modules are init()ed at boot is
                // alphabetical, this means we must wait until all modules have done their init()
@@ -367,7 +288,7 @@ public:
                // Load only when there are no linked servers - we set the TS of the channels we
                // create to the current time, this can lead to desync because spanningtree has
                // no way of knowing what we do
-               ProtoServerList serverlist;
+               ProtocolInterface::ServerList serverlist;
                ServerInstance->PI->GetServerList(serverlist);
                if (serverlist.size() < 2)
                {
@@ -377,38 +298,19 @@ public:
                        }
                        catch (CoreException& e)
                        {
-                               ServerInstance->Logs->Log("m_permchannels", DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason()));
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason()));
                        }
                }
        }
 
-       void ProtoSendMode(void* opaque, TargetTypeFlags type, void* target, const std::vector<std::string>& modes, const std::vector<TranslateType>& translate)
-       {
-               // We never pass an empty modelist but better be sure
-               if (modes.empty())
-                       return;
-
-               ListModeData* lm = static_cast<ListModeData*>(opaque);
-
-               // Append the mode letters without the trailing '+' (for example "IIII", "gg")
-               lm->modes.append(modes[0].begin()+1, modes[0].end());
-
-               // Append the parameters
-               for (std::vector<std::string>::const_iterator i = modes.begin()+1; i != modes.end(); ++i)
-               {
-                       lm->params += *i;
-                       lm->params += ' ';
-               }
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for channel mode +P to provide permanent channels",VF_VENDOR);
+               return Version("Provides channel mode +P to provide permanent channels", VF_VENDOR);
        }
 
-       virtual ModResult OnChannelPreDelete(Channel *c)
+       ModResult OnChannelPreDelete(Channel *c) CXX11_OVERRIDE
        {
-               if (c->IsModeSet('P'))
+               if (c->IsModeSet(p))
                        return MOD_RES_DENY;
 
                return MOD_RES_PASSTHRU;
index 668eea0e5cc0e8a2d1ae8fa83b247b3150c23b9a..8e43552e9a88a49aa12e5a6cd2aae589f4c7ad3e 100644 (file)
  */
 
 
-/* $ModDesc: Provides random quotes on connect. */
-
 #include "inspircd.h"
 
-static FileReader *quotes = NULL;
-
-std::string prefix;
-std::string suffix;
-
-/** Handle /RANDQUOTE
- */
-class CommandRandquote : public Command
-{
- public:
-       CommandRandquote(Module* Creator) : Command(Creator,"RANDQUOTE", 0)
-       {
-       }
-
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
-       {
-               int fsize = quotes->FileSize();
-               if (fsize)
-               {
-                       std::string str = quotes->GetLine(ServerInstance->GenRandomInt(fsize));
-                       if (!str.empty())
-                               user->WriteServ("NOTICE %s :%s%s%s",user->nick.c_str(),prefix.c_str(),str.c_str(),suffix.c_str());
-               }
-
-               return CMD_SUCCESS;
-       }
-};
-
 class ModuleRandQuote : public Module
 {
  private:
-       CommandRandquote cmd;
- public:
-       ModuleRandQuote()
-               : cmd(this)
-       {
-       }
+       std::string prefix;
+       std::string suffix;
+       std::vector<std::string> quotes;
 
-       void init()
+ public:
+       void init() CXX11_OVERRIDE
        {
                ConfigTag* conf = ServerInstance->Config->ConfValue("randquote");
-
-               std::string q_file = conf->getString("file","quotes");
                prefix = conf->getString("prefix");
                suffix = conf->getString("suffix");
-
-               quotes = new FileReader(q_file);
-               if (!quotes->Exists())
-               {
-                       throw ModuleException("m_randquote: QuoteFile not Found!! Please check your config - module will not function.");
-               }
-               ServerInstance->Modules->AddService(cmd);
-               Implementation eventlist[] = { I_OnUserConnect };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               FileReader reader(conf->getString("file", "quotes"));
+               quotes = reader.GetVector();
        }
 
-
-       virtual ~ModuleRandQuote()
+       void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
        {
-               delete quotes;
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides random quotes on connect.",VF_VENDOR);
+               if (!quotes.empty())
+               {
+                       unsigned long random = ServerInstance->GenRandomInt(quotes.size());
+                       user->WriteNotice(prefix + quotes[random] + suffix);
+               }
        }
 
-       virtual void OnUserConnect(LocalUser* user)
+       Version GetVersion() CXX11_OVERRIDE
        {
-               cmd.Handle(std::vector<std::string>(), user);
+               return Version("Provides random quotes on connect", VF_VENDOR);
        }
 };
 
index 26d6b162b9ec7009daae11c3f8645eeb4df5c6b7..5e14b211e5324d95ccf39f5cf6cd8787d457f0c0 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides channel mode +L (limit redirection) and usermode +L (no forced redirection) */
-
 /** Handle channel mode +L
  */
-class Redirect : public ModeHandler
+class Redirect : public ParamMode<Redirect, LocalStringExt>
 {
  public:
-       Redirect(Module* Creator) : ModeHandler(Creator, "redirect", 'L', PARAM_SETONLY, MODETYPE_CHANNEL) { }
+       Redirect(Module* Creator)
+               : ParamMode<Redirect, LocalStringExt>(Creator, "redirect", 'L') { }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
        {
-               if (adding)
+               if (IS_LOCAL(source))
                {
-                       if (IS_LOCAL(source))
+                       if (!ServerInstance->IsChannel(parameter))
                        {
-                               if (!ServerInstance->IsChannel(parameter.c_str(), ServerInstance->Config->Limits.ChanMax))
-                               {
-                                       source->WriteNumeric(403, "%s %s :Invalid channel name", source->nick.c_str(), parameter.c_str());
-                                       parameter.clear();
-                                       return MODEACTION_DENY;
-                               }
+                               source->WriteNumeric(Numerics::NoSuchChannel(parameter));
+                               return MODEACTION_DENY;
                        }
+               }
 
-                       if (IS_LOCAL(source) && !IS_OPER(source))
+               if (IS_LOCAL(source) && !source->IsOper())
+               {
+                       Channel* c = ServerInstance->FindChan(parameter);
+                       if (!c)
                        {
-                               Channel* c = ServerInstance->FindChan(parameter);
-                               if (!c)
-                               {
-                                       source->WriteNumeric(690, "%s :Target channel %s must exist to be set as a redirect.",source->nick.c_str(),parameter.c_str());
-                                       parameter.clear();
-                                       return MODEACTION_DENY;
-                               }
-                               else if (c->GetPrefixValue(source) < OP_VALUE)
-                               {
-                                       source->WriteNumeric(690, "%s :You must be opped on %s to set it as a redirect.",source->nick.c_str(),parameter.c_str());
-                                       parameter.clear();
-                                       return MODEACTION_DENY;
-                               }
-                       }
-
-                       if (channel->GetModeParameter('L') == parameter)
+                               source->WriteNumeric(690, InspIRCd::Format("Target channel %s must exist to be set as a redirect.", parameter.c_str()));
                                return MODEACTION_DENY;
-                       /*
-                        * We used to do some checking for circular +L here, but there is no real need for this any more especially as we
-                        * now catch +L looping in PreJoin. Remove it, since O(n) logic makes me sad, and we catch it anyway. :) -- w00t
-                        */
-                       channel->SetModeParam('L', parameter);
-                       return MODEACTION_ALLOW;
-               }
-               else
-               {
-                       if (channel->IsModeSet('L'))
+                       }
+                       else if (c->GetPrefixValue(source) < OP_VALUE)
                        {
-                               channel->SetModeParam('L', "");
-                               return MODEACTION_ALLOW;
+                               source->WriteNumeric(690, InspIRCd::Format("You must be opped on %s to set it as a redirect.", parameter.c_str()));
+                               return MODEACTION_DENY;
                        }
                }
 
-               return MODEACTION_DENY;
-
+               /*
+                * We used to do some checking for circular +L here, but there is no real need for this any more especially as we
+                * now catch +L looping in PreJoin. Remove it, since O(n) logic makes me sad, and we catch it anyway. :) -- w00t
+                */
+               ext.set(channel, parameter);
+               return MODEACTION_ALLOW;
        }
-};
 
-/** Handles usermode +L to stop forced redirection and print an error.
-*/
-class AntiRedirect : public SimpleUserModeHandler
-{
-       public:
-               AntiRedirect(Module* Creator) : SimpleUserModeHandler(Creator, "antiredirect", 'L') {}
+       void SerializeParam(Channel* chan, const std::string* str, std::string& out)
+       {
+               out += *str;
+       }
 };
 
 class ModuleRedirect : public Module
 {
-
        Redirect re;
-       AntiRedirect re_u;
-       bool UseUsermode;
+       SimpleUserModeHandler antiredirectmode;
+       ChanModeReference limitmode;
 
  public:
-
        ModuleRedirect()
-               : re(this), re_u(this)
+               : re(this)
+               , antiredirectmode(this, "antiredirect", 'L')
+               , limitmode(this, "limit")
        {
        }
 
-       void init()
-       {
-               /* Setting this here so it isn't changable by rehasing the config later. */
-               UseUsermode = ServerInstance->Config->ConfValue("redirect")->getBool("antiredirect");
-
-               /* Channel mode */
-               ServerInstance->Modules->AddService(re);
-
-               /* Check to see if the usermode is enabled in the config */
-               if (UseUsermode)
-               {
-                       /* Log noting that this breaks compatability. */
-                       ServerInstance->Logs->Log("m_redirect", DEFAULT, "REDIRECT: Enabled usermode +L. This breaks linking with servers that do not have this enabled. This is disabled by default in the 2.0 branch but will be enabled in the next version.");
-
-                       /* Try to add the usermode */
-                       ServerInstance->Modules->AddService(re_u);
-               }
-
-               Implementation eventlist[] = { I_OnUserPreJoin };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
                if (chan)
                {
-                       if (chan->IsModeSet('L') && chan->IsModeSet('l'))
+                       if (chan->IsModeSet(re) && chan->IsModeSet(limitmode))
                        {
-                               if (chan->GetUserCounter() >= ConvToInt(chan->GetModeParameter('l')))
+                               if (chan->GetUserCounter() >= ConvToNum<size_t>(chan->GetModeParameter(limitmode)))
                                {
-                                       std::string channel = chan->GetModeParameter('L');
+                                       const std::string& channel = *re.ext.get(chan);
 
                                        /* sometimes broken ulines can make circular or chained +L, avoid this */
-                                       Channel* destchan = NULL;
-                                       destchan = ServerInstance->FindChan(channel);
-                                       if (destchan && destchan->IsModeSet('L'))
+                                       Channel* destchan = ServerInstance->FindChan(channel);
+                                       if (destchan && destchan->IsModeSet(re))
                                        {
-                                               user->WriteNumeric(470, "%s %s * :You may not join this channel. A redirect is set, but you may not be redirected as it is a circular loop.", user->nick.c_str(), cname);
+                                               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('L'))
+
+                                       if (user->IsModeSet(antiredirectmode))
                                        {
-                                               user->WriteNumeric(470, "%s %s %s :Force redirection stopped.",
-                                               user->nick.c_str(), cname, channel.c_str());
+                                               user->WriteNumeric(470, cname, channel, "Force redirection stopped.");
                                                return MOD_RES_DENY;
                                        }
                                        else
                                        {
-                                               user->WriteNumeric(470, "%s %s %s :You may not join this channel, so you are automatically being transferred to the redirect channel.", user->nick.c_str(), cname, channel.c_str());
-                                               Channel::JoinUser(user, channel.c_str(), false, "", false, ServerInstance->Time());
+                                               user->WriteNumeric(470, cname, channel, "You may not join this channel, so you are automatically being transferred to the redirected channel.");
+                                               Channel::JoinUser(user, channel);
                                                return MOD_RES_DENY;
                                        }
                                }
@@ -169,11 +121,7 @@ class ModuleRedirect : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ~ModuleRedirect()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides channel mode +L (limit redirection) and user mode +L (no forced redirection)", VF_VENDOR);
        }
diff --git a/src/modules/m_regex.h b/src/modules/m_regex.h
deleted file mode 100644 (file)
index 0233f93..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Thomas Stagner <aquanight@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/>.
- */
-
-
-#ifndef M_REGEX_H
-#define M_REGEX_H
-
-#include "inspircd.h"
-
-class Regex : public classbase
-{
-protected:
-       std::string regex_string; // The raw uncompiled regex string.
-
-       // Constructor may as well be protected, as this class is abstract.
-       Regex(const std::string& rx) : regex_string(rx)
-       {
-       }
-
-public:
-
-       virtual ~Regex()
-       {
-       }
-
-       virtual bool Matches(const std::string& text) = 0;
-
-       const std::string& GetRegexString() const
-       {
-               return regex_string;
-       }
-};
-
-class RegexFactory : public DataProvider
-{
- public:
-       RegexFactory(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) {}
-
-       virtual Regex* Create(const std::string& expr) = 0;
-};
-
-#endif
index 44d1a5898c7987091d72fd239b26248c1e02cf12..f4dccac20a5b3f25d57e8a05499331487a6db320 100644 (file)
  */
 
 
-#include "m_regex.h"
+#include "modules/regex.h"
 #include "inspircd.h"
 
-/* $ModDesc: Regex module using plain wildcard matching. */
-
 class GlobRegex : public Regex
 {
 public:
@@ -30,11 +28,7 @@ public:
        {
        }
 
-       virtual ~GlobRegex()
-       {
-       }
-
-       virtual bool Matches(const std::string& text)
+       bool Matches(const std::string& text) CXX11_OVERRIDE
        {
                return InspIRCd::Match(text, this->regex_string);
        }
@@ -43,7 +37,7 @@ public:
 class GlobFactory : public RegexFactory
 {
  public:
-       Regex* Create(const std::string& expr)
+       Regex* Create(const std::string& expr) CXX11_OVERRIDE
        {
                return new GlobRegex(expr);
        }
@@ -55,13 +49,14 @@ class ModuleRegexGlob : public Module
 {
        GlobFactory gf;
 public:
-       ModuleRegexGlob() : gf(this) {
-               ServerInstance->Modules->AddService(gf);
+       ModuleRegexGlob()
+               : gf(this)
+       {
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Regex module using plain wildcard matching.", VF_VENDOR);
+               return Version("Regex provider module using plain wildcard matching", VF_VENDOR);
        }
 };
 
diff --git a/src/modules/m_regonlycreate.cpp b/src/modules/m_regonlycreate.cpp
deleted file mode 100644 (file)
index 61f94c0..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2007 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 "account.h"
-
-/* $ModDesc: Prevents users whose nicks are not registered from creating new channels */
-
-class ModuleRegOnlyCreate : public Module
-{
- public:
-       void init()
-       {
-               Implementation eventlist[] = { I_OnUserPreJoin };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
-       {
-               if (chan)
-                       return MOD_RES_PASSTHRU;
-
-               if (IS_OPER(user))
-                       return MOD_RES_PASSTHRU;
-
-               if (user->IsModeSet('r'))
-                       return MOD_RES_PASSTHRU;
-
-               const AccountExtItem* ext = GetAccountExtItem();
-               if (ext && ext->get(user))
-                       return MOD_RES_PASSTHRU;
-
-               // XXX. there may be a better numeric for this..
-               user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must have a registered nickname to create a new channel", user->nick.c_str(), cname);
-               return MOD_RES_DENY;
-       }
-
-       ~ModuleRegOnlyCreate()
-       {
-       }
-
-       Version GetVersion()
-       {
-               return Version("Prevents users whose nicks are not registered from creating new channels", VF_VENDOR);
-       }
-};
-
-MODULE_INIT(ModuleRegOnlyCreate)
index cf139f4a3d5dbe9032039dd18009e5ad872e3fff..850864be2d612435838e3b50f2f21d9a8b8d0b52 100644 (file)
@@ -24,8 +24,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel */
-
 /*
  * This module supports the use of the +q and +a usermodes, but should work without them too.
  * Usage of the command is restricted to +hoaq, and you cannot remove a user with a "higher" level than yourself.
  */
 class RemoveBase : public Command
 {
- private:
        bool& supportnokicks;
+       ChanModeReference& nokicksmode;
 
  public:
-       RemoveBase(Module* Creator, bool& snk, const char* cmdn)
-               : Command(Creator, cmdn, 2, 3), supportnokicks(snk)
+       unsigned int protectedrank;
+
+       RemoveBase(Module* Creator, bool& snk, ChanModeReference& nkm, const char* cmdn)
+               : Command(Creator, cmdn, 2, 3)
+               , supportnokicks(snk)
+               , nokicksmode(nkm)
        {
        }
 
-       CmdResult HandleRMB(const std::vector<std::string>& parameters, User *user, bool neworder)
+       CmdResult HandleRMB(User* user, const CommandBase::Params& parameters,  bool fpart)
        {
                User* target;
                Channel* channel;
                std::string reason;
-               std::string protectkey;
-               std::string founderkey;
-               bool hasnokicks;
+
+               // 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.
@@ -72,42 +74,55 @@ class RemoveBase : public Command
                channel = ServerInstance->FindChan(channame);
 
                /* Fix by brain - someone needs to learn to validate their input! */
-               if ((!target) || (target->registered != REG_ALL) || (!channel))
+               if (!channel)
+               {
+                       user->WriteNumeric(Numerics::NoSuchChannel(channame));
+                       return CMD_FAILURE;
+               }
+               if ((!target) || (target->registered != REG_ALL))
                {
-                       user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), !channel ? channame.c_str() : username.c_str());
+                       user->WriteNumeric(Numerics::NoSuchNick(username));
                        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("*** 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);
-
-               hasnokicks = (ServerInstance->Modules->Find("m_nokicks.so") && channel->IsModeSet('Q'));
-
-               if (ServerInstance->ULine(target->server))
+               if (target->server->IsULine())
                {
-                       user->WriteNumeric(482, "%s %s :Only a u-line may remove a u-line from a channel.", user->nick.c_str(), channame.c_str());
+                       user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channame, "Only a U-line may remove a U-line from a channel.");
                        return CMD_FAILURE;
                }
 
                /* We support the +Q channel mode via. the m_nokicks module, if the module is loaded and the mode is set then disallow the /remove */
-               if ((!IS_LOCAL(user)) || (!supportnokicks || !hasnokicks))
+               if ((!IS_LOCAL(user)) || (!supportnokicks) || (!channel->IsModeSet(nokicksmode)))
                {
                        /* 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.
+                         a ulined target will get a higher level than it's possible for a /remover to get..so they're safe.
+                        * 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.
+                                       CommandBase::Params 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;
 
@@ -120,27 +135,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());
-                               target->WriteServ("NOTICE %s :*** %s removed you from %s with the message: %s", target->nick.c_str(), user->nick.c_str(), channel->name.c_str(), reasonparam.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->WriteServ( "484 %s %s :Can't remove user %s from channel (+Q set)", user->nick.c_str(), channel->name.c_str(), target->nick.c_str());
+                       user->WriteNumeric(ERR_RESTRICTED, channel->name, InspIRCd::Format("Can't remove user %s from channel (+Q is set)", target->nick.c_str()));
                        return CMD_FAILURE;
                }
 
                return CMD_SUCCESS;
        }
-       virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) = 0;
 };
 
 /** Handle /REMOVE
@@ -148,24 +162,16 @@ class RemoveBase : public Command
 class CommandRemove : public RemoveBase
 {
  public:
-       CommandRemove(Module* Creator, bool& snk)
-               : RemoveBase(Creator, snk, "REMOVE")
-       {
-               syntax = "<nick> <channel> [<reason>]";
-               TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END);
-       }
-
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CommandRemove(Module* Creator, bool& snk, ChanModeReference& nkm)
+               : RemoveBase(Creator, snk, nkm, "REMOVE")
        {
-               return HandleRMB(parameters, user, false);
+               syntax = "<channel> <nick> [:<reason>]";
+               TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return HandleRMB(user, parameters, false);
        }
 };
 
@@ -174,67 +180,50 @@ class CommandRemove : public RemoveBase
 class CommandFpart : public RemoveBase
 {
  public:
-       CommandFpart(Module* Creator, bool& snk)
-               : RemoveBase(Creator, snk, "FPART")
+       CommandFpart(Module* Creator, bool& snk, ChanModeReference& nkm)
+               : RemoveBase(Creator, snk, nkm, "FPART")
        {
-               syntax = "<channel> <nick> [<reason>]";
-               TRANSLATE4(TR_TEXT, TR_NICK, TR_TEXT, TR_END);
+               syntax = "<channel> <nick> [:<reason>]";
+               TRANSLATE3(TR_TEXT, TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               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;
+               return HandleRMB(user, parameters, true);
        }
 };
 
 class ModuleRemove : public Module
 {
+       ChanModeReference nokicksmode;
        CommandRemove cmd1;
        CommandFpart cmd2;
        bool supportnokicks;
 
-
  public:
-       ModuleRemove() : cmd1(this, supportnokicks), cmd2(this, supportnokicks)
+       ModuleRemove()
+               : nokicksmode(this, "nokick")
+               , cmd1(this, supportnokicks, nokicksmode)
+               , cmd2(this, supportnokicks, nokicksmode)
        {
        }
 
-       void init()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd1);
-               ServerInstance->Modules->AddService(cmd2);
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_On005Numeric, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               tokens["REMOVE"];
        }
 
-       virtual void On005Numeric(std::string &output)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               output.append(" REMOVE");
+               ConfigTag* tag = ServerInstance->Config->ConfValue("remove");
+               supportnokicks = tag->getBool("supportnokicks");
+               cmd1.protectedrank = cmd2.protectedrank = tag->getUInt("protectedrank", 50000);
        }
 
-       virtual void OnRehash(User* user)
+       Version GetVersion() CXX11_OVERRIDE
        {
-               supportnokicks = ServerInstance->Config->ConfValue("remove")->getBool("supportnokicks");
+               return Version("Provides the REMOVE command as an alternative to KICK, it makes users appear to have left the channel", VF_OPTCOMMON | VF_VENDOR);
        }
-
-       virtual ~ModuleRemove()
-       {
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel", VF_OPTCOMMON | VF_VENDOR);
-       }
-
 };
 
 MODULE_INIT(ModuleRemove)
diff --git a/src/modules/m_repeat.cpp b/src/modules/m_repeat.cpp
new file mode 100644 (file)
index 0000000..609fd9d
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * 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/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/exemption.h"
+
+class ChannelSettings
+{
+ public:
+       enum RepeatAction
+       {
+               ACT_KICK,
+               ACT_BLOCK,
+               ACT_BAN
+       };
+
+       RepeatAction Action;
+       unsigned int Backlog;
+       unsigned int Lines;
+       unsigned int Diff;
+       unsigned long Seconds;
+
+       void serialize(std::string& out) const
+       {
+               if (Action == ACT_BAN)
+                       out.push_back('*');
+               else if (Action == ACT_BLOCK)
+                       out.push_back('~');
+
+               out.append(ConvToStr(Lines)).push_back(':');
+               out.append(ConvToStr(Seconds));
+               if (Diff)
+               {
+                       out.push_back(':');
+                       out.append(ConvToStr(Diff));
+                       if (Backlog)
+                       {
+                               out.push_back(':');
+                               out.append(ConvToStr(Backlog));
+                       }
+               }
+       }
+};
+
+class RepeatMode : public ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >
+{
+ private:
+       struct RepeatItem
+       {
+               time_t ts;
+               std::string line;
+               RepeatItem(time_t TS, const std::string& Line) : ts(TS), line(Line) { }
+       };
+
+       typedef std::deque<RepeatItem> RepeatItemList;
+
+       struct MemberInfo
+       {
+               RepeatItemList ItemList;
+               unsigned int Counter;
+               MemberInfo() : Counter(0) {}
+       };
+
+       struct ModuleSettings
+       {
+               unsigned int MaxLines;
+               unsigned int MaxSecs;
+               unsigned int MaxBacklog;
+               unsigned int MaxDiff;
+               unsigned int MaxMessageSize;
+               ModuleSettings() : MaxLines(0), MaxSecs(0), MaxBacklog(0), MaxDiff() { }
+       };
+
+       std::vector<unsigned int> mx[2];
+       ModuleSettings ms;
+
+       bool CompareLines(const std::string& message, const std::string& historyline, unsigned int trigger)
+       {
+               if (message == historyline)
+                       return true;
+               else if (trigger)
+                       return (Levenshtein(message, historyline) <= trigger);
+
+               return false;
+       }
+
+       unsigned int Levenshtein(const std::string& s1, const std::string& s2)
+       {
+               unsigned int l1 = s1.size();
+               unsigned int l2 = s2.size();
+
+               for (unsigned int i = 0; i < l2; i++)
+                       mx[0][i] = i;
+               for (unsigned int i = 0; i < l1; i++)
+               {
+                       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[0].swap(mx[1]);
+               }
+               return mx[0][l2];
+       }
+
+ public:
+       SimpleExtItem<MemberInfo> MemberInfoExt;
+
+       RepeatMode(Module* Creator)
+               : ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >(Creator, "repeat", 'E')
+               , MemberInfoExt("repeat_memb", ExtensionItem::EXT_MEMBERSHIP, Creator)
+       {
+       }
+
+       void OnUnset(User* source, Channel* chan) CXX11_OVERRIDE
+       {
+               // Unset the per-membership extension when the mode is removed
+               const Channel::MemberMap& users = chan->GetUsers();
+               for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+                       MemberInfoExt.unset(i->second);
+       }
+
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
+       {
+               ChannelSettings settings;
+               if (!ParseSettings(source, parameter, settings))
+               {
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+                               "Invalid repeat syntax. Syntax is: [~|*]<lines>:<sec>[:<difference>][:<backlog>]"));
+                       return MODEACTION_DENY;
+               }
+
+               if ((settings.Backlog > 0) && (settings.Lines > settings.Backlog))
+               {
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+                               "Invalid repeat syntax. You can't set lines higher than backlog."));
+                       return MODEACTION_DENY;
+               }
+
+               LocalUser* localsource = IS_LOCAL(source);
+               if ((localsource) && (!ValidateSettings(localsource, channel, parameter, settings)))
+                       return MODEACTION_DENY;
+
+               ext.set(channel, settings);
+
+               return MODEACTION_ALLOW;
+       }
+
+       bool MatchLine(Membership* memb, ChannelSettings* rs, std::string message)
+       {
+               // If the message is larger than whatever size it's set to,
+               // let's pretend it isn't. If the first 512 (def. setting) match, it's probably spam.
+               if (message.size() > ms.MaxMessageSize)
+                       message.erase(ms.MaxMessageSize);
+
+               MemberInfo* rp = MemberInfoExt.get(memb);
+               if (!rp)
+               {
+                       rp = new MemberInfo;
+                       MemberInfoExt.set(memb, rp);
+               }
+
+               unsigned int matches = 0;
+               if (!rs->Backlog)
+                       matches = rp->Counter;
+
+               RepeatItemList& items = rp->ItemList;
+               const unsigned int trigger = (message.size() * rs->Diff / 100);
+               const time_t now = ServerInstance->Time();
+
+               std::transform(message.begin(), message.end(), message.begin(), ::tolower);
+
+               for (std::deque<RepeatItem>::iterator it = items.begin(); it != items.end(); ++it)
+               {
+                       if (it->ts < now)
+                       {
+                               items.erase(it, items.end());
+                               matches = 0;
+                               break;
+                       }
+
+                       if (CompareLines(message, it->line, trigger))
+                       {
+                               if (++matches >= rs->Lines)
+                               {
+                                       if (rs->Action != ChannelSettings::ACT_BLOCK)
+                                               rp->Counter = 0;
+                                       return true;
+                               }
+                       }
+                       else if ((ms.MaxBacklog == 0) || (rs->Backlog == 0))
+                       {
+                               matches = 0;
+                               items.clear();
+                               break;
+                       }
+               }
+
+               unsigned int max_items = (rs->Backlog ? rs->Backlog : 1);
+               if (items.size() >= max_items)
+                       items.pop_back();
+
+               items.push_front(RepeatItem(now + rs->Seconds, message));
+               rp->Counter = matches;
+               return false;
+       }
+
+       void Resize(size_t size)
+       {
+               size_t newsize = size+1;
+               if (newsize <= mx[0].size())
+                       return;
+               ms.MaxMessageSize = size;
+               mx[0].resize(newsize);
+               mx[1].resize(newsize);
+       }
+
+       void ReadConfig()
+       {
+               ConfigTag* conf = ServerInstance->Config->ConfValue("repeat");
+               ms.MaxLines = conf->getUInt("maxlines", 20);
+               ms.MaxBacklog = conf->getUInt("maxbacklog", 20);
+               ms.MaxSecs = conf->getDuration("maxtime", conf->getDuration("maxsecs", 0));
+
+               ms.MaxDiff = conf->getUInt("maxdistance", 50);
+               if (ms.MaxDiff > 100)
+                       ms.MaxDiff = 100;
+
+               unsigned int newsize = conf->getUInt("size", 512);
+               if (newsize > ServerInstance->Config->Limits.MaxLine)
+                       newsize = ServerInstance->Config->Limits.MaxLine;
+               Resize(newsize);
+       }
+
+       std::string GetModuleSettings() const
+       {
+               return ConvToStr(ms.MaxLines) + ":" + ConvToStr(ms.MaxSecs) + ":" + ConvToStr(ms.MaxDiff) + ":" + ConvToStr(ms.MaxBacklog);
+       }
+
+       void SerializeParam(Channel* chan, const ChannelSettings* chset, std::string& out)
+       {
+               chset->serialize(out);
+       }
+
+ private:
+       bool ParseSettings(User* source, std::string& parameter, ChannelSettings& settings)
+       {
+               irc::sepstream stream(parameter, ':');
+               std::string     item;
+               if (!stream.GetToken(item))
+                       // Required parameter missing
+                       return false;
+
+               if ((item[0] == '*') || (item[0] == '~'))
+               {
+                       settings.Action = ((item[0] == '*') ? ChannelSettings::ACT_BAN : ChannelSettings::ACT_BLOCK);
+                       item.erase(item.begin());
+               }
+               else
+                       settings.Action = ChannelSettings::ACT_KICK;
+
+               if ((settings.Lines = ConvToNum<unsigned int>(item)) == 0)
+                       return false;
+
+               if (!InspIRCd::Duration(item, settings.Seconds))
+                       return false;
+
+               if ((!stream.GetToken(item)) || (settings.Seconds == 0))
+                       // Required parameter missing
+                       return false;
+
+               // The diff and backlog parameters are optional
+               settings.Diff = settings.Backlog = 0;
+               if (stream.GetToken(item))
+               {
+                       // There is a diff parameter, see if it's valid (> 0)
+                       if ((settings.Diff = ConvToNum<unsigned int>(item)) == 0)
+                               return false;
+
+                       if (stream.GetToken(item))
+                       {
+                               // There is a backlog parameter, see if it's valid
+                               if ((settings.Backlog = ConvToNum<unsigned int>(item)) == 0)
+                                       return false;
+
+                               // If there are still tokens, then it's invalid because we allow only 4
+                               if (stream.GetToken(item))
+                                       return false;
+                       }
+               }
+
+               return true;
+       }
+
+       bool ValidateSettings(LocalUser* source, Channel* channel, const std::string& parameter, const ChannelSettings& settings)
+       {
+               if (ms.MaxLines && settings.Lines > ms.MaxLines)
+               {
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+                               "Invalid repeat parameter. The line number you specified is too great. Maximum allowed is %u.", ms.MaxLines)));
+                       return false;
+               }
+
+               if (ms.MaxSecs && settings.Seconds > ms.MaxSecs)
+               {
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+                               "Invalid repeat parameter. The seconds you specified are too great. Maximum allowed is %u.", ms.MaxSecs)));
+                       return false;
+               }
+
+               if (settings.Diff && settings.Diff > ms.MaxDiff)
+               {
+                       if (ms.MaxDiff == 0)
+                               source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+                                       "Invalid repeat parameter. The server administrator has disabled matching on edit distance."));
+                       else
+                               source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+                                       "Invalid repeat parameter. The distance you specified is too great. Maximum allowed is %u.", ms.MaxDiff)));
+                       return false;
+               }
+
+               if (settings.Backlog && settings.Backlog > ms.MaxBacklog)
+               {
+                       if (ms.MaxBacklog == 0)
+                               source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+                                       "Invalid repeat parameter. The server administrator has disabled backlog matching."));
+                       else
+                               source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+                                       "Invalid repeat paramter. The backlog you specified is too great. Maximum allowed is %u.", ms.MaxBacklog)));
+                       return false;
+               }
+
+               return true;
+       }
+};
+
+class RepeatModule : public Module
+{
+       CheckExemption::EventProvider exemptionprov;
+       RepeatMode rm;
+
+ public:
+       RepeatModule()
+               : exemptionprov(this)
+               , rm(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               rm.ReadConfig();
+       }
+
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
+       {
+               if (target.type != MessageTarget::TYPE_CHANNEL || !IS_LOCAL(user))
+                       return MOD_RES_PASSTHRU;
+
+               Channel* chan = target.Get<Channel>();
+               ChannelSettings* settings = rm.ext.get(chan);
+               if (!settings)
+                       return MOD_RES_PASSTHRU;
+
+               Membership* memb = chan->GetUser(user);
+               if (!memb)
+                       return MOD_RES_PASSTHRU;
+
+               ModResult res = CheckExemption::Call(exemptionprov, user, chan, "repeat");
+               if (res == MOD_RES_ALLOW)
+                       return MOD_RES_PASSTHRU;
+
+               if (rm.MatchLine(memb, settings, details.text))
+               {
+                       if (settings->Action == ChannelSettings::ACT_BLOCK)
+                       {
+                               user->WriteNotice("*** This line is too similar to one of your last lines.");
+                               return MOD_RES_DENY;
+                       }
+
+                       if (settings->Action == ChannelSettings::ACT_BAN)
+                       {
+                               Modes::ChangeList changelist;
+                               changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->GetDisplayedHost());
+                               ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist);
+                       }
+
+                       memb->chan->KickUser(ServerInstance->FakeClient, user, "Repeat flood");
+                       return MOD_RES_DENY;
+               }
+               return MOD_RES_PASSTHRU;
+       }
+
+       void Prioritize() CXX11_OVERRIDE
+       {
+               ServerInstance->Modules->SetPriority(this, I_OnUserPreMessage, PRIORITY_LAST);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides channel mode +E, blocking of similar messages", VF_COMMON|VF_VENDOR, rm.GetModuleSettings());
+       }
+};
+
+MODULE_INIT(RepeatModule)
index c76b0e79fc2317ed49bfc377dd1650c8379d502b..853b6b75c14d647e000efbf2419c1af16bf1969d 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/account.h"
 
-/* $ModDesc: Only opers may create new channels if this module is loaded */
+typedef insp::flat_set<std::string, irc::insensitive_swo> AllowChans;
 
 class ModuleRestrictChans : public Module
 {
-       std::set<irc::string> allowchans;
+       AllowChans allowchans;
+       bool allowregistered;
 
-       void ReadConfig()
+       bool CanCreateChannel(LocalUser* user, const std::string& name)
        {
-               allowchans.clear();
-               ConfigTagList tags = ServerInstance->Config->ConfTags("allowchannel");
-               for(ConfigIter i = tags.first; i != tags.second; ++i)
+               const AccountExtItem* accountext = GetAccountExtItem();
+               if (allowregistered && accountext && accountext->get(user))
+                       return true;
+
+               if (user->HasPrivPermission("channels/restricted-create"))
+                       return true;
+
+               for (AllowChans::const_iterator it = allowchans.begin(), it_end = allowchans.end(); it != it_end; ++it)
                {
-                       ConfigTag* tag = i->second;
-                       std::string txt = tag->getString("name");
-                       allowchans.insert(txt.c_str());
+                       if (InspIRCd::Match(name, *it))
+                               return true;
                }
-       }
 
- public:
-       void init()
-       {
-               ReadConfig();
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               return false;
        }
 
-       virtual void OnRehash(User* user)
+ public:
+       ModuleRestrictChans()
+               : allowregistered(false)
        {
-               ReadConfig();
        }
 
-
-       virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               irc::string x = cname;
-               if (!IS_LOCAL(user))
-                       return MOD_RES_PASSTHRU;
-
-               // channel does not yet exist (record is null, about to be created IF we were to allow it)
-               if (!chan)
+               AllowChans newallows;
+               ConfigTagList tags = ServerInstance->Config->ConfTags("allowchannel");
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
                {
-                       // user is not an oper and its not in the allow list
-                       if ((!IS_OPER(user)) && (allowchans.find(x) == allowchans.end()))
-                       {
-                               user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Only IRC operators may create new channels",user->nick.c_str(),cname);
-                               return MOD_RES_DENY;
-                       }
+                       const std::string name = i->second->getString("name");
+                       if (name.empty())
+                               throw ModuleException("Empty <allowchannel:name> at " + i->second->getTagLocation());
+
+                       newallows.insert(name);
                }
-               return MOD_RES_PASSTHRU;
+               allowchans.swap(newallows);
+
+               // Global config
+               ConfigTag* tag = ServerInstance->Config->ConfValue("restrictchans");
+               allowregistered = tag->getBool("allowregistered", false);
        }
 
-       virtual ~ModuleRestrictChans()
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
+               // channel does not yet exist (record is null, about to be created IF we were to allow it)
+               if (!chan && !CanCreateChannel(user, cname))
+                       return MOD_RES_DENY;
+
+               return MOD_RES_PASSTHRU;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Only opers may create new channels if this module is loaded",VF_VENDOR);
+               return Version("Allows restricting who can create channels", VF_VENDOR);
        }
 };
 
index 2a9f1dc9328646a228ac846c0be8d76d3e6cc9c3..75d3d905c8954211598180db6a3d3935f2a6554d 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/ctctags.h"
 
-/* $ModDesc: Forbids users from messaging each other. Users may still message opers and opers may message other opers. */
-
-
-class ModuleRestrictMsg : public Module
+class ModuleRestrictMsg
+       : public Module
+       , public CTCTags::EventListener
 {
  private:
-       bool uline;
-
- public:
-
-       void init()
-       {
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnRehash, I_OnUserPreMessage, I_OnUserPreNotice };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       void OnRehash(User*)
+       ModResult HandleMessage(User* user, const MessageTarget& target)
        {
-               uline = ServerInstance->Config->ConfValue("restrictmsg")->getBool("uline", false);
-       }
-
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               if ((target_type == TYPE_USER) && (IS_LOCAL(user)))
+               if ((target.type == MessageTarget::TYPE_USER) && (IS_LOCAL(user)))
                {
-                       User* u = (User*)dest;
+                       User* u = target.Get<User>();
 
                        // 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 (IS_OPER(u) || IS_OPER(user) || (uline && ServerInstance->ULine(u->server)))
+                       if (u->IsOper() || user->IsOper() || u->server->IsULine())
                        {
                                return MOD_RES_PASSTHRU;
                        }
-                       user->WriteNumeric(ERR_CANTSENDTOUSER, "%s %s :You are not permitted to send private messages to this user",user->nick.c_str(),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;
                }
 
@@ -66,18 +50,25 @@ class ModuleRestrictMsg : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ public:
+       ModuleRestrictMsg()
+               : CTCTags::EventListener(this)
+       {
+       }
+
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               return this->OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
+               return HandleMessage(user, target);
        }
 
-       virtual ~ModuleRestrictMsg()
+       ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
        {
+               return HandleMessage(user, target);
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Forbids users from messaging each other. Users may still message opers and opers may message other opers.",VF_VENDOR);
+               return Version("Forbids users from messaging each other, but users may still message opers and opers may message other opers", VF_VENDOR);
        }
 };
 
diff --git a/src/modules/m_ripemd160.cpp b/src/modules/m_ripemd160.cpp
deleted file mode 100644 (file)
index 04c27e8..0000000
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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/>.
- */
-
-
-/*
- *
- *      AUTHOR:   Antoon Bosselaers, ESAT-COSIC
- *      DATE:     1 March 1996
- *      VERSION:  1.0
- *
- *      Copyright (c) Katholieke Universiteit Leuven
- *      1996, All Rights Reserved
- *
- *  Conditions for use of the RIPEMD-160 Software
- *
- *  The RIPEMD-160 software is freely available for use under the terms and
- *  conditions described hereunder, which shall be deemed to be accepted by
- *  any user of the software and applicable on any use of the software:
- *
- *  1. K.U.Leuven Department of Electrical Engineering-ESAT/COSIC shall for
- *     all purposes be considered the owner of the RIPEMD-160 software and of
- *     all copyright, trade secret, patent or other intellectual property
- *     rights therein.
- *  2. The RIPEMD-160 software is provided on an "as is" basis without
- *     warranty of any sort, express or implied. K.U.Leuven makes no
- *     representation that the use of the software will not infringe any
- *     patent or proprietary right of third parties. User will indemnify
- *     K.U.Leuven and hold K.U.Leuven harmless from any claims or liabilities
- *     which may arise as a result of its use of the software. In no
- *     circumstances K.U.Leuven R&D will be held liable for any deficiency,
- *     fault or other mishappening with regard to the use or performance of
- *     the software.
- *  3. User agrees to give due credit to K.U.Leuven in scientific publications
- *     or communications in relation with the use of the RIPEMD-160 software
- *     as follows: RIPEMD-160 software written by Antoon Bosselaers,
- *     available at http://www.esat.kuleuven.be/~cosicart/ps/AB-9601/.
- *
- */
-
-
-/* $ModDesc: Allows for RIPEMD-160 encrypted oper passwords */
-
-/* macro definitions */
-
-#include "inspircd.h"
-#ifdef HAS_STDINT
-#include <stdint.h>
-#endif
-#include "hash.h"
-
-#define RMDsize 160
-
-#ifndef HAS_STDINT
-typedef                unsigned char           byte;
-typedef                unsigned int            dword;
-#else
-typedef                uint8_t                 byte;
-typedef                uint32_t                dword;
-#endif
-
-/* collect four bytes into one word: */
-#define BYTES_TO_DWORD(strptr)                    \
-            (((dword) *((strptr)+3) << 24) | \
-             ((dword) *((strptr)+2) << 16) | \
-             ((dword) *((strptr)+1) <<  8) | \
-             ((dword) *(strptr)))
-
-/* ROL(x, n) cyclically rotates x over n bits to the left */
-/* x must be of an unsigned 32 bits type and 0 <= n < 32. */
-#define ROL(x, n)        (((x) << (n)) | ((x) >> (32-(n))))
-
-/* the five basic functions F(), G() and H() */
-#define F(x, y, z)        ((x) ^ (y) ^ (z))
-#define G(x, y, z)        (((x) & (y)) | (~(x) & (z)))
-#define H(x, y, z)        (((x) | ~(y)) ^ (z))
-#define I(x, y, z)        (((x) & (z)) | ((y) & ~(z)))
-#define J(x, y, z)        ((x) ^ ((y) | ~(z)))
-
-/* the ten basic operations FF() through III() */
-
-#define FF(a, b, c, d, e, x, s)        {\
-      (a) += F((b), (c), (d)) + (x);\
-      (a) = ROL((a), (s)) + (e);\
-      (c) = ROL((c), 10);\
-   }
-
-#define GG(a, b, c, d, e, x, s)        {\
-      (a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\
-      (a) = ROL((a), (s)) + (e);\
-      (c) = ROL((c), 10);\
-   }
-
-#define HH(a, b, c, d, e, x, s)        {\
-      (a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\
-      (a) = ROL((a), (s)) + (e);\
-      (c) = ROL((c), 10);\
-   }
-
-#define II(a, b, c, d, e, x, s)        {\
-      (a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\
-      (a) = ROL((a), (s)) + (e);\
-      (c) = ROL((c), 10);\
-   }
-
-#define JJ(a, b, c, d, e, x, s)        {\
-      (a) += J((b), (c), (d)) + (x) + 0xa953fd4eUL;\
-      (a) = ROL((a), (s)) + (e);\
-      (c) = ROL((c), 10);\
-   }
-
-#define FFF(a, b, c, d, e, x, s)        {\
-      (a) += F((b), (c), (d)) + (x);\
-      (a) = ROL((a), (s)) + (e);\
-      (c) = ROL((c), 10);\
-   }
-
-#define GGG(a, b, c, d, e, x, s)        {\
-      (a) += G((b), (c), (d)) + (x) + 0x7a6d76e9UL;\
-      (a) = ROL((a), (s)) + (e);\
-      (c) = ROL((c), 10);\
-   }
-
-#define HHH(a, b, c, d, e, x, s)        {\
-      (a) += H((b), (c), (d)) + (x) + 0x6d703ef3UL;\
-      (a) = ROL((a), (s)) + (e);\
-      (c) = ROL((c), 10);\
-   }
-
-#define III(a, b, c, d, e, x, s)        {\
-      (a) += I((b), (c), (d)) + (x) + 0x5c4dd124UL;\
-      (a) = ROL((a), (s)) + (e);\
-      (c) = ROL((c), 10);\
-   }
-
-#define JJJ(a, b, c, d, e, x, s)        {\
-      (a) += J((b), (c), (d)) + (x) + 0x50a28be6UL;\
-      (a) = ROL((a), (s)) + (e);\
-      (c) = ROL((c), 10);\
-   }
-
-
-class RIProv : public HashProvider
-{
-       /** Final hash value
-        */
-       byte hashcode[RMDsize/8];
-
-       void MDinit(dword *MDbuf, unsigned int* key)
-       {
-               if (key)
-               {
-                       ServerInstance->Logs->Log("m_ripemd160.so", DEBUG, "initialize with custom mdbuf");
-                       MDbuf[0] = key[0];
-                       MDbuf[1] = key[1];
-                       MDbuf[2] = key[2];
-                       MDbuf[3] = key[3];
-                       MDbuf[4] = key[4];
-               }
-               else
-               {
-                       ServerInstance->Logs->Log("m_ripemd160.so", DEBUG, "initialize with default mdbuf");
-                       MDbuf[0] = 0x67452301UL;
-                       MDbuf[1] = 0xefcdab89UL;
-                       MDbuf[2] = 0x98badcfeUL;
-                       MDbuf[3] = 0x10325476UL;
-                       MDbuf[4] = 0xc3d2e1f0UL;
-               }
-               return;
-       }
-
-
-       void compress(dword *MDbuf, dword *X)
-       {
-               dword aa = MDbuf[0],  bb = MDbuf[1],  cc = MDbuf[2],
-                       dd = MDbuf[3],  ee = MDbuf[4];
-               dword aaa = MDbuf[0], bbb = MDbuf[1], ccc = MDbuf[2],
-                       ddd = MDbuf[3], eee = MDbuf[4];
-
-               /* round 1 */
-               FF(aa, bb, cc, dd, ee, X[ 0], 11);
-               FF(ee, aa, bb, cc, dd, X[ 1], 14);
-               FF(dd, ee, aa, bb, cc, X[ 2], 15);
-               FF(cc, dd, ee, aa, bb, X[ 3], 12);
-               FF(bb, cc, dd, ee, aa, X[ 4],  5);
-               FF(aa, bb, cc, dd, ee, X[ 5],  8);
-               FF(ee, aa, bb, cc, dd, X[ 6],  7);
-               FF(dd, ee, aa, bb, cc, X[ 7],  9);
-               FF(cc, dd, ee, aa, bb, X[ 8], 11);
-               FF(bb, cc, dd, ee, aa, X[ 9], 13);
-               FF(aa, bb, cc, dd, ee, X[10], 14);
-               FF(ee, aa, bb, cc, dd, X[11], 15);
-               FF(dd, ee, aa, bb, cc, X[12],  6);
-               FF(cc, dd, ee, aa, bb, X[13],  7);
-               FF(bb, cc, dd, ee, aa, X[14],  9);
-               FF(aa, bb, cc, dd, ee, X[15],  8);
-
-               /* round 2 */
-               GG(ee, aa, bb, cc, dd, X[ 7],  7);
-               GG(dd, ee, aa, bb, cc, X[ 4],  6);
-               GG(cc, dd, ee, aa, bb, X[13],  8);
-               GG(bb, cc, dd, ee, aa, X[ 1], 13);
-               GG(aa, bb, cc, dd, ee, X[10], 11);
-               GG(ee, aa, bb, cc, dd, X[ 6],  9);
-               GG(dd, ee, aa, bb, cc, X[15],  7);
-               GG(cc, dd, ee, aa, bb, X[ 3], 15);
-               GG(bb, cc, dd, ee, aa, X[12],  7);
-               GG(aa, bb, cc, dd, ee, X[ 0], 12);
-               GG(ee, aa, bb, cc, dd, X[ 9], 15);
-               GG(dd, ee, aa, bb, cc, X[ 5],  9);
-               GG(cc, dd, ee, aa, bb, X[ 2], 11);
-               GG(bb, cc, dd, ee, aa, X[14],  7);
-               GG(aa, bb, cc, dd, ee, X[11], 13);
-               GG(ee, aa, bb, cc, dd, X[ 8], 12);
-
-               /* round 3 */
-               HH(dd, ee, aa, bb, cc, X[ 3], 11);
-               HH(cc, dd, ee, aa, bb, X[10], 13);
-               HH(bb, cc, dd, ee, aa, X[14],  6);
-               HH(aa, bb, cc, dd, ee, X[ 4],  7);
-               HH(ee, aa, bb, cc, dd, X[ 9], 14);
-               HH(dd, ee, aa, bb, cc, X[15],  9);
-               HH(cc, dd, ee, aa, bb, X[ 8], 13);
-               HH(bb, cc, dd, ee, aa, X[ 1], 15);
-               HH(aa, bb, cc, dd, ee, X[ 2], 14);
-               HH(ee, aa, bb, cc, dd, X[ 7],  8);
-               HH(dd, ee, aa, bb, cc, X[ 0], 13);
-               HH(cc, dd, ee, aa, bb, X[ 6],  6);
-               HH(bb, cc, dd, ee, aa, X[13],  5);
-               HH(aa, bb, cc, dd, ee, X[11], 12);
-               HH(ee, aa, bb, cc, dd, X[ 5],  7);
-               HH(dd, ee, aa, bb, cc, X[12],  5);
-
-               /* round 4 */
-               II(cc, dd, ee, aa, bb, X[ 1], 11);
-               II(bb, cc, dd, ee, aa, X[ 9], 12);
-               II(aa, bb, cc, dd, ee, X[11], 14);
-               II(ee, aa, bb, cc, dd, X[10], 15);
-               II(dd, ee, aa, bb, cc, X[ 0], 14);
-               II(cc, dd, ee, aa, bb, X[ 8], 15);
-               II(bb, cc, dd, ee, aa, X[12],  9);
-               II(aa, bb, cc, dd, ee, X[ 4],  8);
-               II(ee, aa, bb, cc, dd, X[13],  9);
-               II(dd, ee, aa, bb, cc, X[ 3], 14);
-               II(cc, dd, ee, aa, bb, X[ 7],  5);
-               II(bb, cc, dd, ee, aa, X[15],  6);
-               II(aa, bb, cc, dd, ee, X[14],  8);
-               II(ee, aa, bb, cc, dd, X[ 5],  6);
-               II(dd, ee, aa, bb, cc, X[ 6],  5);
-               II(cc, dd, ee, aa, bb, X[ 2], 12);
-
-               /* round 5 */
-               JJ(bb, cc, dd, ee, aa, X[ 4],  9);
-               JJ(aa, bb, cc, dd, ee, X[ 0], 15);
-               JJ(ee, aa, bb, cc, dd, X[ 5],  5);
-               JJ(dd, ee, aa, bb, cc, X[ 9], 11);
-               JJ(cc, dd, ee, aa, bb, X[ 7],  6);
-               JJ(bb, cc, dd, ee, aa, X[12],  8);
-               JJ(aa, bb, cc, dd, ee, X[ 2], 13);
-               JJ(ee, aa, bb, cc, dd, X[10], 12);
-               JJ(dd, ee, aa, bb, cc, X[14],  5);
-               JJ(cc, dd, ee, aa, bb, X[ 1], 12);
-               JJ(bb, cc, dd, ee, aa, X[ 3], 13);
-               JJ(aa, bb, cc, dd, ee, X[ 8], 14);
-               JJ(ee, aa, bb, cc, dd, X[11], 11);
-               JJ(dd, ee, aa, bb, cc, X[ 6],  8);
-               JJ(cc, dd, ee, aa, bb, X[15],  5);
-               JJ(bb, cc, dd, ee, aa, X[13],  6);
-
-               /* parallel round 1 */
-               JJJ(aaa, bbb, ccc, ddd, eee, X[ 5],  8);
-               JJJ(eee, aaa, bbb, ccc, ddd, X[14],  9);
-               JJJ(ddd, eee, aaa, bbb, ccc, X[ 7],  9);
-               JJJ(ccc, ddd, eee, aaa, bbb, X[ 0], 11);
-               JJJ(bbb, ccc, ddd, eee, aaa, X[ 9], 13);
-               JJJ(aaa, bbb, ccc, ddd, eee, X[ 2], 15);
-               JJJ(eee, aaa, bbb, ccc, ddd, X[11], 15);
-               JJJ(ddd, eee, aaa, bbb, ccc, X[ 4],  5);
-               JJJ(ccc, ddd, eee, aaa, bbb, X[13],  7);
-               JJJ(bbb, ccc, ddd, eee, aaa, X[ 6],  7);
-               JJJ(aaa, bbb, ccc, ddd, eee, X[15],  8);
-               JJJ(eee, aaa, bbb, ccc, ddd, X[ 8], 11);
-               JJJ(ddd, eee, aaa, bbb, ccc, X[ 1], 14);
-               JJJ(ccc, ddd, eee, aaa, bbb, X[10], 14);
-               JJJ(bbb, ccc, ddd, eee, aaa, X[ 3], 12);
-               JJJ(aaa, bbb, ccc, ddd, eee, X[12],  6);
-
-               /* parallel round 2 */
-               III(eee, aaa, bbb, ccc, ddd, X[ 6],  9);
-               III(ddd, eee, aaa, bbb, ccc, X[11], 13);
-               III(ccc, ddd, eee, aaa, bbb, X[ 3], 15);
-               III(bbb, ccc, ddd, eee, aaa, X[ 7],  7);
-               III(aaa, bbb, ccc, ddd, eee, X[ 0], 12);
-               III(eee, aaa, bbb, ccc, ddd, X[13],  8);
-               III(ddd, eee, aaa, bbb, ccc, X[ 5],  9);
-               III(ccc, ddd, eee, aaa, bbb, X[10], 11);
-               III(bbb, ccc, ddd, eee, aaa, X[14],  7);
-               III(aaa, bbb, ccc, ddd, eee, X[15],  7);
-               III(eee, aaa, bbb, ccc, ddd, X[ 8], 12);
-               III(ddd, eee, aaa, bbb, ccc, X[12],  7);
-               III(ccc, ddd, eee, aaa, bbb, X[ 4],  6);
-               III(bbb, ccc, ddd, eee, aaa, X[ 9], 15);
-               III(aaa, bbb, ccc, ddd, eee, X[ 1], 13);
-               III(eee, aaa, bbb, ccc, ddd, X[ 2], 11);
-
-               /* parallel round 3 */
-               HHH(ddd, eee, aaa, bbb, ccc, X[15],  9);
-               HHH(ccc, ddd, eee, aaa, bbb, X[ 5],  7);
-               HHH(bbb, ccc, ddd, eee, aaa, X[ 1], 15);
-               HHH(aaa, bbb, ccc, ddd, eee, X[ 3], 11);
-               HHH(eee, aaa, bbb, ccc, ddd, X[ 7],  8);
-               HHH(ddd, eee, aaa, bbb, ccc, X[14],  6);
-               HHH(ccc, ddd, eee, aaa, bbb, X[ 6],  6);
-               HHH(bbb, ccc, ddd, eee, aaa, X[ 9], 14);
-               HHH(aaa, bbb, ccc, ddd, eee, X[11], 12);
-               HHH(eee, aaa, bbb, ccc, ddd, X[ 8], 13);
-               HHH(ddd, eee, aaa, bbb, ccc, X[12],  5);
-               HHH(ccc, ddd, eee, aaa, bbb, X[ 2], 14);
-               HHH(bbb, ccc, ddd, eee, aaa, X[10], 13);
-               HHH(aaa, bbb, ccc, ddd, eee, X[ 0], 13);
-               HHH(eee, aaa, bbb, ccc, ddd, X[ 4],  7);
-               HHH(ddd, eee, aaa, bbb, ccc, X[13],  5);
-
-               /* parallel round 4 */
-               GGG(ccc, ddd, eee, aaa, bbb, X[ 8], 15);
-               GGG(bbb, ccc, ddd, eee, aaa, X[ 6],  5);
-               GGG(aaa, bbb, ccc, ddd, eee, X[ 4],  8);
-               GGG(eee, aaa, bbb, ccc, ddd, X[ 1], 11);
-               GGG(ddd, eee, aaa, bbb, ccc, X[ 3], 14);
-               GGG(ccc, ddd, eee, aaa, bbb, X[11], 14);
-               GGG(bbb, ccc, ddd, eee, aaa, X[15],  6);
-               GGG(aaa, bbb, ccc, ddd, eee, X[ 0], 14);
-               GGG(eee, aaa, bbb, ccc, ddd, X[ 5],  6);
-               GGG(ddd, eee, aaa, bbb, ccc, X[12],  9);
-               GGG(ccc, ddd, eee, aaa, bbb, X[ 2], 12);
-               GGG(bbb, ccc, ddd, eee, aaa, X[13],  9);
-               GGG(aaa, bbb, ccc, ddd, eee, X[ 9], 12);
-               GGG(eee, aaa, bbb, ccc, ddd, X[ 7],  5);
-               GGG(ddd, eee, aaa, bbb, ccc, X[10], 15);
-               GGG(ccc, ddd, eee, aaa, bbb, X[14],  8);
-
-               /* parallel round 5 */
-               FFF(bbb, ccc, ddd, eee, aaa, X[12] ,  8);
-               FFF(aaa, bbb, ccc, ddd, eee, X[15] ,  5);
-               FFF(eee, aaa, bbb, ccc, ddd, X[10] , 12);
-               FFF(ddd, eee, aaa, bbb, ccc, X[ 4] ,  9);
-               FFF(ccc, ddd, eee, aaa, bbb, X[ 1] , 12);
-               FFF(bbb, ccc, ddd, eee, aaa, X[ 5] ,  5);
-               FFF(aaa, bbb, ccc, ddd, eee, X[ 8] , 14);
-               FFF(eee, aaa, bbb, ccc, ddd, X[ 7] ,  6);
-               FFF(ddd, eee, aaa, bbb, ccc, X[ 6] ,  8);
-               FFF(ccc, ddd, eee, aaa, bbb, X[ 2] , 13);
-               FFF(bbb, ccc, ddd, eee, aaa, X[13] ,  6);
-               FFF(aaa, bbb, ccc, ddd, eee, X[14] ,  5);
-               FFF(eee, aaa, bbb, ccc, ddd, X[ 0] , 15);
-               FFF(ddd, eee, aaa, bbb, ccc, X[ 3] , 13);
-               FFF(ccc, ddd, eee, aaa, bbb, X[ 9] , 11);
-               FFF(bbb, ccc, ddd, eee, aaa, X[11] , 11);
-
-               /* combine results */
-               ddd += cc + MDbuf[1];               /* final result for MDbuf[0] */
-               MDbuf[1] = MDbuf[2] + dd + eee;
-               MDbuf[2] = MDbuf[3] + ee + aaa;
-               MDbuf[3] = MDbuf[4] + aa + bbb;
-               MDbuf[4] = MDbuf[0] + bb + ccc;
-               MDbuf[0] = ddd;
-
-               return;
-       }
-
-       void MDfinish(dword *MDbuf, byte *strptr, dword lswlen, dword mswlen)
-       {
-               unsigned int i;                                 /* counter       */
-               dword        X[16];                             /* message words */
-
-               memset(X, 0, sizeof(X));
-
-               /* put bytes from strptr into X */
-               for (i=0; i<(lswlen&63); i++) {
-                       /* byte i goes into word X[i div 4] at pos.  8*(i mod 4)  */
-                       X[i>>2] ^= (dword) *strptr++ << (8 * (i&3));
-               }
-
-               /* append the bit m_n == 1 */
-               X[(lswlen>>2)&15] ^= (dword)1 << (8*(lswlen&3) + 7);
-
-               if ((lswlen & 63) > 55) {
-                       /* length goes to next block */
-                       compress(MDbuf, X);
-                       memset(X, 0, sizeof(X));
-               }
-
-               /* append length in bits*/
-               X[14] = lswlen << 3;
-               X[15] = (lswlen >> 29) | (mswlen << 3);
-               compress(MDbuf, X);
-
-               return;
-       }
-
-       byte *RMD(byte *message, dword length, unsigned int* key)
-       {
-               ServerInstance->Logs->Log("m_ripemd160", DEBUG, "RMD: '%s' length=%u", (const char*)message, length);
-               dword         MDbuf[RMDsize/32];   /* contains (A, B, C, D(E))   */
-               dword         X[16];               /* current 16-word chunk        */
-               unsigned int  i;                   /* counter                      */
-               dword         nbytes;              /* # of bytes not yet processed */
-
-               /* initialize */
-               MDinit(MDbuf, key);
-
-               /* process message in 16-word chunks */
-               for (nbytes=length; nbytes > 63; nbytes-=64) {
-                       for (i=0; i<16; i++) {
-                               X[i] = BYTES_TO_DWORD(message);
-                               message += 4;
-                       }
-                       compress(MDbuf, X);
-               }                                    /* length mod 64 bytes left */
-
-               MDfinish(MDbuf, message, length, 0);
-
-               for (i=0; i<RMDsize/8; i+=4) {
-                       hashcode[i]   =  MDbuf[i>>2];         /* implicit cast to byte  */
-                       hashcode[i+1] = (MDbuf[i>>2] >>  8);  /*  extracts the 8 least  */
-                       hashcode[i+2] = (MDbuf[i>>2] >> 16);  /*  significant bits.     */
-                       hashcode[i+3] = (MDbuf[i>>2] >> 24);
-               }
-
-               return (byte *)hashcode;
-       }
-public:
-       std::string sum(const std::string& data)
-       {
-               char* rv = (char*)RMD((byte*)data.data(), data.length(), NULL);
-               return std::string(rv, RMDsize / 8);
-       }
-
-       std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
-       {
-               return "";
-       }
-
-       RIProv(Module* m) : HashProvider(m, "hash/ripemd160", 20, 64) {}
-};
-
-class ModuleRIPEMD160 : public Module
-{
- public:
-       RIProv mr;
-       ModuleRIPEMD160() : mr(this)
-       {
-               ServerInstance->Modules->AddService(mr);
-       }
-
-       Version GetVersion()
-       {
-               return Version("Provides RIPEMD-160 hashing", VF_VENDOR);
-       }
-
-};
-
-MODULE_INIT(ModuleRIPEMD160)
-
index d1ab5d9ba42cc35645ca77b54170a315f4319046..f9abe2158fffd769f99227edd37e4d68b061c59e 100644 (file)
  */
 
 
-/* $ModDesc: RLINE: Regexp user banning. */
-
 #include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
+#include "modules/stats.h"
 #include "xline.h"
 
 static bool ZlineOnMatch = false;
@@ -33,7 +32,7 @@ class RLine : public XLine
 {
  public:
 
-       /** Create a R-Line.
+       /** Create a R-line.
         * @param s_time The set time
         * @param d The duration of the xline
         * @param src The sender of the xline
@@ -41,7 +40,7 @@ class RLine : public XLine
         * @param regex Pattern to match with
         * @
         */
-       RLine(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& regexs, dynamic_reference<RegexFactory>& rxfactory)
+       RLine(time_t s_time, unsigned long d, const std::string& src, const std::string& re, const std::string& regexs, dynamic_reference<RegexFactory>& rxfactory)
                : XLine(s_time, d, src, re, "R")
                , matchtext(regexs)
        {
@@ -58,30 +57,32 @@ class RLine : public XLine
                delete regex;
        }
 
-       bool Matches(User *u)
+       bool Matches(User* u) CXX11_OVERRIDE
        {
-               if (u->exempt)
+               LocalUser* lu = IS_LOCAL(u);
+               if (lu && lu->exempt)
                        return false;
 
-               std::string compare = u->nick + "!" + u->ident + "@" + u->host + " " + u->fullname;
-               return regex->Matches(compare);
+               const std::string host = u->nick + "!" + u->ident + "@" + u->GetRealHost() + " " + u->GetRealName();
+               const std::string ip = u->nick + "!" + u->ident + "@" + u->GetIPString() + " " + u->GetRealName();
+               return (regex->Matches(host) || regex->Matches(ip));
        }
 
-       bool Matches(const std::string &compare)
+       bool Matches(const std::string& compare) CXX11_OVERRIDE
        {
                return regex->Matches(compare);
        }
 
-       void Apply(User* u)
+       void Apply(User* u) CXX11_OVERRIDE
        {
                if (ZlineOnMatch)
                {
                        ZLine* zl = new ZLine(ServerInstance->Time(), duration ? expiry - ServerInstance->Time() : 0, ServerInstance->Config->ServerName.c_str(), reason.c_str(), u->GetIPString());
                        if (ServerInstance->XLines->AddLine(zl, NULL))
                        {
-                               std::string timestr = ServerInstance->TimeString(zl->expiry);
-                               ServerInstance->SNO->WriteToSnoMask('x', "Z-line added due to R-line match on *@%s%s%s: %s",
-                                       zl->ipaddr.c_str(), zl->duration ? " to expire on " : "", zl->duration ? timestr.c_str() : "", zl->reason.c_str());
+                               std::string expirystr = zl->duration ? InspIRCd::Format(" to expire in %s (on %s)", InspIRCd::DurationString(zl->duration).c_str(), InspIRCd::TimeString(zl->expiry).c_str()) : "";
+                               ServerInstance->SNO->WriteToSnoMask('x', "Z-line added due to R-line match on %s%s: %s",
+                                       zl->ipaddr.c_str(), expirystr.c_str(), zl->reason.c_str());
                                added_zline = true;
                        }
                        else
@@ -90,15 +91,9 @@ class RLine : public XLine
                DefaultApply(u, "R", false);
        }
 
-       void DisplayExpiry()
+       const std::string& Displayable() CXX11_OVERRIDE
        {
-               ServerInstance->SNO->WriteToSnoMask('x',"Removing expired R-line %s (set by %s %ld seconds ago)",
-                       this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
-       }
-
-       const char* Displayable()
-       {
-               return matchtext.c_str();
+               return matchtext;
        }
 
        std::string matchtext;
@@ -116,10 +111,10 @@ class RLineFactory : public XLineFactory
        RLineFactory(dynamic_reference<RegexFactory>& rx) : XLineFactory("R"), rxfactory(rx)
        {
        }
-       
+
        /** Generate a RLine
         */
-       XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+       XLine* Generate(time_t set_time, unsigned long duration, const std::string& source, const std::string& reason, const std::string& xline_specific_mask) CXX11_OVERRIDE
        {
                if (!rxfactory)
                {
@@ -129,10 +124,6 @@ class RLineFactory : public XLineFactory
 
                return new RLine(set_time, duration, source, reason, xline_specific_mask, rxfactory);
        }
-
-       ~RLineFactory()
-       {
-       }
 };
 
 /** Handle /RLINE
@@ -146,17 +137,22 @@ class CommandRLine : public Command
  public:
        CommandRLine(Module* Creator, RLineFactory& rlf) : Command(Creator,"RLINE", 1, 3), factory(rlf)
        {
-               flags_needed = 'o'; this->syntax = "<regex> [<rline-duration>] :<reason>";
+               flags_needed = 'o'; this->syntax = "<regex> [<duration> :<reason>]";
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
 
                if (parameters.size() >= 3)
                {
                        // Adding - XXX todo make this respect <insane> tag perhaps..
 
-                       long duration = ServerInstance->Duration(parameters[1]);
+                       unsigned long duration;
+                       if (!InspIRCd::Duration(parameters[1], duration))
+                       {
+                               user->WriteNotice("*** Invalid duration for R-line.");
+                               return CMD_FAILURE;
+                       }
                        XLine *r = NULL;
 
                        try
@@ -165,7 +161,7 @@ class CommandRLine : public Command
                        }
                        catch (ModuleException &e)
                        {
-                               ServerInstance->SNO->WriteToSnoMask('a',"Could not add RLINE: %s", e.GetReason());
+                               ServerInstance->SNO->WriteToSnoMask('a', "Could not add R-line: " + e.GetReason());
                        }
 
                        if (r)
@@ -174,13 +170,13 @@ class CommandRLine : public Command
                                {
                                        if (!duration)
                                        {
-                                               ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent R-line for %s: %s", user->nick.c_str(), parameters[0].c_str(), parameters[2].c_str());
+                                               ServerInstance->SNO->WriteToSnoMask('x', "%s added permanent R-line for %s: %s", user->nick.c_str(), parameters[0].c_str(), parameters[2].c_str());
                                        }
                                        else
                                        {
-                                               time_t c_requires_crap = duration + ServerInstance->Time();
-                                               std::string timestr = ServerInstance->TimeString(c_requires_crap);
-                                               ServerInstance->SNO->WriteToSnoMask('x', "%s added timed R-line for %s to expire on %s: %s", user->nick.c_str(), parameters[0].c_str(), timestr.c_str(), parameters[2].c_str());
+                                               ServerInstance->SNO->WriteToSnoMask('x', "%s added timed R-line for %s, expires in %s (on %s): %s",
+                                                       user->nick.c_str(), parameters[0].c_str(), InspIRCd::DurationString(duration).c_str(),
+                                                       InspIRCd::TimeString(ServerInstance->Time() + duration).c_str(), parameters[2].c_str());
                                        }
 
                                        ServerInstance->XLines->ApplyLines();
@@ -188,26 +184,28 @@ class CommandRLine : public Command
                                else
                                {
                                        delete r;
-                                       user->WriteServ("NOTICE %s :*** R-Line for %s already exists", user->nick.c_str(), parameters[0].c_str());
+                                       user->WriteNotice("*** R-line for " + parameters[0] + " already exists.");
                                }
                        }
                }
                else
                {
-                       if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "R", user))
+                       std::string reason;
+
+                       if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "R", reason, user))
                        {
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s removed R-line on %s",user->nick.c_str(),parameters[0].c_str());
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s removed R-line on %s: %s", user->nick.c_str(), parameters[0].c_str(), reason.c_str());
                        }
                        else
                        {
-                               user->WriteServ("NOTICE %s :*** R-Line %s not found in list, try /stats R.",user->nick.c_str(),parameters[0].c_str());
+                               user->WriteNotice("*** R-line " + parameters[0] + " not found on the list.");
                        }
                }
 
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                if (IS_LOCAL(user))
                        return ROUTE_LOCALONLY; // spanningtree will send ADDLINE
@@ -216,9 +214,8 @@ class CommandRLine : public Command
        }
 };
 
-class ModuleRLine : public Module
+class ModuleRLine : public Module, public Stats::EventListener
 {
- private:
        dynamic_reference<RegexFactory> rxfactory;
        RLineFactory f;
        CommandRLine r;
@@ -228,34 +225,31 @@ class ModuleRLine : public Module
 
  public:
        ModuleRLine()
-               : rxfactory(this, "regex"), f(rxfactory), r(this, f)
+               : Stats::EventListener(this)
+               , rxfactory(this, "regex")
+               , f(rxfactory)
+               , r(this, f)
                , initing(true)
        {
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
-               OnRehash(NULL);
-
-               ServerInstance->Modules->AddService(r);
                ServerInstance->XLines->RegisterFactory(&f);
-
-               Implementation eventlist[] = { I_OnUserRegister, I_OnRehash, I_OnUserPostNick, I_OnStats, I_OnBackgroundTimer, I_OnUnloadModule };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       virtual ~ModuleRLine()
+       ~ModuleRLine()
        {
                ServerInstance->XLines->DelAll("R");
                ServerInstance->XLines->UnregisterFactory(&f);
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("RLINE: Regexp user banning.", VF_COMMON | VF_VENDOR, rxfactory ? rxfactory->name : "");
+               return Version("Provides support for banning users through regular expression patterns", VF_COMMON | VF_VENDOR, rxfactory ? rxfactory->name : "");
        }
 
-       ModResult OnUserRegister(LocalUser* user)
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
        {
                // Apply lines on user connect
                XLine *rl = ServerInstance->XLines->MatchesLine("R", user);
@@ -269,7 +263,7 @@ class ModuleRLine : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual void OnRehash(User *user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("rline");
 
@@ -287,31 +281,31 @@ class ModuleRLine : public Module
                if (!rxfactory)
                {
                        if (newrxengine.empty())
-                               ServerInstance->SNO->WriteToSnoMask('a', "WARNING: No regex engine loaded - R-Line functionality disabled until this is corrected.");
+                               ServerInstance->SNO->WriteToSnoMask('a', "WARNING: No regex engine loaded - R-line functionality disabled until this is corrected.");
                        else
-                               ServerInstance->SNO->WriteToSnoMask('a', "WARNING: Regex engine '%s' is not loaded - R-Line functionality disabled until this is corrected.", newrxengine.c_str());
+                               ServerInstance->SNO->WriteToSnoMask('a', "WARNING: Regex engine '%s' is not loaded - R-line functionality disabled until this is corrected.", newrxengine.c_str());
 
                        ServerInstance->XLines->DelAll(f.GetType());
                }
                else if ((!initing) && (rxfactory.operator->() != factory))
                {
-                       ServerInstance->SNO->WriteToSnoMask('a', "Regex engine has changed, removing all R-Lines");
+                       ServerInstance->SNO->WriteToSnoMask('a', "Regex engine has changed, removing all R-lines.");
                        ServerInstance->XLines->DelAll(f.GetType());
                }
 
                initing = false;
        }
 
-       virtual ModResult OnStats(char symbol, User* user, string_list &results)
+       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;
        }
 
-       virtual void OnUserPostNick(User *user, const std::string &oldnick)
+       void OnUserPostNick(User *user, const std::string &oldnick) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return;
@@ -328,7 +322,7 @@ class ModuleRLine : public Module
                }
        }
 
-       virtual void OnBackgroundTimer(time_t curtime)
+       void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
        {
                if (added_zline)
                {
@@ -337,9 +331,9 @@ class ModuleRLine : public Module
                }
        }
 
-       void OnUnloadModule(Module* mod)
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE
        {
-               // If the regex engine became unavailable or has changed, remove all rlines
+               // If the regex engine became unavailable or has changed, remove all R-lines.
                if (!rxfactory)
                {
                        ServerInstance->XLines->DelAll(f.GetType());
@@ -351,7 +345,7 @@ class ModuleRLine : public Module
                }
        }
 
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                Module* mod = ServerInstance->Modules->Find("m_cgiirc.so");
                ServerInstance->Modules->SetPriority(this, I_OnUserRegister, PRIORITY_AFTER, mod);
diff --git a/src/modules/m_rmode.cpp b/src/modules/m_rmode.cpp
new file mode 100644 (file)
index 0000000..7db988b
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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/>.
+ */
+
+
+#include "inspircd.h"
+#include "listmode.h"
+
+/** Handle /RMODE
+ */
+class CommandRMode : public Command
+{
+ public:
+       CommandRMode(Module* Creator) : Command(Creator,"RMODE", 2, 3)
+       {
+               allow_empty_last_param = false;
+               syntax = "<channel> <mode> [<pattern>]";
+       }
+
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               ModeHandler* mh;
+               Channel* chan = ServerInstance->FindChan(parameters[0]);
+               char modeletter = parameters[1][0];
+
+               if (chan == NULL)
+               {
+                       user->WriteNotice("The channel " + parameters[0] + " does not exist.");
+                       return CMD_FAILURE;
+               }
+
+               mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
+               if (mh == NULL || parameters[1].size() > 1)
+               {
+                       user->WriteNotice(parameters[1] + " is not a valid channel mode.");
+                       return CMD_FAILURE;
+               }
+
+               if (chan->GetPrefixValue(user) < mh->GetLevelRequired(false))
+               {
+                       user->WriteNotice("You do not have access to unset " + ConvToStr(modeletter) + " on " +  chan->name + ".");
+                       return CMD_FAILURE;
+               }
+
+               std::string pattern = parameters.size() > 2 ? parameters[2] : "*";
+               PrefixMode* pm;
+               ListModeBase* lm;
+               ListModeBase::ModeList* ml;
+               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.
+                       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(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))
+               {
+                       for (ListModeBase::ModeList::iterator it = ml->begin(); it != ml->end(); ++it)
+                       {
+                               if (!InspIRCd::Match(it->mask, pattern))
+                                       continue;
+                               changelist.push_remove(mh, it->mask);
+                       }
+               }
+               else
+               {
+                       if (chan->IsModeSet(mh))
+                               changelist.push_remove(mh);
+               }
+
+               ServerInstance->Modes->Process(user, chan, NULL, changelist);
+               return CMD_SUCCESS;
+       }
+};
+
+class ModuleRMode : public Module
+{
+       CommandRMode cmd;
+
+ public:
+       ModuleRMode() : cmd(this) { }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Allows glob-based removal of list modes", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleRMode)
index 7ac46573224f8eb9aa1e6f9edd7bc08e4060b6f5..39ebb28cc2799f13956496e4ec9bac58f63069cb 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides command SAJOIN to allow opers to force-join users to channels */
-
 /** Handle /SAJOIN
  */
 class CommandSajoin : public Command
 {
  public:
-       CommandSajoin(Module* Creator) : Command(Creator,"SAJOIN", 2)
+       CommandSajoin(Module* Creator) : Command(Creator,"SAJOIN", 1)
        {
                allow_empty_last_param = false;
-               flags_needed = 'o'; Penalty = 0; syntax = "<nick> <channel>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               flags_needed = 'o'; syntax = "[<nick>] <channel>[,<channel>]+";
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
+               const unsigned int channelindex = (parameters.size() > 1) ? 1 : 0;
+               if (CommandParser::LoopCall(user, this, parameters, channelindex))
+                       return CMD_FAILURE;
+
+               const std::string& channel = parameters[channelindex];
+               const std::string& nickname = parameters.size() > 1 ? parameters[0] : user->nick;
+
+               User* dest = ServerInstance->FindNick(nickname);
                if ((dest) && (dest->registered == REG_ALL))
                {
-                       if (ServerInstance->ULine(dest->server))
+                       if (user != dest && !user->HasPrivPermission("users/sajoin-others"))
+                       {
+                               user->WriteNotice("*** You are not allowed to /SAJOIN other users (the privilege users/sajoin-others is needed to /SAJOIN others).");
+                               return CMD_FAILURE;
+                       }
+
+                       if (dest->server->IsULine())
                        {
-                               user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+                               user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a U-lined client");
                                return CMD_FAILURE;
                        }
-                       if (IS_LOCAL(user) && !ServerInstance->IsChannel(parameters[1].c_str(), ServerInstance->Config->Limits.ChanMax))
+                       if (IS_LOCAL(user) && !ServerInstance->IsChannel(channel))
                        {
                                /* we didn't need to check this for each character ;) */
-                               user->WriteServ("NOTICE "+user->nick+" :*** Invalid characters in channel name or name too long");
+                               user->WriteNotice("*** Invalid characters in channel name or name too long");
                                return CMD_FAILURE;
                        }
 
-                       /* For local users, we send the JoinUser which may create a channel and set its TS.
+                       Channel* chan = ServerInstance->FindChan(channel);
+                       if ((chan) && (chan->HasUser(dest)))
+                       {
+                               user->WriteRemoteNotice("*** " + dest->nick + " is already on " + channel);
+                               return CMD_FAILURE;
+                       }
+
+                       /* For local users, we call Channel::JoinUser which may create a channel and set its TS.
                         * For non-local users, we just return CMD_SUCCESS, knowing this will propagate it where it needs to be
-                        * and then that server will generate the users JOIN or FJOIN instead.
+                        * and then that server will handle the command.
                         */
-                       if (IS_LOCAL(dest))
+                       LocalUser* localuser = IS_LOCAL(dest);
+                       if (localuser)
                        {
-                               Channel::JoinUser(dest, parameters[1].c_str(), true, "", false, ServerInstance->Time());
-                               /* Fix for dotslasher and w00t - if the join didnt succeed, return CMD_FAILURE so that it doesnt propagate */
-                               Channel* n = ServerInstance->FindChan(parameters[1]);
-                               if (n)
+                               chan = Channel::JoinUser(localuser, channel, true);
+                               if (chan)
                                {
-                                       if (n->HasUser(dest))
-                                       {
-                                               ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAJOIN to make "+dest->nick+" join "+parameters[1]);
-                                               return CMD_SUCCESS;
-                                       }
-                                       else
-                                       {
-                                               user->WriteServ("NOTICE "+user->nick+" :*** Could not join "+dest->nick+" to "+parameters[1]+" (User is probably banned, or blocking modes)");
-                                               return CMD_FAILURE;
-                                       }
+                                       ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAJOIN to make "+dest->nick+" join "+channel);
+                                       return CMD_SUCCESS;
                                }
                                else
                                {
-                                       user->WriteServ("NOTICE "+user->nick+" :*** Could not join "+dest->nick+" to "+parameters[1]);
+                                       user->WriteNotice("*** Could not join "+dest->nick+" to "+channel);
                                        return CMD_FAILURE;
                                }
                        }
@@ -87,17 +96,14 @@ class CommandSajoin : public Command
                }
                else
                {
-                       user->WriteServ("NOTICE "+user->nick+" :*** No such nickname "+parameters[0]);
+                       user->WriteNotice("*** No such nickname: '" + nickname + "'");
                        return CMD_FAILURE;
                }
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -110,20 +116,10 @@ class ModuleSajoin : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleSajoin()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Provides the SAJOIN command, allows opers to force-join users to channels", VF_OPTCOMMON | VF_VENDOR);
        }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides command SAJOIN to allow opers to force-join users to channels", VF_OPTCOMMON | VF_VENDOR);
-       }
-
 };
 
 MODULE_INIT(ModuleSajoin)
index afca49e251ba17b3294670277915a5f17b8b0bd5..a323ed85c65cb0a6fda1952d0f72dff46fb48b07 100644 (file)
@@ -20,8 +20,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides a SAKICK command */
-
 /** Handle /SAKICK
  */
 class CommandSakick : public Command
@@ -29,30 +27,28 @@ class CommandSakick : public Command
  public:
        CommandSakick(Module* Creator) : Command(Creator,"SAKICK", 2, 3)
        {
-               flags_needed = 'o'; Penalty = 0; syntax = "<channel> <nick> [reason]";
-               TRANSLATE4(TR_TEXT, TR_NICK, TR_TEXT, TR_END);
+               flags_needed = 'o'; syntax = "<channel> <nick> [:<reason>]";
+               TRANSLATE3(TR_TEXT, TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                User* dest = ServerInstance->FindNick(parameters[1]);
                Channel* channel = ServerInstance->FindChan(parameters[0]);
-               const char* reason = "";
 
                if ((dest) && (dest->registered == REG_ALL) && (channel))
                {
-                       if (parameters.size() > 2)
-                       {
-                               reason = parameters[2].c_str();
-                       }
-                       else
+                       const std::string& reason = (parameters.size() > 2) ? parameters[2] : dest->nick;
+
+                       if (dest->server->IsULine())
                        {
-                               reason = dest->nick.c_str();
+                               user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a U-lined client");
+                               return CMD_FAILURE;
                        }
 
-                       if (ServerInstance->ULine(dest->server))
+                       if (!channel->HasUser(dest))
                        {
-                               user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client", user->nick.c_str());
+                               user->WriteNotice("*** " + dest->nick + " is not on " + channel->name);
                                return CMD_FAILURE;
                        }
 
@@ -62,31 +58,24 @@ class CommandSakick : public Command
                         */
                        if (IS_LOCAL(dest))
                        {
+                               // Target is on this server, kick them and send the snotice
                                channel->KickUser(ServerInstance->FakeClient, dest, reason);
-                       }
-
-                       if (IS_LOCAL(user))
-                       {
-                               /* Locally issued command; send the snomasks */
-                               ServerInstance->SNO->WriteGlobalSno('a', user->nick + " SAKICKed " + dest->nick + " on " + parameters[0]);
+                               ServerInstance->SNO->WriteGlobalSno('a', user->nick + " SAKICKed " + dest->nick + " on " + channel->name);
                        }
 
                        return CMD_SUCCESS;
                }
                else
                {
-                       user->WriteServ("NOTICE %s :*** Invalid nickname or channel", user->nick.c_str());
+                       user->WriteNotice("*** Invalid nickname or channel");
                }
 
                return CMD_FAILURE;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[1]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[1]);
        }
 };
 
@@ -99,21 +88,10 @@ class ModuleSakick : public Module
        {
        }
 
-       void init()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd);
+               return Version("Provides the SAKICK command", VF_OPTCOMMON|VF_VENDOR);
        }
-
-       virtual ~ModuleSakick()
-       {
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides a SAKICK command", VF_OPTCOMMON|VF_VENDOR);
-       }
-
 };
 
 MODULE_INIT(ModuleSakick)
-
index ea2ae24ab3d525cc5fc56a156e703a8056f9f0c3..b1642e47085b9ce4b3a61b096ce9a37460a7b556 100644 (file)
  */
 
 
-/* $ModDesc: Provides command SAMODE to allow opers to change modes on channels and users */
-
 #include "inspircd.h"
 
 /** Handle /SAMODE
  */
 class CommandSamode : public Command
 {
+       bool logged;
+
  public:
        bool active;
        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;
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                if (parameters[0].c_str()[0] != '#')
                {
                        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;
+                       }
+
+                       // Changing the modes of another user requires a special permission
+                       if ((target != user) && (!user->HasPrivPermission("users/samode-usermodes")))
+                       {
+                               user->WriteNotice("*** You are not allowed to /SAMODE other users (the privilege users/samode-usermodes is needed to /SAMODE others).");
                                return CMD_FAILURE;
                        }
                }
 
+               // XXX: Make ModeParser clear LastParse
+               Modes::ChangeList emptychangelist;
+               ServerInstance->Modes->ProcessSingle(ServerInstance->FakeClient, NULL, ServerInstance->FakeClient, emptychangelist);
+
+               logged = false;
                this->active = true;
-               ServerInstance->Parser->CallHandler("MODE", parameters, user);
-               if (ServerInstance->Modes->GetLastParse().length())
-                       ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " +ServerInstance->Modes->GetLastParse());
+               ServerInstance->Parser.CallHandler("MODE", parameters, user);
                this->active = false;
+
+               if (!logged)
+               {
+                       // If we haven't logged anything yet 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 could also result in this, but
+                       // that is not possible with /SAMODE because we require at least 2 parameters.
+                       LogUsage(user, stdalgo::string::join(parameters));
+               }
+
                return CMD_SUCCESS;
        }
+
+       void LogUsage(const User* user, const std::string& text)
+       {
+               logged = true;
+               ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " + text);
+       }
 };
 
 class ModuleSaMode : public Module
@@ -67,32 +94,44 @@ class ModuleSaMode : public Module
        {
        }
 
-       void init()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd);
-               ServerInstance->Modules->Attach(I_OnPreMode, this);
+               return Version("Provides the SAMODE command, allows opers to change modes on channels and users", VF_VENDOR);
        }
 
-       ~ModuleSaMode()
+       ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
        {
+               if (cmd.active)
+                       return MOD_RES_ALLOW;
+               return MOD_RES_PASSTHRU;
        }
 
-       Version GetVersion()
+       void OnMode(User* user, User* destuser, Channel* destchan, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags) CXX11_OVERRIDE
        {
-               return Version("Provides command SAMODE to allow opers to change modes on channels and users", VF_VENDOR);
-       }
+               if (!cmd.active)
+                       return;
 
-       ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters)
-       {
-               if (cmd.active)
-                       return MOD_RES_ALLOW;
-               return MOD_RES_PASSTHRU;
+               std::string logtext = (destuser ? destuser->nick : destchan->name);
+               logtext.push_back(' ');
+               logtext += ClientProtocol::Messages::Mode::ToModeLetters(modes);
+
+               for (Modes::ChangeList::List::const_iterator i = modes.getlist().begin(); i != modes.getlist().end(); ++i)
+               {
+                       const Modes::Change& item = *i;
+                       if (!item.param.empty())
+                               logtext.append(1, ' ').append(item.param);
+               }
+
+               cmd.LogUsage(user, logtext);
        }
 
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
+               Module* disable = ServerInstance->Modules->Find("m_disable.so");
+               ServerInstance->Modules->SetPriority(this, I_OnRawMode, PRIORITY_BEFORE, disable);
+
                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);
        }
 };
 
index 4e4be77ae635c6e7f6d73d452dee4c6e88057e81..11dc50ddcd69da3ed463f9183f8471ac5041e5e7 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for SANICK command */
-
 /** Handle /SANICK
  */
 class CommandSanick : public Command
@@ -31,32 +29,32 @@ 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>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               flags_needed = 'o'; syntax = "<nick> <newnick>";
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                User* target = ServerInstance->FindNick(parameters[0]);
 
                /* Do local sanity checks and bails */
                if (IS_LOCAL(user))
                {
-                       if (target && ServerInstance->ULine(target->server))
+                       if (target && target->server->IsULine())
                        {
-                               user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+                               user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a U-lined client");
                                return CMD_FAILURE;
                        }
 
                        if ((!target) || (target->registered != REG_ALL))
                        {
-                               user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick.c_str(), parameters[0].c_str());
+                               user->WriteNotice("*** No such nickname: '" + parameters[0] + "'");
                                return CMD_FAILURE;
                        }
 
-                       if (!ServerInstance->IsNick(parameters[1].c_str(), ServerInstance->Config->Limits.NickMax))
+                       if (!ServerInstance->IsNick(parameters[1]))
                        {
-                               user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick.c_str(), parameters[1].c_str());
+                               user->WriteNotice("*** Invalid nickname: '" + parameters[1] + "'");
                                return CMD_FAILURE;
                        }
                }
@@ -66,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,12 +77,9 @@ class CommandSanick : public Command
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -98,20 +93,10 @@ class ModuleSanick : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleSanick()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Provides the SANICK command, allows opers to change the nicknames of users", VF_OPTCOMMON | VF_VENDOR);
        }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides support for SANICK command", VF_OPTCOMMON | VF_VENDOR);
-       }
-
 };
 
 MODULE_INIT(ModuleSanick)
index 89256e0e480bfc5cc9811e69a27f4a12d66b545e..9fb6b3de5ab165e43b1d40ae12b545fe0e21d90a 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides command SAPART to force-part users from a channel. */
-
 /** Handle /SAPART
  */
 class CommandSapart : public Command
@@ -30,12 +28,15 @@ class CommandSapart : public Command
  public:
        CommandSapart(Module* Creator) : Command(Creator,"SAPART", 2, 3)
        {
-               flags_needed = 'o'; Penalty = 0; syntax = "<nick> <channel> [reason]";
-               TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END);
+               flags_needed = 'o'; syntax = "<nick> <channel>[,<channel>]+ [:<reason>]";
+               TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
+               if (CommandParser::LoopCall(user, this, parameters, 1))
+                       return CMD_FAILURE;
+
                User* dest = ServerInstance->FindNick(parameters[0]);
                Channel* channel = ServerInstance->FindChan(parameters[1]);
                std::string reason;
@@ -45,9 +46,15 @@ class CommandSapart : public Command
                        if (parameters.size() > 2)
                                reason = parameters[2];
 
-                       if (ServerInstance->ULine(dest->server))
+                       if (dest->server->IsULine())
+                       {
+                               user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a U-lined client");
+                               return CMD_FAILURE;
+                       }
+
+                       if (!channel->HasUser(dest))
                        {
-                               user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+                               user->WriteNotice("*** " + dest->nick + " is not on " + channel->name);
                                return CMD_FAILURE;
                        }
 
@@ -58,44 +65,22 @@ class CommandSapart : public Command
                        if (IS_LOCAL(dest))
                        {
                                channel->PartUser(dest, reason);
-
-                               Channel* n = ServerInstance->FindChan(parameters[1]);
-                               if (!n)
-                               {
-                                       ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAPART to make "+dest->nick+" part "+parameters[1]);
-                                       return CMD_SUCCESS;
-                               }
-                               else
-                               {
-                                       if (!n->HasUser(dest))
-                                       {
-                                               ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAPART to make "+dest->nick+" part "+parameters[1]);
-                                               return CMD_SUCCESS;
-                                       }
-                                       else
-                                       {
-                                               user->WriteServ("NOTICE %s :*** Unable to make %s part %s",user->nick.c_str(), dest->nick.c_str(), parameters[1].c_str());
-                                               return CMD_FAILURE;
-                                       }
-                               }
+                               ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAPART to make "+dest->nick+" part "+channel->name);
                        }
 
                        return CMD_SUCCESS;
                }
                else
                {
-                       user->WriteServ("NOTICE %s :*** Invalid nickname or channel", user->nick.c_str());
+                       user->WriteNotice("*** Invalid nickname or channel");
                }
 
                return CMD_FAILURE;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -109,21 +94,10 @@ class ModuleSapart : public Module
        {
        }
 
-       void init()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd);
+               return Version("Provides the SAPART command, allows opers to force-part users from channels", VF_OPTCOMMON | VF_VENDOR);
        }
-
-       virtual ~ModuleSapart()
-       {
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides command SAPART to force-part users from a channel.", VF_OPTCOMMON | VF_VENDOR);
-       }
-
 };
 
 MODULE_INIT(ModuleSapart)
-
index 909a026abe306cacd77663f041b25233990d7d6a..ad3c857e099067ec647f5c63969b5e84fc388235 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for an SAQUIT command, exits user with a reason */
-
 /** Handle /SAQUIT
  */
 class CommandSaquit : public Command
@@ -30,25 +28,25 @@ class CommandSaquit : public Command
  public:
        CommandSaquit(Module* Creator) : Command(Creator, "SAQUIT", 2, 2)
        {
-               flags_needed = 'o'; Penalty = 0; syntax = "<nick> <reason>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               flags_needed = 'o'; syntax = "<nick> :<reason>";
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                User* dest = ServerInstance->FindNick(parameters[0]);
-               if ((dest) && (!IS_SERVER(dest)) && (dest->registered == REG_ALL))
+               if ((dest) && (dest->registered == REG_ALL))
                {
-                       if (ServerInstance->ULine(dest->server))
+                       if (dest->server->IsULine())
                        {
-                               user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+                               user->WriteNumeric(ERR_NOPRIVILEGES, "Cannot use an SA command on a U-lined client");
                                return CMD_FAILURE;
                        }
 
                        // Pass the command on, so the client's server can quit it properly.
                        if (!IS_LOCAL(dest))
                                return CMD_SUCCESS;
-                       
+
                        ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAQUIT to make "+dest->nick+" quit with a reason of "+parameters[1]);
 
                        ServerInstance->Users->QuitUser(dest, parameters[1]);
@@ -56,17 +54,14 @@ class CommandSaquit : public Command
                }
                else
                {
-                       user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNotice("*** Invalid nickname: '" + parameters[0] + "'");
                        return CMD_FAILURE;
                }
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* dest = ServerInstance->FindNick(parameters[0]);
-               if (dest)
-                       return ROUTE_OPT_UCAST(dest->server);
-               return ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -79,20 +74,10 @@ class ModuleSaquit : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleSaquit()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Provides the SAQUIT command, allows opers to force-quit users", VF_OPTCOMMON | VF_VENDOR);
        }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides support for an SAQUIT command, exits user with a reason", VF_OPTCOMMON | VF_VENDOR);
-       }
-
 };
 
 MODULE_INIT(ModuleSaquit)
index 0ef93ec5ae66476923dfe7eaded192dba54711df..54bb8a44a7a1243cc7a2dfcafca1c4d89ddb4126 100644 (file)
 
 
 #include "inspircd.h"
-#include "m_cap.h"
-#include "account.h"
-#include "sasl.h"
-#include "ssl.h"
+#include "modules/cap.h"
+#include "modules/account.h"
+#include "modules/sasl.h"
+#include "modules/ssl.h"
+#include "modules/server.h"
 
-/* $ModDesc: Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE. */
+enum
+{
+       // From IRCv3 sasl-3.1
+       RPL_SASLSUCCESS = 903,
+       ERR_SASLFAIL = 904,
+       ERR_SASLTOOLONG = 905,
+       ERR_SASLABORTED = 906,
+       RPL_SASLMECHS = 908
+};
+
+static std::string sasl_target;
+
+class ServerTracker : public ServerEventListener
+{
+       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)
+               : ServerEventListener(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)
+static void SendSASL(LocalUser* user, const std::string& agent, char mode, const std::vector<std::string>& parameters)
 {
-       if (!ServerInstance->PI->SendEncapsulatedData(params))
+       CommandBase::Params params;
+       params.push_back(user->uuid);
+       params.push_back(agent);
+       params.push_back(ConvToStr(mode));
+       params.insert(params.end(), parameters.begin(), parameters.end());
+
+       if (!ServerInstance->PI->SendEncapsulatedData(sasl_target, "SASL", params))
        {
-               SASLFallback(NULL, params);
+               FOREACH_MOD_CUSTOM(*saslevprov, SASLEventListener, OnSASLAuth, (params));
        }
 }
 
+static ClientProtocol::EventProvider* g_protoev;
+
 /**
  * Tracks SASL authentication state like charybdis does. --nenolod
  */
@@ -46,95 +166,37 @@ class SaslAuthenticator
 {
  private:
        std::string agent;
-       User *user;
+       LocalUser* user;
        SaslState state;
        SaslResult result;
        bool state_announced;
 
-       /* taken from m_services_account */
-       static bool ReadCGIIRCExt(const char* extname, User* user, std::string& out)
+       void SendHostIP(UserCertificateAPI& sslapi)
        {
-               ExtensionItem* wiext = ServerInstance->Extensions.GetItem(extname);
-               if (!wiext)
-                       return false;
-
-               if (wiext->creator->ModuleSourceFile != "m_cgiirc.so")
-                       return false;
+               std::vector<std::string> params;
+               params.push_back(user->GetRealHost());
+               params.push_back(user->GetIPString());
+               params.push_back(sslapi && sslapi->GetCertificate(user) ? "S" : "P");
 
-               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);
-
-               LocalUser* lu = IS_LOCAL(user);
-               if (lu)
-               {
-                       // NOTE: SaslAuthenticator instances are only created for local
-                       // users so this parameter will always be appended.
-                       SocketCertificateRequest req(&lu->eh, ServerInstance->Modules->Find("m_sasl.so"));
-                       params.push_back(req.cert ? "S" : "P");
-               }
-
-               SendSASL(params);
+               SendSASL(user, "*", 'H', params);
        }
 
  public:
-       SaslAuthenticator(User* user_, const std::string& method)
-               : user(user_), state(SASL_INIT), state_announced(false)
+       SaslAuthenticator(LocalUser* user_, const std::string& method, UserCertificateAPI& sslapi)
+               : user(user_)
+               , state(SASL_INIT)
+               , state_announced(false)
        {
-               SendHostIP();
-
-               parameterlist params;
-               params.push_back(sasl_target);
-               params.push_back("SASL");
-               params.push_back(user->uuid);
-               params.push_back("*");
-               params.push_back("S");
+               SendHostIP(sslapi);
+
+               std::vector<std::string> params;
                params.push_back(method);
 
-               if (method == "EXTERNAL" && IS_LOCAL(user_))
-               {
-                       SocketCertificateRequest req(&((LocalUser*)user_)->eh, ServerInstance->Modules->Find("m_sasl.so"));
-                       std::string fp = req.GetFingerprint();
+               const std::string fp = sslapi ? sslapi->GetFingerprint(user) : "";
+               if (fp.size())
+                       params.push_back(fp);
 
-                       if (fp.size())
-                               params.push_back(fp);
-               }
-
-               SendSASL(params);
+               SendSASL(user, "*", 'S', params);
        }
 
        SaslResult GetSaslResult(const std::string &result_)
@@ -149,7 +211,7 @@ class SaslAuthenticator
        }
 
        /* checks for and deals with a state change. */
-       SaslState ProcessInboundMessage(const std::vector<std::string> &msg)
+       SaslState ProcessInboundMessage(const CommandBase::Params& msg)
        {
                switch (this->state)
                {
@@ -165,53 +227,47 @@ class SaslAuthenticator
                                return this->state;
 
                        if (msg[2] == "C")
-                               this->user->Write("AUTHENTICATE %s", msg[3].c_str());
+                       {
+                               ClientProtocol::Message authmsg("AUTHENTICATE");
+                               authmsg.PushParamRef(msg[3]);
+
+                               ClientProtocol::Event authevent(*g_protoev, authmsg);
+                               LocalUser* const localuser = IS_LOCAL(user);
+                               if (localuser)
+                                       localuser->Send(authevent);
+                       }
                        else if (msg[2] == "D")
                        {
                                this->state = SASL_DONE;
                                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(RPL_SASLMECHS, msg[3], "are available SASL mechanisms");
                        else
-                               ServerInstance->Logs->Log("m_sasl", DEFAULT, "Services sent an unknown SASL message \"%s\" \"%s\"", msg[2].c_str(), msg[3].c_str());
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Services sent an unknown SASL message \"%s\" \"%s\"", msg[2].c_str(), msg[3].c_str());
 
                        break;
                 case SASL_DONE:
                        break;
                 default:
-                       ServerInstance->Logs->Log("m_sasl", DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
                        break;
                }
 
                return this->state;
        }
 
-       void Abort(void)
-       {
-               this->state = SASL_DONE;
-               this->result = SASL_ABORT;
-       }
-
        bool SendClientMessage(const std::vector<std::string>& parameters)
        {
                if (this->state != SASL_COMM)
                        return true;
 
-               parameterlist params;
-               params.push_back(sasl_target);
-               params.push_back("SASL");
-               params.push_back(this->user->uuid);
-               params.push_back(this->agent);
-               params.push_back("C");
-
-               params.insert(params.end(), parameters.begin(), parameters.end());
-
-               SendSASL(params);
+               SendSASL(this->user, this->agent, 'C', parameters);
 
                if (parameters[0].c_str()[0] == '*')
                {
-                       this->Abort();
+                       this->state = SASL_DONE;
+                       this->result = SASL_ABORT;
                        return false;
                }
 
@@ -226,13 +282,13 @@ class SaslAuthenticator
                switch (this->result)
                {
                 case SASL_OK:
-                       this->user->WriteNumeric(903, "%s :SASL authentication successful", this->user->nick.c_str());
+                       this->user->WriteNumeric(RPL_SASLSUCCESS, "SASL authentication successful");
                        break;
                 case SASL_ABORT:
-                       this->user->WriteNumeric(906, "%s :SASL authentication aborted", this->user->nick.c_str());
+                       this->user->WriteNumeric(ERR_SASLABORTED, "SASL authentication aborted");
                        break;
                 case SASL_FAIL:
-                       this->user->WriteNumeric(904, "%s :SASL authentication failed", this->user->nick.c_str());
+                       this->user->WriteNumeric(ERR_SASLFAIL, "SASL authentication failed");
                        break;
                 default:
                        break;
@@ -242,32 +298,45 @@ class SaslAuthenticator
        }
 };
 
-class CommandAuthenticate : public Command
+class CommandAuthenticate : public SplitCommand
 {
+ private:
+        // The maximum length of an AUTHENTICATE request.
+        static const size_t MAX_AUTHENTICATE_SIZE = 400;
+
  public:
        SimpleExtItem<SaslAuthenticator>& authExt;
-       GenericCap& cap;
-       CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, GenericCap& Cap)
-               : Command(Creator, "AUTHENTICATE", 1), authExt(ext), cap(Cap)
+       Cap::Capability& cap;
+       UserCertificateAPI sslapi;
+
+       CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, Cap::Capability& Cap)
+               : SplitCommand(Creator, "AUTHENTICATE", 1)
+               , authExt(ext)
+               , cap(Cap)
+               , sslapi(Creator)
        {
                works_before_reg = true;
                allow_empty_last_param = false;
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
        {
-               /* 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;
 
+                       if (parameters[0].length() > MAX_AUTHENTICATE_SIZE)
+                       {
+                               user->WriteNumeric(ERR_SASLTOOLONG, "SASL message too long");
+                               return CMD_FAILURE;
+                       }
+
                        SaslAuthenticator *sasl = authExt.get(user);
                        if (!sasl)
-                               authExt.set(user, new SaslAuthenticator(user, parameters[0]));
+                               authExt.set(user, new SaslAuthenticator(user, parameters[0], sslapi));
                        else if (sasl->SendClientMessage(parameters) == false)  // IAL abort extension --nenolod
                        {
                                sasl->AnnounceState();
@@ -287,12 +356,12 @@ class CommandSASL : public Command
                this->flags_needed = FLAG_SERVERONLY; // should not be called by users
        }
 
-       CmdResult Handle(const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* target = ServerInstance->FindNick(parameters[1]);
-               if ((!target) || (IS_SERVER(target)))
+               User* target = ServerInstance->FindUUID(parameters[1]);
+               if (!target)
                {
-                       ServerInstance->Logs->Log("m_sasl", DEBUG,"User not found in sasl ENCAP event: %s", parameters[1].c_str());
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User not found in sasl ENCAP event: %s", parameters[1].c_str());
                        return CMD_FAILURE;
                }
 
@@ -309,7 +378,7 @@ class CommandSASL : public Command
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_BROADCAST;
        }
@@ -318,51 +387,52 @@ class CommandSASL : public Command
 class ModuleSASL : public Module
 {
        SimpleExtItem<SaslAuthenticator> authExt;
-       GenericCap cap;
+       ServerTracker servertracker;
+       SASLCap cap;
        CommandAuthenticate auth;
        CommandSASL sasl;
+       Events::ModuleEventProvider sasleventprov;
+       ClientProtocol::EventProvider protoev;
+
  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")
+               , protoev(this, auth.name)
        {
+               saslevprov = &sasleventprov;
+               g_protoev = &protoev;
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnEvent, I_OnUserConnect, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
-               ServiceProvider* providelist[] = { &auth, &sasl, &authExt };
-               ServerInstance->Modules->AddServices(providelist, 3);
-
                if (!ServerInstance->Modules->Find("m_services_account.so") || !ServerInstance->Modules->Find("m_cap.so"))
-                       ServerInstance->Logs->Log("m_sasl", DEFAULT, "WARNING: m_services_account.so and m_cap.so are not loaded! m_sasl.so will NOT function correctly until these two modules are loaded!");
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: m_services_account and m_cap are not loaded! m_sasl will NOT function correctly until these two modules are loaded!");
        }
 
-       void OnRehash(User*)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               sasl_target = ServerInstance->Config->ConfValue("sasl")->getString("target", "*");
-       }
+               std::string target = ServerInstance->Config->ConfValue("sasl")->getString("target");
+               if (target.empty())
+                       throw ModuleException("<sasl:target> must be set to the name of your services server!");
 
-       void OnUserConnect(LocalUser *user)
-       {
-               SaslAuthenticator *sasl_ = authExt.get(user);
-               if (sasl_)
-               {
-                       sasl_->Abort();
-                       authExt.unset(user);
-               }
+               sasl_target = target;
+               servertracker.Reset();
        }
 
-       Version GetVersion()
+       void OnDecodeMetaData(Extensible* target, const std::string& extname, const std::string& extdata) CXX11_OVERRIDE
        {
-               return Version("Provides support for IRC Authentication Layer (aka: SASL) via AUTHENTICATE.", VF_VENDOR);
+               if ((target == NULL) && (extname == "saslmechlist"))
+                       cap.SetMechlist(extdata);
        }
 
-       void OnEvent(Event &ev)
+       Version GetVersion() CXX11_OVERRIDE
        {
-               cap.HandleEvent(ev);
+               return Version("Provides support for IRC Authentication Layer (aka: SASL) via AUTHENTICATE", VF_VENDOR);
        }
 };
 
index ae1c19d9148022e2324d27ba96ec19182b051f99..dc1e954887313a7a158e946ddde4c5b39f1810ac 100644 (file)
@@ -17,8 +17,6 @@
  */
 
 
-/* $ModDesc: Provides a SATOPIC command */
-
 #include "inspircd.h"
 
 /** Handle /SATOPIC
@@ -28,10 +26,10 @@ 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 = "<channel> :<topic>";
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                /*
                 * Handles a SATOPIC request. Notifies all +s users.
@@ -40,17 +38,21 @@ class CommandSATopic : public Command
 
                if(target)
                {
-                       std::string newTopic = parameters[1];
+                       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;
+                       }
 
-                       // 3rd parameter overrides access checks
-                       target->SetTopic(user, newTopic, true);
+                       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(401, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
                        return CMD_FAILURE;
                }
        }
@@ -65,18 +67,9 @@ class ModuleSATopic : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleSATopic()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides a SATOPIC command", VF_VENDOR);
+               return Version("Provides the SATOPIC command", VF_VENDOR);
        }
 };
 
index 5d11d27f7b0fa16b72a2e568d232745e279e0f42..e74134a3a764213c6f74abf1fd506acae9828441 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/account.h"
 
-/* $ModDesc: Disallows /LIST for recently connected clients to hinder spam bots */
+typedef std::vector<std::string> AllowList;
 
 class ModuleSecureList : public Module
 {
- private:
-       std::vector<std::string> allowlist;
-       time_t WaitTime;
- public:
-       void init()
-       {
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnRehash, I_OnPreCommand, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ~ModuleSecureList()
-       {
-       }
+       AllowList allowlist;
+       bool exemptregistered;
+       unsigned int WaitTime;
 
-       virtual Version GetVersion()
+ public:
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Disallows /LIST for recently connected clients to hinder spam bots", VF_VENDOR);
+               return Version("Disallows the LIST command for recently connected clients to hinder spam bots", VF_VENDOR);
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               allowlist.clear();
+               AllowList newallows;
 
                ConfigTagList tags = ServerInstance->Config->ConfTags("securehost");
                for (ConfigIter i = tags.first; i != tags.second; ++i)
-                       allowlist.push_back(i->second->getString("exception"));
+               {
+                       std::string host = i->second->getString("exception");
+                       if (host.empty())
+                               throw ModuleException("<securehost:exception> is a required field at " + i->second->getTagLocation());
+                       newallows.push_back(host);
+               }
 
-               WaitTime = ServerInstance->Config->ConfValue("securelist")->getInt("waittime", 60);
+               ConfigTag* tag = ServerInstance->Config->ConfValue("securelist");
+
+               exemptregistered = tag->getBool("exemptregistered");
+               WaitTime = tag->getDuration("waittime", 60, 1);
+               allowlist.swap(newallows);
        }
 
 
@@ -61,34 +61,38 @@ class ModuleSecureList : public Module
         * OnPreCommand()
         *   Intercept the LIST command.
         */
-       virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
        {
                /* If the command doesnt appear to be valid, we dont want to mess with it. */
                if (!validated)
                        return MOD_RES_PASSTHRU;
 
-               if ((command == "LIST") && (ServerInstance->Time() < (user->signon+WaitTime)) && (!IS_OPER(user)))
+               if ((command == "LIST") && (ServerInstance->Time() < (user->signon+WaitTime)) && (!user->IsOper()))
                {
                        /* Normally wouldnt be allowed here, are they exempt? */
                        for (std::vector<std::string>::iterator x = allowlist.begin(); x != allowlist.end(); x++)
                                if (InspIRCd::Match(user->MakeHost(), *x, ascii_case_insensitive_map))
                                        return MOD_RES_PASSTHRU;
 
+                       const AccountExtItem* ext = GetAccountExtItem();
+                       if (exemptregistered && ext && ext->get(user))
+                               return MOD_RES_PASSTHRU;
+
                        /* Not exempt, BOOK EM DANNO! */
-                       user->WriteServ("NOTICE %s :*** You cannot list within the first %lu seconds of connecting. Please try again later.",user->nick.c_str(), (unsigned long) WaitTime);
+                       user->WriteNotice("*** You cannot list within the first " + ConvToStr(WaitTime) + " seconds of connecting. Please try again later.");
                        /* 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(321, "%s Channel :Users Name",user->nick.c_str());
-                       user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
+                       user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
+                       user->WriteNumeric(RPL_LISTEND, "End of channel list.");
                        return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       virtual void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               output.append(" SECURELIST");
+               tokens["SECURELIST"];
        }
 };
 
index 95872b5b27dba2af1dcb2bd068ff580721991f72..557223948b6361a297124388b5db82a9d703ab70 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for seeing local and remote nickchanges via snomasks 'n' and 'N'. */
-
 class ModuleSeeNicks : public Module
 {
  public:
-       void init()
+       void init() CXX11_OVERRIDE
        {
                ServerInstance->SNO->EnableSnomask('n',"NICK");
-               Implementation eventlist[] = { I_OnUserPostNick };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for seeing local and remote nickchanges via snomasks", VF_VENDOR);
+               return Version("Provides snomasks 'n' and 'N' to see local and remote nickchanges", VF_VENDOR);
        }
 
-       virtual void OnUserPostNick(User* user, const std::string &oldnick)
+       void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
        {
                ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'n' : 'N',"User %s changed their nickname to %s", oldnick.c_str(), user->nick.c_str());
        }
index cf77ae9ba75ac99b5cce271eb71e82608c7b7fbd..bd512f58bb23e3d7de5228137f878d9d29dc5b30 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Implements extban +b s: - server name bans */
-
 class ModuleServerBan : public Module
 {
- private:
  public:
-       void init()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               return Version("Provides extban 's' to ban users connected to a specified server", VF_OPTCOMMON|VF_VENDOR);
        }
 
-       ~ModuleServerBan()
-       {
-       }
-
-       Version GetVersion()
-       {
-               return Version("Extban 's' - server ban",VF_OPTCOMMON|VF_VENDOR);
-       }
-
-       ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+       ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
        {
                if ((mask.length() > 2) && (mask[0] == 's') && (mask[1] == ':'))
                {
-                       if (InspIRCd::Match(user->server, mask.substr(2)))
+                       if (InspIRCd::Match(user->server->GetName(), mask.substr(2)))
                                return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('s');
+               tokens["EXTBAN"].push_back('s');
        }
 };
 
-
 MODULE_INIT(ModuleServerBan)
-
index 154968e9ec274b38ca22bd9820d47aaa8d61f6d0..6e15aa8bf7dd138bc466ac9507b250350c4f0889 100644 (file)
  */
 
 
-/* $ModDesc: Provides support for ircu-style services accounts, including chmode +R, etc. */
-
 #include "inspircd.h"
-#include "account.h"
+#include "modules/account.h"
+#include "modules/callerid.h"
+#include "modules/ctctags.h"
+#include "modules/exemption.h"
+#include "modules/whois.h"
+
+enum
+{
+       // From UnrealIRCd.
+       RPL_WHOISREGNICK = 307,
+
+       // From ircu.
+       RPL_WHOISACCOUNT = 330,
+
+       // From ircd-hybrid?
+       ERR_NEEDREGGEDNICK = 477,
+
+       // From IRCv3 sasl-3.1.
+       RPL_LOGGEDIN = 900,
+       RPL_LOGGEDOUT = 901
+};
 
 /** Channel mode +r - mark a channel as identified
  */
@@ -34,21 +52,21 @@ class Channel_r : public ModeHandler
  public:
        Channel_r(Module* Creator) : ModeHandler(Creator, "c_registered", 'r', PARAM_NONE, MODETYPE_CHANNEL) { }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
        {
-               // only a u-lined server may add or remove the +r mode.
+               // Only a U-lined server may add or remove the +r mode.
                if (!IS_LOCAL(source))
                {
                        // Only change the mode if it's not redundant
-                       if ((adding != channel->IsModeSet('r')))
+                       if ((adding != channel->IsModeSet(this)))
                        {
-                               channel->SetMode('r',adding);
+                               channel->SetMode(this, adding);
                                return MODEACTION_ALLOW;
                        }
                }
                else
                {
-                       source->WriteNumeric(500, "%s :Only a server may modify the +r channel mode", source->nick.c_str());
+                       source->WriteNumeric(ERR_NOPRIVILEGES, "Only a server may modify the +r channel mode");
                }
                return MODEACTION_DENY;
        }
@@ -62,128 +80,126 @@ class User_r : public ModeHandler
  public:
        User_r(Module* Creator) : ModeHandler(Creator, "u_registered", 'r', PARAM_NONE, MODETYPE_USER) { }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(source))
                {
-                       if ((adding != dest->IsModeSet('r')))
+                       if ((adding != dest->IsModeSet(this)))
                        {
-                               dest->SetMode('r',adding);
+                               dest->SetMode(this, adding);
                                return MODEACTION_ALLOW;
                        }
                }
                else
                {
-                       source->WriteNumeric(500, "%s :Only a server may modify the +r user mode", source->nick.c_str());
+                       source->WriteNumeric(ERR_NOPRIVILEGES, "Only a server may modify the +r user mode");
                }
                return MODEACTION_DENY;
        }
 };
 
-/** Channel mode +R - unidentified users cannot join
- */
-class AChannel_R : public SimpleChannelModeHandler
+class AccountExtItemImpl : public AccountExtItem
 {
- public:
-       AChannel_R(Module* Creator) : SimpleChannelModeHandler(Creator, "reginvite", 'R') { }
-};
+       Events::ModuleEventProvider eventprov;
 
-/** User mode +R - unidentified users cannot message
- */
-class AUser_R : public SimpleUserModeHandler
-{
  public:
-       AUser_R(Module* Creator) : SimpleUserModeHandler(Creator, "regdeaf", 'R') { }
-};
+       AccountExtItemImpl(Module* mod)
+               : AccountExtItem("accountname", ExtensionItem::EXT_USER, mod)
+               , eventprov(mod, "event/account")
+       {
+       }
 
-/** Channel mode +M - unidentified users cannot message channel
- */
-class AChannel_M : public SimpleChannelModeHandler
-{
- public:
-       AChannel_M(Module* Creator) : SimpleChannelModeHandler(Creator, "regmoderated", 'M') { }
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
+       {
+               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 (IS_LOCAL(user))
+               {
+                       if (value.empty())
+                       {
+                               // Logged out.
+                               user->WriteNumeric(RPL_LOGGEDOUT, user->GetFullHost(), "You are now logged out");
+                       }
+                       else
+                       {
+                               // Logged in.
+                               user->WriteNumeric(RPL_LOGGEDIN, user->GetFullHost(), value, InspIRCd::Format("You are now logged in as %s", value.c_str()));
+                       }
+               }
+
+               FOREACH_MOD_CUSTOM(eventprov, AccountEventListener, OnAccountChange, (user, value));
+       }
 };
 
-class ModuleServicesAccount : public Module
+class ModuleServicesAccount
+       : public Module
+       , public Whois::EventListener
+       , public CTCTags::EventListener
 {
-       AChannel_R m1;
-       AChannel_M m2;
-       AUser_R m3;
+ private:
+       CallerID::API calleridapi;
+       CheckExemption::EventProvider exemptionprov;
+       SimpleChannelModeHandler m1;
+       SimpleChannelModeHandler m2;
+       SimpleUserModeHandler m3;
        Channel_r m4;
        User_r m5;
-       AccountExtItem accountname;
+       AccountExtItemImpl accountname;
        bool checking_ban;
 
-       static bool ReadCGIIRCExt(const char* extname, User* user, const 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;
-       }
-
  public:
-       ModuleServicesAccount() : m1(this), m2(this), m3(this), m4(this), m5(this),
-               accountname("accountname", this), checking_ban(false)
-       {
-       }
-
-       void init()
+       ModuleServicesAccount()
+               : Whois::EventListener(this)
+               , CTCTags::EventListener(this)
+               , calleridapi(this)
+               , exemptionprov(this)
+               , m1(this, "reginvite", 'R')
+               , m2(this, "regmoderated", 'M')
+               , m3(this, "regdeaf", 'R')
+               , m4(this)
+               , m5(this)
+               , accountname(this)
+               , checking_ban(false)
        {
-               ServiceProvider* providerlist[] = { &m1, &m2, &m3, &m4, &m5, &accountname };
-               ServerInstance->Modules->AddServices(providerlist, sizeof(providerlist)/sizeof(ServiceProvider*));
-               Implementation eventlist[] = { I_OnWhois, I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserPreJoin, I_OnCheckBan,
-                       I_OnDecodeMetaData, I_On005Numeric, I_OnUserPostNick, I_OnSetConnectClass };
-
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       void On005Numeric(std::string &t)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('R');
-               ServerInstance->AddExtBanChar('U');
+               tokens["EXTBAN"].push_back('R');
+               tokens["EXTBAN"].push_back('U');
        }
 
        /* <- :twisted.oscnet.org 330 w00t2 w00t2 w00t :is logged in as */
-       void OnWhois(User* source, User* dest)
+       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 %s :is logged in as", source->nick.c_str(), dest->nick.c_str(), account->c_str());
+                       whois.SendLine(RPL_WHOISACCOUNT, *account, "is logged in as");
                }
 
-               if (dest->IsModeSet('r'))
+               if (whois.GetTarget()->IsModeSet(m5))
                {
                        /* user is registered */
-                       ServerInstance->SendWhoisLine(source, dest, 307, "%s %s :is a registered nick", source->nick.c_str(), dest->nick.c_str());
+                       whois.SendLine(RPL_WHOISREGNICK, "is a registered nick");
                }
        }
 
-       void OnUserPostNick(User* user, const std::string &oldnick)
+       void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
        {
                /* On nickchange, if they have +r, remove it */
-               if (user->IsModeSet('r') && assign(user->nick) != oldnick)
-               {
-                       std::vector<std::string> modechange;
-                       modechange.push_back(user->nick);
-                       modechange.push_back("-r");
-                       ServerInstance->SendMode(modechange, ServerInstance->FakeClient);
-               }
+               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)
+       ModResult HandleMessage(User* user, const MessageTarget& target)
        {
                if (!IS_LOCAL(user))
                        return MOD_RES_PASSTHRU;
@@ -191,33 +207,47 @@ class ModuleServicesAccount : public Module
                std::string *account = accountname.get(user);
                bool is_registered = account && !account->empty();
 
-               if (target_type == TYPE_CHANNEL)
+               if (target.type == MessageTarget::TYPE_CHANNEL)
                {
-                       Channel* c = (Channel*)dest;
-                       ModResult res = ServerInstance->OnCheckExemption(user,c,"regmoderated");
+                       Channel* targchan = target.Get<Channel>();
 
-                       if (c->IsModeSet('M') && !is_registered && res != MOD_RES_ALLOW)
-                       {
-                               // user messaging a +M channel and is not registered
-                               user->WriteNumeric(477, user->nick+" "+c->name+" :You need to be identified to a registered account to message this channel");
-                               return MOD_RES_DENY;
-                       }
+                       if (!targchan->IsModeSet(m2) || is_registered)
+                               return MOD_RES_PASSTHRU;
+
+                       if (CheckExemption::Call(exemptionprov, user, targchan, "regmoderated") == MOD_RES_ALLOW)
+                               return MOD_RES_PASSTHRU;
+
+                       // User is messaging a +M channel and is not registered or exempt.
+                       user->WriteNumeric(ERR_NEEDREGGEDNICK, targchan->name, "You need to be identified to a registered account to message this channel");
+                       return MOD_RES_DENY;
                }
-               else if (target_type == TYPE_USER)
+               else if (target.type == MessageTarget::TYPE_USER)
                {
-                       User* u = (User*)dest;
+                       User* targuser = target.Get<User>();
+                       if (!targuser->IsModeSet(m3)  || is_registered)
+                               return MOD_RES_PASSTHRU;
 
-                       if (u->IsModeSet('R') && !is_registered)
-                       {
-                               // user messaging a +R user and is not registered
-                               user->WriteNumeric(477, ""+ user->nick +" "+ u->nick +" :You need to be identified to a registered account to message this user");
-                               return MOD_RES_DENY;
-                       }
+                       if (calleridapi && calleridapi->IsOnAcceptList(user, targuser))
+                               return MOD_RES_PASSTHRU;
+
+                       // User is messaging a +R user and is not registered or on an accept list.
+                       user->WriteNumeric(ERR_NEEDREGGEDNICK, targuser->nick, "You need to be identified to a registered account to message this user");
+                       return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
+       {
+               return HandleMessage(user, target);
+       }
+
+       ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
+       {
+               return HandleMessage(user, target);
+       }
+
+       ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask) CXX11_OVERRIDE
        {
                if (checking_ban)
                        return MOD_RES_PASSTHRU;
@@ -253,27 +283,19 @@ class ModuleServicesAccount : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
-               return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
-       }
-
-       ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
-       {
-               if (!IS_LOCAL(user))
-                       return MOD_RES_PASSTHRU;
-
                std::string *account = accountname.get(user);
                bool is_registered = account && !account->empty();
 
                if (chan)
                {
-                       if (chan->IsModeSet('R'))
+                       if (chan->IsModeSet(m1))
                        {
                                if (!is_registered)
                                {
                                        // joining a +R channel and not identified
-                                       user->WriteNumeric(477, user->nick + " " + chan->name + " :You need to be identified to a registered account to join this channel");
+                                       user->WriteNumeric(ERR_NEEDREGGEDNICK, chan->name, "You need to be identified to a registered account to join this channel");
                                        return MOD_RES_DENY;
                                }
                        }
@@ -281,58 +303,16 @@ class ModuleServicesAccount : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       // Whenever the linking module receives metadata from another server and doesnt know what
-       // to do with it (of course, hence the 'meta') it calls this method, and it is up to each
-       // module in turn to figure out if this metadata key belongs to them, and what they want
-       // to do with it.
-       // In our case we're only sending a single string around, so we just construct a std::string.
-       // Some modules will probably get much more complex and format more detailed structs and classes
-       // in a textual way for sending over the link.
-       void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata)
-       {
-               User* dest = dynamic_cast<User*>(target);
-               // check if its our metadata key, and its associated with a user
-               if (dest && (extname == "accountname"))
-               {
-                       std::string *account = accountname.get(dest);
-                       if (account && !account->empty())
-                       {
-                               trim(*account);
-
-                               if (IS_LOCAL(dest))
-                               {
-                                       const std::string* host = &dest->dhost;
-                                       if (dest->registered != REG_ALL)
-                                       {
-                                               if (!ReadCGIIRCExt("cgiirc_webirc_hostname", dest, host))
-                                               {
-                                                       ReadCGIIRCExt("cgiirc_webirc_ip", dest, host);
-                                               }
-                                       }
-
-                                       dest->WriteNumeric(900, "%s %s!%s@%s %s :You are now logged in as %s",
-                                               dest->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), host->c_str(), account->c_str(), account->c_str());
-                               }
-
-                               AccountEvent(this, dest, *account).Send();
-                       }
-                       else
-                       {
-                               AccountEvent(this, dest, "").Send();
-                       }
-               }
-       }
-
-       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
        {
                if (myclass->config->getBool("requireaccount") && !accountname.get(user))
                        return MOD_RES_DENY;
                return MOD_RES_PASSTHRU;
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for ircu-style services accounts, including chmode +R, etc.",VF_OPTCOMMON|VF_VENDOR);
+               return Version("Provides support for ircu-style services accounts, including channel mode +R, etc", VF_OPTCOMMON|VF_VENDOR);
        }
 };
 
index b4f2b5bbd5ae5ba5a42ac78a9db2587e8a7c4ec9..95a601c7022ca479208a58da73f00d33997b5b55 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/whois.h"
 
-/* $ModDesc: Provides usermode +k to protect services from kicks, kills and mode changes. */
+enum
+{
+       // From AustHex.
+       RPL_WHOISSERVICE = 310,
+
+       // From UnrealIRCd.
+       ERR_KILLDENY = 485
+};
 
 /** Handles user mode +k
  */
@@ -30,7 +38,7 @@ class ServProtectMode : public ModeHandler
  public:
        ServProtectMode(Module* Creator) : ModeHandler(Creator, "servprotect", 'k', PARAM_NONE, MODETYPE_USER) { oper = true; }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
        {
                /* Because this returns MODEACTION_DENY all the time, there is only ONE
                 * way to add this mode and that is at client introduction in the UID command,
@@ -44,47 +52,41 @@ class ServProtectMode : public ModeHandler
        }
 };
 
-class ModuleServProtectMode : public Module
+class ModuleServProtectMode : public Module, public Whois::EventListener, public Whois::LineEventListener
 {
        ServProtectMode bm;
  public:
        ModuleServProtectMode()
-               : bm(this)
-       {
-       }
-
-       void init()
+               : Whois::EventListener(this)
+               , Whois::LineEventListener(this)
+               , bm(this)
        {
-               ServerInstance->Modules->AddService(bm);
-               Implementation eventlist[] = { I_OnWhois, I_OnKill, I_OnWhoisLine, I_OnRawMode, I_OnUserPreKick };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-
-       ~ModuleServProtectMode()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides usermode +k to protect services from kicks, kills, and mode changes.", VF_VENDOR);
+               return Version("Provides user mode +k to protect services from kicks, kills, and mode changes", VF_VENDOR);
        }
 
-       void OnWhois(User* src, User* dst)
+       void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
        {
-               if (dst->IsModeSet('k'))
+               if (whois.GetTarget()->IsModeSet(bm))
                {
-                       ServerInstance->SendWhoisLine(src, dst, 310, src->nick+" "+dst->nick+" :is an "+ServerInstance->Config->Network+" Service");
+                       whois.SendLine(RPL_WHOISSERVICE, "is a Network Service on " + ServerInstance->Config->Network);
                }
        }
 
-       ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string &param, bool adding, int pcnt)
+       ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE
        {
                /* Check that the mode is not a server mode, it is being removed, the user making the change is local, there is a parameter,
                 * and the user making the change is not a uline
                 */
-               if (!adding && chan && IS_LOCAL(user) && !param.empty() && !ServerInstance->ULine(user->server))
+               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);
@@ -95,10 +97,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('k') && memb && memb->modes.find(mode) != std::string::npos)
+                               if ((u->IsModeSet(bm)) && (memb) && (memb->HasMode(pm)))
                                {
                                        /* BZZZT, Denied! */
-                                       user->WriteNumeric(482, "%s %s :You are not permitted to remove privileges from %s services", user->nick.c_str(), 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;
                                }
                        }
@@ -107,37 +109,35 @@ class ModuleServProtectMode : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnKill(User* src, User* dst, const std::string &reason)
+       ModResult OnKill(User* src, User* dst, const std::string &reason) CXX11_OVERRIDE
        {
                if (src == NULL)
                        return MOD_RES_PASSTHRU;
 
-               if (dst->IsModeSet('k'))
+               if (dst->IsModeSet(bm))
                {
-                       src->WriteNumeric(485, "%s :You are not permitted to kill %s services!", src->nick.c_str(), ServerInstance->Config->Network.c_str());
+                       src->WriteNumeric(ERR_KILLDENY, 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;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnUserPreKick(User *src, Membership* memb, const std::string &reason)
+       ModResult OnUserPreKick(User *src, Membership* memb, const std::string &reason) CXX11_OVERRIDE
        {
-               if (memb->user->IsModeSet('k'))
+               if (memb->user->IsModeSet(bm))
                {
-                       src->WriteNumeric(484, "%s %s :You are not permitted to kick services",
-                               src->nick.c_str(), 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)
+       ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
        {
-               return ((src != dst) && (numeric == 319) && dst->IsModeSet('k')) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+               return ((numeric.GetNumeric() == 319) && whois.GetTarget()->IsModeSet(bm)) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
        }
 };
 
-
 MODULE_INIT(ModuleServProtectMode)
index 2ef0c054865a6fcf9a19ee5b1631f6324e0c4938..dad8491530c59da43d3dfb32f4b6dfb90e647fe7 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the SETHOST command */
-
 /** Handle /SETHOST
  */
 class CommandSethost : public Command
 {
- private:
-       char* hostmap;
  public:
-       CommandSethost(Module* Creator, char* hmap) : Command(Creator,"SETHOST", 1), hostmap(hmap)
+       std::bitset<UCHAR_MAX> hostmap;
+
+       CommandSethost(Module* Creator)
+               : Command(Creator,"SETHOST", 1)
        {
                allow_empty_last_param = false;
-               flags_needed = 'o'; syntax = "<new-hostname>";
-               TRANSLATE2(TR_TEXT, TR_END);
+               flags_needed = 'o'; syntax = "<host>";
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               size_t len = 0;
-               for (std::string::const_iterator x = parameters[0].begin(); x != parameters[0].end(); x++, len++)
+               if (parameters[0].length() > ServerInstance->Config->Limits.MaxHost)
                {
-                       if (!hostmap[(const unsigned char)*x])
-                       {
-                               user->WriteServ("NOTICE "+user->nick+" :*** SETHOST: Invalid characters in hostname");
-                               return CMD_FAILURE;
-                       }
+                       user->WriteNotice("*** SETHOST: Host too long");
+                       return CMD_FAILURE;
                }
 
-               if (len > 64)
+               for (std::string::const_iterator x = parameters[0].begin(); x != parameters[0].end(); x++)
                {
-                       user->WriteServ("NOTICE %s :*** SETHOST: Host too long",user->nick.c_str());
-                       return CMD_FAILURE;
+                       if (!hostmap.test(static_cast<unsigned char>(*x)))
+                       {
+                               user->WriteNotice("*** SETHOST: Invalid characters in hostname");
+                               return CMD_FAILURE;
+                       }
                }
 
-               if (user->ChangeDisplayedHost(parameters[0].c_str()))
+               if (user->ChangeDisplayedHost(parameters[0]))
                {
-                       ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SETHOST to change their displayed host to "+user->dhost);
+                       ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SETHOST to change their displayed host to "+user->GetDisplayedHost());
                        return CMD_SUCCESS;
                }
 
@@ -69,39 +66,26 @@ class CommandSethost : public Command
 class ModuleSetHost : public Module
 {
        CommandSethost cmd;
-       char hostmap[256];
+
  public:
        ModuleSetHost()
-               : cmd(this, hostmap)
-       {
-       }
-
-       void init()
+               : cmd(this)
        {
-               OnRehash(NULL);
-               ServerInstance->Modules->AddService(cmd);
-               Implementation eventlist[] = { I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                std::string hmap = ServerInstance->Config->ConfValue("hostname")->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789");
 
-               memset(hostmap, 0, sizeof(hostmap));
+               cmd.hostmap.reset();
                for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
-                       hostmap[(unsigned char)*n] = 1;
+                       cmd.hostmap.set(static_cast<unsigned char>(*n));
        }
 
-       virtual ~ModuleSetHost()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Provides the SETHOST command", VF_VENDOR);
        }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides support for the SETHOST command", VF_VENDOR);
-       }
-
 };
 
 MODULE_INIT(ModuleSetHost)
index f63be1381bb2ecee0ebc61142ac4916922280b55..04b5c97c858a440ac5e9c49f872af1d40081cf86 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the SETIDENT command */
-
 /** Handle /SETIDENT
  */
 class CommandSetident : public Command
@@ -32,32 +30,30 @@ class CommandSetident : public Command
  CommandSetident(Module* Creator) : Command(Creator,"SETIDENT", 1)
        {
                allow_empty_last_param = false;
-               flags_needed = 'o'; syntax = "<new-ident>";
-               TRANSLATE2(TR_TEXT, TR_END);
+               flags_needed = 'o'; syntax = "<ident>";
        }
 
-       CmdResult Handle(const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                if (parameters[0].size() > ServerInstance->Config->Limits.IdentMax)
                {
-                       user->WriteServ("NOTICE %s :*** SETIDENT: Ident is too long", user->nick.c_str());
+                       user->WriteNotice("*** SETIDENT: Ident is too long");
                        return CMD_FAILURE;
                }
 
-               if (!ServerInstance->IsIdent(parameters[0].c_str()))
+               if (!ServerInstance->IsIdent(parameters[0]))
                {
-                       user->WriteServ("NOTICE %s :*** SETIDENT: Invalid characters in ident", user->nick.c_str());
+                       user->WriteNotice("*** SETIDENT: Invalid characters in ident");
                        return CMD_FAILURE;
                }
 
-               user->ChangeIdent(parameters[0].c_str());
+               user->ChangeIdent(parameters[0]);
                ServerInstance->SNO->WriteGlobalSno('a', "%s used SETIDENT to change their ident to '%s'", user->nick.c_str(), user->ident.c_str());
 
                return CMD_SUCCESS;
        }
 };
 
-
 class ModuleSetIdent : public Module
 {
        CommandSetident cmd;
@@ -67,21 +63,10 @@ class ModuleSetIdent : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleSetIdent()
+       Version GetVersion() CXX11_OVERRIDE
        {
+               return Version("Provides the SETIDENT command", VF_VENDOR);
        }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides support for the SETIDENT command", VF_VENDOR);
-       }
-
 };
 
-
 MODULE_INIT(ModuleSetIdent)
index fdb29d14fe7904233bb573950207283ebdb79d13..eaae082499d0c1dfcbccb018cdd197b6fd534956 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Allows opers to set their idle time */
+enum
+{
+       // InspIRCd-specific.
+       ERR_INVALIDIDLETIME = 948,
+       RPL_IDLETIMESET = 944
+};
 
 /** Handle /SETIDLE
  */
-class CommandSetidle : public Command
+class CommandSetidle : public SplitCommand
 {
  public:
-       CommandSetidle(Module* Creator) : Command(Creator,"SETIDLE", 1)
+       CommandSetidle(Module* Creator) : SplitCommand(Creator,"SETIDLE", 1)
        {
                flags_needed = 'o'; syntax = "<duration>";
-               TRANSLATE2(TR_TEXT, TR_END);
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
        {
-               time_t idle = ServerInstance->Duration(parameters[0]);
-               if (idle < 1)
+               unsigned long idle;
+               if (!InspIRCd::Duration(parameters[0], idle))
                {
-                       user->WriteNumeric(948, "%s :Invalid idle time.",user->nick.c_str());
+                       user->WriteNumeric(ERR_INVALIDIDLETIME, "Invalid idle time.");
                        return CMD_FAILURE;
                }
                user->idle_lastmsg = (ServerInstance->Time() - idle);
@@ -47,7 +51,7 @@ class CommandSetidle : public Command
                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, "%s :Idle time set.",user->nick.c_str());
+               user->WriteNumeric(RPL_IDLETIMESET, "Idle time set.");
 
                return CMD_SUCCESS;
        }
@@ -63,18 +67,9 @@ class ModuleSetIdle : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleSetIdle()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Allows opers to set their idle time", VF_VENDOR);
+               return Version("Provides the SETIDLE command, allows opers to set their idle time", VF_VENDOR);
        }
 };
 
index d0610853bc4ce7e1308d01616473136b25c8eb98..846938c8dcabe75f26a520ccb12480c198f27e0f 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for the SETNAME command */
-
 
 
 class CommandSetname : public Command
 {
  public:
+       bool notifyopers;
        CommandSetname(Module* Creator) : Command(Creator,"SETNAME", 1, 1)
        {
                allow_empty_last_param = false;
-               syntax = "<new-gecos>";
-               TRANSLATE2(TR_TEXT, TR_END);
+               syntax = ":<realname>";
        }
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               if (parameters[0].size() > ServerInstance->Config->Limits.MaxGecos)
+               if (parameters[0].size() > ServerInstance->Config->Limits.MaxReal)
                {
-                       user->WriteServ("NOTICE %s :*** SETNAME: GECOS too long", user->nick.c_str());
+                       user->WriteNotice("*** SETNAME: Real name is too long");
                        return CMD_FAILURE;
                }
 
-               if (user->ChangeName(parameters[0].c_str()))
+               if (user->ChangeRealName(parameters[0]))
                {
-                       ServerInstance->SNO->WriteGlobalSno('a', "%s used SETNAME to change their GECOS to '%s'", user->nick.c_str(), parameters[0].c_str());
+                       if (notifyopers)
+                               ServerInstance->SNO->WriteGlobalSno('a', "%s used SETNAME to change their real name to '%s'",
+                                       user->nick.c_str(), parameters[0].c_str());
                }
 
                return CMD_SUCCESS;
@@ -62,18 +62,21 @@ class ModuleSetName : public Module
        {
        }
 
-       void init()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd);
-       }
+               ConfigTag* tag = ServerInstance->Config->ConfValue("setname");
 
-       virtual ~ModuleSetName()
-       {
+               // Whether the module should only be usable by server operators.
+               bool operonly = tag->getBool("operonly");
+               cmd.flags_needed = operonly ? 'o' : 0;
+
+               // Whether a snotice should be sent out when a user changes their real name.
+               cmd.notifyopers = tag->getBool("notifyopers", !operonly);
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for the SETNAME command", VF_VENDOR);
+               return Version("Provides the SETNAME command", VF_VENDOR);
        }
 };
 
diff --git a/src/modules/m_sha1.cpp b/src/modules/m_sha1.cpp
new file mode 100644 (file)
index 0000000..561a7b6
--- /dev/null
@@ -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) CXX11_OVERRIDE
+       {
+               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() CXX11_OVERRIDE
+       {
+               return Version("Implements SHA-1 hashing", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleSHA1)
index 86970968a80740c43ef31204426e2e141854123c..e3ca22a2b9a6c18aa718cc1727eed6304b7a083d 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/// $CompilerFlags: -Ivendor_directory("sha2")
+/// $CompilerFlags: require_compiler("GCC") -Wno-long-long
 
-/* m_sha256 - Based on m_opersha256 written by Special <john@yarbbles.com>
- * Modified and improved by Craig Edwards, December 2006.
- *
- *
- * FIPS 180-2 SHA-224/256/384/512 implementation
- * Last update: 05/23/2005
- * Issue date:  04/30/2005
- *
- * Copyright (C) 2005 Olivier Gay <olivier.gay@a3.epfl.ch>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the project nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
 
-/* $ModDesc: Allows for SHA-256 encrypted oper passwords */
-
-#include "inspircd.h"
-#ifdef HAS_STDINT
-#include <stdint.h>
-#endif
-#include "hash.h"
-
-#ifndef HAS_STDINT
-typedef unsigned int uint32_t;
+// 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
 
-#define SHA256_DIGEST_SIZE (256 / 8)
-#define SHA256_BLOCK_SIZE  (512 / 8)
-
-/** An sha 256 context, used by m_opersha256
- */
-class SHA256Context
-{
- public:
-       unsigned int tot_len;
-       unsigned int len;
-       unsigned char block[2 * SHA256_BLOCK_SIZE];
-       uint32_t h[8];
-};
-
-#define SHFR(x, n)    (x >> n)
-#define ROTR(x, n)   ((x >> n) | (x << ((sizeof(x) << 3) - n)))
-#define ROTL(x, n)   ((x << n) | (x >> ((sizeof(x) << 3) - n)))
-#define CH(x, y, z)  ((x & y) ^ (~x & z))
-#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
-
-#define SHA256_F1(x) (ROTR(x,  2) ^ ROTR(x, 13) ^ ROTR(x, 22))
-#define SHA256_F2(x) (ROTR(x,  6) ^ ROTR(x, 11) ^ ROTR(x, 25))
-#define SHA256_F3(x) (ROTR(x,  7) ^ ROTR(x, 18) ^ SHFR(x,  3))
-#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
-
-#define UNPACK32(x, str)                      \
-{                                            \
-       *((str) + 3) = (uint8_t) ((x)      );      \
-       *((str) + 2) = (uint8_t) ((x) >>  8);      \
-       *((str) + 1) = (uint8_t) ((x) >> 16);      \
-       *((str) + 0) = (uint8_t) ((x) >> 24);      \
-}
-
-#define PACK32(str, x)                  \
-{                                            \
-       *(x) = ((uint32_t) *((str) + 3)      )     \
-       | ((uint32_t) *((str) + 2) <<  8)     \
-       | ((uint32_t) *((str) + 1) << 16)     \
-       | ((uint32_t) *((str) + 0) << 24);    \
-}
-
-/* Macros used for loops unrolling */
-
-#define SHA256_SCR(i)                    \
-{                                            \
-       w[i] =  SHA256_F4(w[i - 2]) + w[i - 7]     \
-       + SHA256_F3(w[i - 15]) + w[i - 16];  \
-}
-
-const unsigned int sha256_h0[8] =
-{
-       0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
-       0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
-};
+#include "inspircd.h"
+#include "modules/hash.h"
 
-uint32_t sha256_k[64] =
-{
-       0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
-       0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
-       0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
-       0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
-       0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
-       0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
-       0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
-       0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
-       0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
-       0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
-       0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
-       0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
-       0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
-       0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
-       0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
-       0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
-};
+#include <sha2.c>
 
 class HashSHA256 : public HashProvider
 {
-       void SHA256Init(SHA256Context *ctx, const unsigned int* ikey)
-       {
-               if (ikey)
-               {
-                       for (int i = 0; i < 8; i++)
-                               ctx->h[i] = ikey[i];
-               }
-               else
-               {
-                       for (int i = 0; i < 8; i++)
-                               ctx->h[i] = sha256_h0[i];
-               }
-               ctx->len = 0;
-               ctx->tot_len = 0;
-       }
-
-       void SHA256Transform(SHA256Context *ctx, unsigned char *message, unsigned int block_nb)
-       {
-               uint32_t w[64];
-               uint32_t wv[8];
-               unsigned char *sub_block;
-               for (unsigned int i = 1; i <= block_nb; i++)
-               {
-                       int j;
-                       sub_block = message + ((i - 1) << 6);
-
-                       for (j = 0; j < 16; j++)
-                               PACK32(&sub_block[j << 2], &w[j]);
-                       for (j = 16; j < 64; j++)
-                               SHA256_SCR(j);
-                       for (j = 0; j < 8; j++)
-                               wv[j] = ctx->h[j];
-                       for (j = 0; j < 64; j++)
-                       {
-                               uint32_t t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j];
-                               uint32_t t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
-                               wv[7] = wv[6];
-                               wv[6] = wv[5];
-                               wv[5] = wv[4];
-                               wv[4] = wv[3] + t1;
-                               wv[3] = wv[2];
-                               wv[2] = wv[1];
-                               wv[1] = wv[0];
-                               wv[0] = t1 + t2;
-                       }
-                       for (j = 0; j < 8; j++)
-                               ctx->h[j] += wv[j];
-               }
-       }
-
-       void SHA256Update(SHA256Context *ctx, unsigned char *message, unsigned int len)
-       {
-               /*
-                * XXX here be dragons!
-                * After many hours of pouring over this, I think I've found the problem.
-                * When Special created our module from the reference one, he used:
-                *
-                *     unsigned int rem_len = SHA256_BLOCK_SIZE - ctx->len;
-                *
-                * instead of the reference's version of:
-                *
-                *     unsigned int tmp_len = SHA256_BLOCK_SIZE - ctx->len;
-                *     unsigned int rem_len = len < tmp_len ? len : tmp_len;
-                *
-                * I've changed back to the reference version of this code, and it seems to work with no errors.
-                * So I'm inclined to believe this was the problem..
-                *             -- w00t (January 06, 2008)
-                */
-               unsigned int tmp_len = SHA256_BLOCK_SIZE - ctx->len;
-               unsigned int rem_len = len < tmp_len ? len : tmp_len;
-
-
-               memcpy(&ctx->block[ctx->len], message, rem_len);
-               if (ctx->len + len < SHA256_BLOCK_SIZE)
-               {
-                       ctx->len += len;
-                       return;
-               }
-               unsigned int new_len = len - rem_len;
-               unsigned int block_nb = new_len / SHA256_BLOCK_SIZE;
-               unsigned char *shifted_message = message + rem_len;
-               SHA256Transform(ctx, ctx->block, 1);
-               SHA256Transform(ctx, shifted_message, block_nb);
-               rem_len = new_len % SHA256_BLOCK_SIZE;
-               memcpy(ctx->block, &shifted_message[block_nb << 6],rem_len);
-               ctx->len = rem_len;
-               ctx->tot_len += (block_nb + 1) << 6;
-       }
-
-       void SHA256Final(SHA256Context *ctx, unsigned char *digest)
-       {
-               unsigned int block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) < (ctx->len % SHA256_BLOCK_SIZE)));
-               unsigned int len_b = (ctx->tot_len + ctx->len) << 3;
-               unsigned int pm_len = block_nb << 6;
-               memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
-               ctx->block[ctx->len] = 0x80;
-               UNPACK32(len_b, ctx->block + pm_len - 4);
-               SHA256Transform(ctx, ctx->block, block_nb);
-               for (int i = 0 ; i < 8; i++)
-                       UNPACK32(ctx->h[i], &digest[i << 2]);
-       }
-
-       void SHA256(const char *src, unsigned char *dest, unsigned int len)
-       {
-               SHA256Context ctx;
-               SHA256Init(&ctx, NULL);
-               SHA256Update(&ctx, (unsigned char *)src, len);
-               SHA256Final(&ctx, dest);
-       }
-
  public:
-       std::string sum(const std::string& data)
+       std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
        {
                unsigned char bytes[SHA256_DIGEST_SIZE];
-               SHA256(data.data(), bytes, data.length());
+               sha256((unsigned char*)data.data(), data.length(),  bytes);
                return std::string((char*)bytes, SHA256_DIGEST_SIZE);
        }
 
-       std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
+       HashSHA256(Module* parent)
+               : HashProvider(parent, "sha256", 32, 64)
        {
-               return "";
        }
-
-       HashSHA256(Module* parent) : HashProvider(parent, "hash/sha256", 32, 64) {}
 };
 
 class ModuleSHA256 : public Module
@@ -277,10 +57,9 @@ class ModuleSHA256 : public Module
  public:
        ModuleSHA256() : sha(this)
        {
-               ServerInstance->Modules->AddService(sha);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Implements SHA-256 hashing", VF_VENDOR);
        }
diff --git a/src/modules/m_showfile.cpp b/src/modules/m_showfile.cpp
new file mode 100644 (file)
index 0000000..150b43e
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 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"
+
+enum
+{
+       // From UnrealIRCd.
+       RPL_RULES = 232,
+       RPL_RULESTART = 308,
+       RPL_RULESEND = 309,
+       ERR_NORULES = 434
+};
+
+class CommandShowFile : public Command
+{
+       enum Method
+       {
+               SF_MSG,
+               SF_NOTICE,
+               SF_NUMERIC
+       };
+
+       std::string introtext;
+       std::string endtext;
+       unsigned int intronumeric;
+       unsigned int textnumeric;
+       unsigned int endnumeric;
+       file_cache contents;
+       Method method;
+
+ public:
+       CommandShowFile(Module* parent, const std::string& cmdname)
+               : Command(parent, cmdname)
+       {
+       }
+
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               if (method == SF_NUMERIC)
+               {
+                       if (!introtext.empty() && intronumeric)
+                               user->WriteRemoteNumeric(intronumeric, introtext);
+
+                       for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
+                               user->WriteRemoteNumeric(textnumeric, InspIRCd::Format("- %s", i->c_str()));
+
+                       if (!endtext.empty() && endnumeric)
+                               user->WriteRemoteNumeric(endnumeric, endtext.c_str());
+               }
+               else if (IS_LOCAL(user))
+               {
+                       LocalUser* const localuser = IS_LOCAL(user);
+                       for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
+                       {
+                               const std::string& line = *i;
+                               ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, localuser, line, ((method == SF_MSG) ? MSG_PRIVMSG : MSG_NOTICE));
+                               localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
+                       }
+               }
+               return CMD_SUCCESS;
+       }
+
+       void UpdateSettings(ConfigTag* tag, const std::vector<std::string>& filecontents)
+       {
+               introtext = tag->getString("introtext", "Showing " + name);
+               endtext = tag->getString("endtext", "End of " + name);
+               intronumeric = tag->getUInt("intronumeric", RPL_RULESTART, 0, 999);
+               textnumeric = tag->getUInt("numeric", RPL_RULES, 0, 999);
+               endnumeric = tag->getUInt("endnumeric", RPL_RULESEND, 0, 999);
+               std::string smethod = tag->getString("method");
+
+               method = SF_NUMERIC;
+               if (smethod == "msg")
+                       method = SF_MSG;
+               else if (smethod == "notice")
+                       method = SF_NOTICE;
+
+               contents = filecontents;
+               InspIRCd::ProcessColors(contents);
+       }
+};
+
+class ModuleShowFile : public Module
+{
+       std::vector<CommandShowFile*> cmds;
+
+       void ReadTag(ConfigTag* tag, std::vector<CommandShowFile*>& newcmds)
+       {
+               std::string cmdname = tag->getString("name");
+               if (cmdname.empty())
+                       throw ModuleException("Empty value for 'name'");
+
+               std::transform(cmdname.begin(), cmdname.end(), cmdname.begin(), ::toupper);
+
+               const std::string file = tag->getString("file", cmdname);
+               if (file.empty())
+                       throw ModuleException("Empty value for 'file'");
+               FileReader reader(file);
+
+               CommandShowFile* sfcmd;
+               Command* handler = ServerInstance->Parser.GetHandler(cmdname);
+               if (handler)
+               {
+                       // Command exists, check if it is ours
+                       if (handler->creator != this)
+                               throw ModuleException("Command " + cmdname + " already exists");
+
+                       // This is our command, make sure we don't have the same entry twice
+                       sfcmd = static_cast<CommandShowFile*>(handler);
+                       if (stdalgo::isin(newcmds, sfcmd))
+                               throw ModuleException("Command " + cmdname + " is already used in a <showfile> tag");
+               }
+               else
+               {
+                       // Command doesn't exist, create it
+                       sfcmd = new CommandShowFile(this, cmdname);
+                       ServerInstance->Modules->AddService(*sfcmd);
+               }
+
+               sfcmd->UpdateSettings(tag, reader.GetVector());
+               newcmds.push_back(sfcmd);
+       }
+
+ public:
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               std::vector<CommandShowFile*> newcmds;
+
+               ConfigTagList tags = ServerInstance->Config->ConfTags("showfile");
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+                       try
+                       {
+                               ReadTag(tag, newcmds);
+                       }
+                       catch (CoreException& ex)
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error: " + ex.GetReason() + " at " + tag->getTagLocation());
+                       }
+               }
+
+               // Remove all commands that were removed from the config
+               std::vector<CommandShowFile*> removed(cmds.size());
+               std::sort(newcmds.begin(), newcmds.end());
+               std::set_difference(cmds.begin(), cmds.end(), newcmds.begin(), newcmds.end(), removed.begin());
+
+               stdalgo::delete_all(removed);
+               cmds.swap(newcmds);
+       }
+
+       ~ModuleShowFile()
+       {
+               stdalgo::delete_all(cmds);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides support for showing text files to users", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleShowFile)
index 398ebf571bda40d549319385a4aed41536d7550a..8532b14eb62d2bf7a7acafe32d0fcf9da9bea0a2 100644 (file)
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Allows opers to set +W to see when a user uses WHOIS on them */
+#include "modules/whois.h"
 
 /** Handle user mode +W
  */
 class SeeWhois : public SimpleUserModeHandler
 {
  public:
-       SeeWhois(Module* Creator, bool IsOpersOnly) : SimpleUserModeHandler(Creator, "showwhois", 'W')
+       SeeWhois(Module* Creator)
+               : SimpleUserModeHandler(Creator, "showwhois", 'W')
        {
-               oper = IsOpersOnly;
+       }
+
+       void SetOperOnly(bool operonly)
+       {
+               oper = operonly;
        }
 };
 
@@ -46,12 +50,12 @@ class WhoisNoticeCmd : public Command
 
        void HandleFast(User* dest, User* src)
        {
-               dest->WriteServ("NOTICE %s :*** %s (%s@%s) did a /whois on you",
-                       dest->nick.c_str(), src->nick.c_str(), src->ident.c_str(),
-                       dest->HasPrivPermission("users/auspex") ? src->host.c_str() : src->dhost.c_str());
+               dest->WriteNotice("*** " + src->nick + " (" + src->ident + "@" +
+                       src->GetHost(dest->HasPrivPermission("users/auspex")) +
+                       ") did a /whois on you");
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                User* dest = ServerInstance->FindNick(parameters[0]);
                if (!dest)
@@ -66,49 +70,42 @@ class WhoisNoticeCmd : public Command
        }
 };
 
-class ModuleShowwhois : public Module
+class ModuleShowwhois : public Module, public Whois::EventListener
 {
        bool ShowWhoisFromOpers;
-       SeeWhois* sw;
+       SeeWhois sw;
        WhoisNoticeCmd cmd;
 
  public:
 
        ModuleShowwhois()
-               : sw(NULL), cmd(this)
+               : Whois::EventListener(this)
+               , sw(this)
+               , cmd(this)
        {
        }
 
-       void init()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("showwhois");
 
-               bool OpersOnly = tag->getBool("opersonly", true);
+               sw.SetOperOnly(tag->getBool("opersonly", true));
                ShowWhoisFromOpers = tag->getBool("showfromopers", true);
-
-               sw = new SeeWhois(this, OpersOnly);
-               ServerInstance->Modules->AddService(*sw);
-               ServerInstance->Modules->AddService(cmd);
-               Implementation eventlist[] = { I_OnWhois };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       ~ModuleShowwhois()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               delete sw;
+               return Version("Provides user mode +W for opers to see when a user uses WHOIS on them", VF_OPTCOMMON|VF_VENDOR);
        }
 
-       Version GetVersion()
+       void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
        {
-               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)
-       {
-               if (!dest->IsModeSet('W') || source == dest)
+               User* const source = whois.GetSource();
+               User* const dest = whois.GetTarget();
+               if (!dest->IsModeSet(sw) || whois.IsSelfWhois())
                        return;
 
-               if (!ShowWhoisFromOpers && IS_OPER(source))
+               if (!ShowWhoisFromOpers && source->IsOper())
                        return;
 
                if (IS_LOCAL(dest))
@@ -117,15 +114,12 @@ class ModuleShowwhois : public Module
                }
                else
                {
-                       std::vector<std::string> params;
-                       params.push_back(dest->server);
-                       params.push_back("WHOISNOTICE");
+                       CommandBase::Params params;
                        params.push_back(dest->uuid);
                        params.push_back(source->uuid);
-                       ServerInstance->PI->SendEncapsulatedData(params);
+                       ServerInstance->PI->SendEncapsulatedData(dest->server->GetName(), cmd.name, params);
                }
        }
-
 };
 
 MODULE_INIT(ModuleShowwhois)
index 98e63f0263bb3cc2e9112e211377e496496632bc..6414b01e79cf2259f3951f070f50fc57269bcac0 100644 (file)
 
 #include "inspircd.h"
 #include "xline.h"
+#include "modules/shun.h"
+#include "modules/stats.h"
 
-/* $ModDesc: Provides the /SHUN command, which stops a user from executing all except configured commands. */
-
-class Shun : public XLine
-{
-public:
-       std::string matchtext;
-
-       Shun(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& shunmask)
-               : XLine(s_time, d, src, re, "SHUN")
-               , matchtext(shunmask)
-       {
-       }
-
-       ~Shun()
-       {
-       }
-
-       bool Matches(User *u)
-       {
-               // E: overrides shun
-               if (u->exempt)
-                       return false;
-
-               if (InspIRCd::Match(u->GetFullHost(), matchtext) || InspIRCd::Match(u->GetFullRealHost(), matchtext) || InspIRCd::Match(u->nick+"!"+u->ident+"@"+u->GetIPString(), matchtext))
-                       return true;
-
-               return false;
-       }
-
-       bool Matches(const std::string &s)
-       {
-               if (matchtext == s)
-                       return true;
-               return false;
-       }
-
-       void DisplayExpiry()
-       {
-               ServerInstance->SNO->WriteToSnoMask('x',"Removing expired shun %s (set by %s %ld seconds ago)",
-                       this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
-       }
-
-       const char* Displayable()
-       {
-               return matchtext.c_str();
-       }
-};
 
 /** An XLineFactory specialized to generate shun pointers
  */
@@ -80,12 +35,12 @@ class ShunFactory : public XLineFactory
 
        /** Generate a shun
        */
-       XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+       XLine* Generate(time_t set_time, unsigned long duration, const std::string& source, const std::string& reason, const std::string& xline_specific_mask) CXX11_OVERRIDE
        {
                return new Shun(set_time, duration, source, reason, xline_specific_mask);
        }
 
-       bool AutoApplyToUserList(XLine *x)
+       bool AutoApplyToUserList(XLine* x) CXX11_OVERRIDE
        {
                return false;
        }
@@ -98,44 +53,50 @@ class CommandShun : public Command
  public:
        CommandShun(Module* Creator) : Command(Creator, "SHUN", 1, 3)
        {
-               flags_needed = 'o'; this->syntax = "<nick!user@hostmask> [<shun-duration>] :<reason>";
+               flags_needed = 'o'; this->syntax = "<nick!user@host> [<duration> :<reason>]";
        }
 
-       CmdResult Handle(const std::vector<std::string>& parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                /* syntax: SHUN nick!user@host time :reason goes here */
                /* 'time' is a human-readable timestring, like 2d3h2s. */
 
                std::string target = parameters[0];
-               
+
                User *find = ServerInstance->FindNick(target);
                if ((find) && (find->registered == REG_ALL))
                        target = std::string("*!*@") + find->GetIPString();
 
                if (parameters.size() == 1)
                {
-                       if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "SHUN", user))
+                       std::string reason;
+
+                       if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "SHUN", reason, user))
                        {
-                               ServerInstance->SNO->WriteToSnoMask('x', "%s removed SHUN on %s", user->nick.c_str(), parameters[0].c_str());
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s removed SHUN on %s: %s", user->nick.c_str(), parameters[0].c_str(), reason.c_str());
                        }
-                       else if (ServerInstance->XLines->DelLine(target.c_str(), "SHUN", user))
+                       else if (ServerInstance->XLines->DelLine(target.c_str(), "SHUN", reason, user))
                        {
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s removed SHUN on %s",user->nick.c_str(),target.c_str());
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s removed SHUN on %s: %s", user->nick.c_str(), target.c_str(), reason.c_str());
                        }
                        else
                        {
-                               user->WriteServ("NOTICE %s :*** Shun %s not found in list, try /stats H.", user->nick.c_str(), parameters[0].c_str());
+                               user->WriteNotice("*** Shun " + parameters[0] + " not found on the list.");
                                return CMD_FAILURE;
                        }
                }
                else
                {
                        // Adding - XXX todo make this respect <insane> tag perhaps..
-                       long duration;
+                       unsigned long duration;
                        std::string expr;
                        if (parameters.size() > 2)
                        {
-                               duration = ServerInstance->Duration(parameters[1]);
+                               if (!InspIRCd::Duration(parameters[1], duration))
+                               {
+                                       user->WriteNotice("*** Invalid duration for SHUN.");
+                                       return CMD_FAILURE;
+                               }
                                expr = parameters[2];
                        }
                        else
@@ -149,28 +110,27 @@ class CommandShun : public Command
                        {
                                if (!duration)
                                {
-                                       ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent SHUN for %s: %s",
+                                       ServerInstance->SNO->WriteToSnoMask('x', "%s added permanent SHUN for %s: %s",
                                                user->nick.c_str(), target.c_str(), expr.c_str());
                                }
                                else
                                {
-                                       time_t c_requires_crap = duration + ServerInstance->Time();
-                                       std::string timestr = ServerInstance->TimeString(c_requires_crap);
-                                       ServerInstance->SNO->WriteToSnoMask('x', "%s added timed SHUN for %s to expire on %s: %s",
-                                               user->nick.c_str(), target.c_str(), timestr.c_str(), expr.c_str());
+                                       ServerInstance->SNO->WriteToSnoMask('x', "%s added timed SHUN for %s, expires in %s (on %s): %s",
+                                               user->nick.c_str(), target.c_str(), InspIRCd::DurationString(duration).c_str(),
+                                               InspIRCd::TimeString(ServerInstance->Time() + duration).c_str(), expr.c_str());
                                }
                        }
                        else
                        {
                                delete r;
-                               user->WriteServ("NOTICE %s :*** Shun for %s already exists", user->nick.c_str(), target.c_str());
+                               user->WriteNotice("*** Shun for " + target + " already exists.");
                                return CMD_FAILURE;
                        }
                }
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                if (IS_LOCAL(user))
                        return ROUTE_LOCALONLY; // spanningtree will send ADDLINE
@@ -179,51 +139,48 @@ class CommandShun : public Command
        }
 };
 
-class ModuleShun : public Module
+class ModuleShun : public Module, public Stats::EventListener
 {
        CommandShun cmd;
        ShunFactory f;
-       std::set<std::string> ShunEnabledCommands;
+       insp::flat_set<std::string> ShunEnabledCommands;
        bool NotifyOfShun;
        bool affectopers;
 
  public:
-       ModuleShun() : cmd(this)
+       ModuleShun()
+               : Stats::EventListener(this)
+               , cmd(this)
        {
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
                ServerInstance->XLines->RegisterFactory(&f);
-               ServerInstance->Modules->AddService(cmd);
-
-               Implementation eventlist[] = { I_OnStats, I_OnPreCommand, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
        }
 
-       virtual ~ModuleShun()
+       ~ModuleShun()
        {
                ServerInstance->XLines->DelAll("SHUN");
                ServerInstance->XLines->UnregisterFactory(&f);
        }
 
-       void Prioritize()
+       void Prioritize() CXX11_OVERRIDE
        {
                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);
        }
 
-       virtual ModResult OnStats(char symbol, User* user, string_list& out)
+       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;
        }
 
-       virtual void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("shun");
                std::string cmds = tag->getString("enabledcommands");
@@ -234,10 +191,10 @@ class ModuleShun : public Module
 
                ShunEnabledCommands.clear();
 
-               std::stringstream dcmds(cmds);
+               irc::spacesepstream dcmds(cmds);
                std::string thiscmd;
 
-               while (dcmds >> thiscmd)
+               while (dcmds.GetToken(thiscmd))
                {
                        ShunEnabledCommands.insert(thiscmd);
                }
@@ -246,7 +203,7 @@ class ModuleShun : public Module
                affectopers = tag->getBool("affectopers", false);
        }
 
-       virtual ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
        {
                if (validated)
                        return MOD_RES_PASSTHRU;
@@ -257,18 +214,16 @@ class ModuleShun : public Module
                        return MOD_RES_PASSTHRU;
                }
 
-               if (!affectopers && IS_OPER(user))
+               if (!affectopers && user->IsOper())
                {
                        /* Don't do anything if the user is an operator and affectopers isn't set */
                        return MOD_RES_PASSTHRU;
                }
 
-               std::set<std::string>::iterator i = ShunEnabledCommands.find(command);
-
-               if (i == ShunEnabledCommands.end())
+               if (!ShunEnabledCommands.count(command))
                {
                        if (NotifyOfShun)
-                               user->WriteServ("NOTICE %s :*** Command %s not processed, as you have been blocked from issuing commands (SHUN)", user->nick.c_str(), command.c_str());
+                               user->WriteNotice("*** Command " + command + " not processed, as you have been blocked from issuing commands (SHUN)");
                        return MOD_RES_DENY;
                }
 
@@ -287,11 +242,10 @@ class ModuleShun : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides the /SHUN command, which stops a user from executing all except configured commands.",VF_VENDOR|VF_COMMON);
+               return Version("Provides the SHUN command, which stops a user from executing all except configured commands", VF_VENDOR|VF_COMMON);
        }
 };
 
 MODULE_INIT(ModuleShun)
-
index c82ab3f9d6052ecf7c21f571bdfee029291b119a..01f99ce7eed5222601d57fcde7c2e4c3a24434fc 100644 (file)
@@ -1,11 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   Copyright (C) 2005-2007 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2006 John Brooks <john.brooks@dereferenced.net>
+ *   Copyright (C) 2019 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
 
 
 #include "inspircd.h"
+#include "modules/ctctags.h"
 
-/* $ModDesc: Provides support for the /SILENCE command */
+enum
+{
+       // From ircu?
+       RPL_SILELIST = 271,
+       RPL_ENDOFSILELIST = 272,
+       ERR_SILELISTFULL = 511,
 
-/* Improved drop-in replacement for the /SILENCE command
- * syntax: /SILENCE [+|-]<mask> <p|c|i|n|t|a|x> as in <privatemessage|channelmessage|invites|privatenotice|channelnotice|all|exclude>
- *
- * example that blocks all except private messages
- *  /SILENCE +*!*@* a
- *  /SILENCE +*!*@* px
- *
- * example that blocks all invites except from channel services
- *  /SILENCE +*!*@* i
- *  /SILENCE +chanserv!services@chatters.net ix
- *
- * example that blocks some bad dude from private, notice and inviting you
- *  /SILENCE +*!kiddie@lamerz.net pin
- *
- * TODO: possibly have add and remove check for existing host and only modify flags according to
- *       what's been changed instead of having to remove first, then add if you want to change
- *       an entry.
- */
+       // InspIRCd-specific.
+       ERR_SILENCE = 952
+};
 
-// pair of hostmask and flags
-typedef std::pair<std::string, int> silenceset;
+class SilenceEntry
+{
+ public:
+       enum SilenceFlags
+       {
+               // Does nothing; for internal use only.
+               SF_NONE = 0,
 
-// deque list of pairs
-typedef std::deque<silenceset> silencelist;
+               // Exclude users who match this flags ("x").
+               SF_EXEMPT = 1,
 
-// intmasks for flags
-static int SILENCE_PRIVATE     = 0x0001; /* p  private messages      */
-static int SILENCE_CHANNEL     = 0x0002; /* c  channel messages      */
-static int SILENCE_INVITE      = 0x0004; /* i  invites               */
-static int SILENCE_NOTICE      = 0x0008; /* n  notices               */
-static int SILENCE_CNOTICE     = 0x0010; /* t  channel notices       */
-static int SILENCE_ALL         = 0x0020; /* a  all, (pcint)          */
-static int SILENCE_EXCLUDE     = 0x0040; /* x  exclude this pattern  */
+               // 2, 4, 8, 16 are reserved for future use.
 
+               // Matches a NOTICE targeted at a channel ("n").
+               SF_NOTICE_CHANNEL = 32,
 
-class CommandSVSSilence : public Command
-{
- public:
-       CommandSVSSilence(Module* Creator) : Command(Creator,"SVSSILENCE", 2)
-       {
-               syntax = "<target> {[+|-]<mask> <p|c|i|n|t|a|x>}";
-               TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
-       }
+               // Matches a NOTICE targeted at a user ("N").
+               SF_NOTICE_USER = 64,
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
-       {
-               /*
-                * XXX: thought occurs to me
-                * We may want to change the syntax of this command to
-                * SVSSILENCE <flagsora+> +<nick> -<nick> +<nick>
-                * style command so services can modify lots of entries at once.
-                * leaving it backwards compatible for now as it's late. -- w
-                */
-               if (!ServerInstance->ULine(user->server))
-                       return CMD_FAILURE;
+               // Matches a PRIVMSG targeted at a channel ("p").
+               SF_PRIVMSG_CHANNEL = 128,
 
-               User *u = ServerInstance->FindNick(parameters[0]);
-               if (!u)
-                       return CMD_FAILURE;
+               // Matches a PRIVMSG targeted at a user ("P").
+               SF_PRIVMSG_USER = 256,
 
-               if (IS_LOCAL(u))
-               {
-                       ServerInstance->Parser->CallHandler("SILENCE", std::vector<std::string>(parameters.begin() + 1, parameters.end()), u);
-               }
+               // Matches a TAGMSG targeted at a channel ("t").
+               SF_TAGMSG_CHANNEL = 512,
 
-               return CMD_SUCCESS;
-       }
+               // Matches a TAGMSG targeted at a user ("T").
+               SF_TAGMSG_USER = 1024,
 
-       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;
-       }
-};
+               // Matches a CTCP targeted at a channel ("c").
+               SF_CTCP_CHANNEL = 2048,
 
-class CommandSilence : public Command
-{
-       unsigned int& maxsilence;
- public:
-       SimpleExtItem<silencelist> ext;
-       CommandSilence(Module* Creator, unsigned int &max) : Command(Creator, "SILENCE", 0),
-               maxsilence(max), ext("silence_list", Creator)
-       {
-               allow_empty_last_param = false;
-               syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}";
-               TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
-       }
+               // Matches a CTCP targeted at a user ("C").
+               SF_CTCP_USER = 4096,
 
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user)
-       {
-               if (!parameters.size())
-               {
-                       // no parameters, show the current silence list.
-                       silencelist* sl = ext.get(user);
-                       // if the user has a silence list associated with their user record, show it
-                       if (sl)
-                       {
-                               for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
-                               {
-                                       std::string decomppattern = DecompPattern(c->second);
-                                       user->WriteNumeric(271, "%s %s %s %s",user->nick.c_str(), user->nick.c_str(),c->first.c_str(), decomppattern.c_str());
-                               }
-                       }
-                       user->WriteNumeric(272, "%s :End of Silence List",user->nick.c_str());
+               // Matches an invite to a channel ("i").
+               SF_INVITE = 8192,
 
-                       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);
-                       char action = parameters[0][0];
-                       // Default is private and notice so clients do not break
-                       int pattern = CompilePattern("pn");
-
-                       // if pattern supplied, use it
-                       if (parameters.size() > 1) {
-                               pattern = CompilePattern(parameters[1].c_str());
-                       }
+               // The default if no flags have been specified.
+               SF_DEFAULT = SF_NOTICE_CHANNEL | SF_NOTICE_USER | SF_PRIVMSG_CHANNEL | SF_PRIVMSG_USER | SF_TAGMSG_CHANNEL |
+                       SF_TAGMSG_USER | SF_CTCP_CHANNEL | SF_CTCP_USER | SF_INVITE
+       };
 
-                       if (pattern == 0)
-                       {
-                               user->WriteServ("NOTICE %s :Bad SILENCE pattern",user->nick.c_str());
-                               return CMD_INVALID;
-                       }
+       // The flags that this mask is silenced for.
+       uint32_t flags;
 
-                       if (!mask.length())
-                       {
-                               // 'SILENCE +' or 'SILENCE -', assume *!*@*
-                               mask = "*!*@*";
-                       }
+       // The mask which is silenced (e.g. *!*@example.com).
+       std::string mask;
 
-                       ModeParser::CleanMask(mask);
+       SilenceEntry(uint32_t Flags, const std::string& Mask)
+               : flags(Flags)
+               , mask(Mask)
+       {
+       }
 
-                       if (action == '-')
-                       {
-                               std::string decomppattern = DecompPattern(pattern);
-                               // fetch their silence list
-                               silencelist* sl = ext.get(user);
-                               // does it contain any entries and does it exist?
-                               if (sl)
-                               {
-                                       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)
-                                               {
-                                                       sl->erase(i);
-                                                       user->WriteNumeric(950, "%s %s :Removed %s %s from silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
-                                                       if (!sl->size())
-                                                       {
-                                                               ext.unset(user);
-                                                       }
-                                                       return CMD_SUCCESS;
-                                               }
-                                       }
-                               }
-                               user->WriteNumeric(952, "%s %s :%s %s does not exist on your silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
-                       }
-                       else if (action == '+')
-                       {
-                               // fetch the user's current silence list
-                               silencelist* sl = ext.get(user);
-                               if (!sl)
-                               {
-                                       sl = new silencelist;
-                                       ext.set(user, sl);
-                               }
-                               if (sl->size() > maxsilence)
-                               {
-                                       user->WriteNumeric(952, "%s %s :Your silence list is full",user->nick.c_str(), user->nick.c_str());
-                                       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)
-                                       {
-                                               user->WriteNumeric(952, "%s %s :%s %s is already on your silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
-                                               return CMD_FAILURE;
-                                       }
-                               }
-                               if (((pattern & SILENCE_EXCLUDE) > 0))
-                               {
-                                       sl->push_front(silenceset(mask,pattern));
-                               }
-                               else
-                               {
-                                       sl->push_back(silenceset(mask,pattern));
-                               }
-                               user->WriteNumeric(951, "%s %s :Added %s %s to silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
-                               return CMD_SUCCESS;
-                       }
-               }
-               return CMD_SUCCESS;
+       bool operator <(const SilenceEntry& other) const
+       {
+               if (flags & SF_EXEMPT && other.flags & ~SF_EXEMPT)
+                       return true;
+               if (other.flags & SF_EXEMPT && flags & ~SF_EXEMPT)
+                       return false;
+               if (flags < other.flags)
+                       return true;
+               if (other.flags < flags)
+                       return false;
+               return mask < other.mask;
        }
 
-       /* turn the nice human readable pattern into a mask */
-       int CompilePattern(const char* pattern)
+       // Converts a flag list to a bitmask.
+       static bool FlagsToBits(const std::string& flags, uint32_t& out)
        {
-               int p = 0;
-               for (const char* n = pattern; *n; n++)
+               out = SF_NONE;
+               for (std::string::const_iterator flag = flags.begin(); flag != flags.end(); ++flag)
                {
-                       switch (*n)
+                       switch (*flag)
                        {
-                               case 'p':
-                                       p |= SILENCE_PRIVATE;
+                               case 'C':
+                                       out |= SF_CTCP_USER;
                                        break;
                                case 'c':
-                                       p |= SILENCE_CHANNEL;
+                                       out |= SF_CTCP_CHANNEL;
+                                       break;
+                               case 'd':
+                                       out |= SF_DEFAULT;
                                        break;
                                case 'i':
-                                       p |= SILENCE_INVITE;
+                                       out |= SF_INVITE;
+                                       break;
+                               case 'N':
+                                       out |= SF_NOTICE_USER;
                                        break;
                                case 'n':
-                                       p |= SILENCE_NOTICE;
+                                       out |= SF_NOTICE_CHANNEL;
                                        break;
-                               case 't':
-                                       p |= SILENCE_CNOTICE;
+                               case 'P':
+                                       out |= SF_PRIVMSG_USER;
+                                       break;
+                               case 'p':
+                                       out |= SF_PRIVMSG_CHANNEL;
+                                       break;
+                               case 'T':
+                                       out |= SF_TAGMSG_USER;
                                        break;
-                               case 'a':
-                               case '*':
-                                       p |= SILENCE_ALL;
+                               case 't':
+                                       out |= SF_TAGMSG_CHANNEL;
                                        break;
                                case 'x':
-                                       p |= SILENCE_EXCLUDE;
+                                       out |= SF_EXEMPT;
                                        break;
                                default:
-                                       break;
+                                       out = SF_NONE;
+                                       return false;
                        }
                }
-               return p;
+               return true;
        }
 
-       /* turn the mask into a nice human readable format */
-       std::string DecompPattern (const int pattern)
+       // Converts a bitmask to a flag list.
+       static std::string BitsToFlags(uint32_t flags)
        {
                std::string out;
-               if (pattern & SILENCE_PRIVATE)
-                       out += ",privatemessages";
-               if (pattern & SILENCE_CHANNEL)
-                       out += ",channelmessages";
-               if (pattern & SILENCE_INVITE)
-                       out += ",invites";
-               if (pattern & SILENCE_NOTICE)
-                       out += ",privatenotices";
-               if (pattern & SILENCE_CNOTICE)
-                       out += ",channelnotices";
-               if (pattern & SILENCE_ALL)
-                       out = ",all";
-               if (pattern & SILENCE_EXCLUDE)
-                       out += ",exclude";
-               if (out.length())
-                       return "<" + out.substr(1) + ">";
-               else
-                       return "<none>";
+               if (flags & SF_CTCP_USER)
+                       out.push_back('C');
+               if (flags & SF_CTCP_CHANNEL)
+                       out.push_back('c');
+               if (flags & SF_INVITE)
+                       out.push_back('i');
+               if (flags & SF_NOTICE_USER)
+                       out.push_back('N');
+               if (flags & SF_NOTICE_CHANNEL)
+                       out.push_back('n');
+               if (flags & SF_PRIVMSG_USER)
+                       out.push_back('P');
+               if (flags & SF_PRIVMSG_CHANNEL)
+                       out.push_back('p');
+               if (flags & SF_TAGMSG_CHANNEL)
+                       out.push_back('T');
+               if (flags & SF_TAGMSG_USER)
+                       out.push_back('t');
+               if (flags & SF_EXEMPT)
+                       out.push_back('x');
+               return out;
        }
-
 };
 
-class ModuleSilence : public Module
+typedef insp::flat_set<SilenceEntry> SilenceList;
+
+class SilenceMessage : public ClientProtocol::Message
 {
-       unsigned int maxsilence;
-       CommandSilence cmdsilence;
-       CommandSVSSilence cmdsvssilence;
  public:
+       SilenceMessage(const std::string& mask, const std::string& flags)
+               : ClientProtocol::Message("SILENCE")
+       {
+               PushParam(mask);
+               PushParamRef(flags);
+       }
+};
 
-       ModuleSilence()
-               : maxsilence(32), cmdsilence(this, maxsilence), cmdsvssilence(this)
+class CommandSilence : public SplitCommand
+{
+ private:
+       ClientProtocol::EventProvider msgprov;
+
+       CmdResult AddSilence(LocalUser* user, const std::string& mask, uint32_t flags)
        {
+               SilenceList* list = ext.get(user);
+               if (list && list->size() > maxsilence)
+               {
+                       user->WriteNumeric(ERR_SILELISTFULL, mask, SilenceEntry::BitsToFlags(flags), "Your SILENCE list is full");
+                       return CMD_FAILURE;
+               }
+               else if (!list)
+               {
+                       // There is no list; create it.
+                       list = new SilenceList();
+                       ext.set(user, list);
+               }
+
+               if (!list->insert(SilenceEntry(flags, mask)).second)
+               {
+                       user->WriteNumeric(ERR_SILENCE, mask, SilenceEntry::BitsToFlags(flags), "The SILENCE entry you specified already exists");
+                       return CMD_FAILURE;
+               }
+
+               SilenceMessage msg("+" + mask, SilenceEntry::BitsToFlags(flags));
+               user->Send(msgprov, msg);
+               return CMD_SUCCESS;
        }
 
-       void init()
+       CmdResult RemoveSilence(LocalUser* user, const std::string& mask, uint32_t flags)
        {
-               OnRehash(NULL);
-               ServerInstance->Modules->AddService(cmdsilence);
-               ServerInstance->Modules->AddService(cmdsvssilence);
-               ServerInstance->Modules->AddService(cmdsilence.ext);
+               SilenceList* list = ext.get(user);
+               if (list)
+               {
+                       for (SilenceList::iterator iter = list->begin(); iter != list->end(); ++iter)
+                       {
+                               if (!irc::equals(iter->mask, mask) || iter->flags != flags)
+                                       continue;
 
-               Implementation eventlist[] = { I_OnRehash, I_On005Numeric, I_OnUserPreNotice, I_OnUserPreMessage, I_OnUserPreInvite };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+                               list->erase(iter);
+                               SilenceMessage msg("-" + mask, SilenceEntry::BitsToFlags(flags));
+                               user->Send(msgprov, msg);
+                               return CMD_SUCCESS;
+                       }
+               }
+
+               user->WriteNumeric(ERR_SILENCE, mask, SilenceEntry::BitsToFlags(flags), "The SILENCE entry you specified could not be found");
+               return CMD_FAILURE;
        }
 
-       void OnRehash(User* user)
+       CmdResult ShowSilenceList(LocalUser* user)
        {
-               maxsilence = ServerInstance->Config->ConfValue("silence")->getInt("maxentries", 32);
-               if (!maxsilence)
-                       maxsilence = 32;
+               SilenceList* list = ext.get(user);
+               if (list)
+               {
+                       for (SilenceList::const_iterator iter = list->begin(); iter != list->end(); ++iter)
+                       {
+                               user->WriteNumeric(RPL_SILELIST, iter->mask, SilenceEntry::BitsToFlags(iter->flags));
+                       }
+               }
+               user->WriteNumeric(RPL_ENDOFSILELIST, "End of SILENCE list");
+               return CMD_SUCCESS;
        }
 
-       void On005Numeric(std::string &output)
+ public:
+       SimpleExtItem<SilenceList> ext;
+       unsigned int maxsilence;
+
+       CommandSilence(Module* Creator)
+               : SplitCommand(Creator, "SILENCE")
+               , msgprov(Creator, "SILENCE")
+               , ext("silence_list", ExtensionItem::EXT_USER, Creator)
        {
-               // we don't really have a limit...
-               output = output + " ESILENCE SILENCE=" + ConvToStr(maxsilence);
+               allow_empty_last_param = false;
+               syntax = "[(+|-)<mask> [CcdiNnPpTtx]]";
        }
 
-       void OnBuildExemptList(MessageType message_type, Channel* chan, User* sender, char status, CUList &exempt_list, const std::string &text)
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
        {
-               int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE);
-               const UserMembList *ulist = chan->GetUsers();
+               if (parameters.empty())
+                       return ShowSilenceList(user);
+
+               // If neither add nor remove are specified we default to add.
+               bool is_remove = parameters[0][0] == '-';
+
+               // If a prefix mask has been given then strip it and clean it up.
+               std::string mask = parameters[0];
+               if (mask[0] == '-' || mask[0] == '+')
+               {
+                       mask.erase(0);
+                       if (mask.empty())
+                               mask.assign("*");
+                       ModeParser::CleanMask(mask);
+               }
 
-               for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+               // If the user specified a flags then use that. Otherwise, default to blocking
+               // all CTCPs, invites, notices, privmsgs, and invites.
+               uint32_t flags = SilenceEntry::SF_DEFAULT;
+               if (parameters.size() > 1)
                {
-                       if (IS_LOCAL(i->first))
+                       if (!SilenceEntry::FlagsToBits(parameters[1], flags))
+                       {
+                               user->WriteNumeric(ERR_SILENCE, mask, parameters[1], "You specified one or more invalid SILENCE flags");
+                               return CMD_FAILURE;
+                       }
+                       else if (flags == SilenceEntry::SF_EXEMPT)
                        {
-                               if (MatchPattern(i->first, sender, public_silence) == MOD_RES_DENY)
-                               {
-                                       exempt_list.insert(i->first);
-                               }
+                               // The user specified "x" with no other flags which does not make sense; add the "d" flag.
+                               flags |= SilenceEntry::SF_DEFAULT;
                        }
                }
+
+               return is_remove ? RemoveSilence(user, mask, flags) : AddSilence(user, mask, flags);
        }
+};
 
-       ModResult PreText(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list, int silence_type)
+class ModuleSilence
+       : public Module
+       , public CTCTags::EventListener
+{
+ private:
+       bool exemptuline;
+       CommandSilence cmd;
+
+       ModResult BuildChannelExempts(User* source, Channel* channel, SilenceEntry::SilenceFlags flag, CUList& exemptions)
        {
-               if (target_type == TYPE_USER && IS_LOCAL(((User*)dest)))
+               const Channel::MemberMap& members = channel->GetUsers();
+               for (Channel::MemberMap::const_iterator member = members.begin(); member != members.end(); ++member)
                {
-                       return MatchPattern((User*)dest, user, silence_type);
+                       if (!CanReceiveMessage(source, member->first, flag))
+                               exemptions.insert(member->first);
                }
-               else if (target_type == TYPE_CHANNEL)
+               return MOD_RES_PASSTHRU;
+       }
+
+       bool CanReceiveMessage(User* source, User* target, SilenceEntry::SilenceFlags flag)
+       {
+               // Servers handle their own clients.
+               if (!IS_LOCAL(target))
+                       return true;
+
+               if (exemptuline && source->server->IsULine())
+                       return true;
+
+               SilenceList* list = cmd.ext.get(target);
+               if (!list)
+                       return true;
+
+               for (SilenceList::iterator iter = list->begin(); iter != list->end(); ++iter)
                {
-                       Channel* chan = (Channel*)dest;
-                       if (chan)
-                       {
-                               this->OnBuildExemptList((silence_type == SILENCE_PRIVATE ? MSG_PRIVMSG : MSG_NOTICE), chan, user, status, exempt_list, "");
-                       }
+                       if (!(iter->flags & flag))
+                               continue;
+
+                       if (InspIRCd::Match(source->GetFullHost(), iter->mask))
+                               return iter->flags & SilenceEntry::SF_EXEMPT;
                }
-               return MOD_RES_PASSTHRU;
+
+               return true;
+       }
+
+ public:
+       ModuleSilence()
+               : CTCTags::EventListener(this)
+               , cmd(this)
+       {
        }
 
-       ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_PRIVATE);
+               ConfigTag* tag = ServerInstance->Config->ConfValue("silence");
+               exemptuline = tag->getBool("exemptuline", true);
+               cmd.maxsilence = tag->getUInt("maxentries", 32, 1);
        }
 
-       ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_NOTICE);
+               tokens["ESILENCE"] = "CcdiNnPpTtx";
+               tokens["SILENCE"] = ConvToStr(cmd.maxsilence);
        }
 
-       ModResult OnUserPreInvite(User* source,User* dest,Channel* channel, time_t timeout)
+       ModResult OnUserPreInvite(User* source, User* dest, Channel* channel, time_t timeout) CXX11_OVERRIDE
        {
-               return MatchPattern(dest, source, SILENCE_INVITE);
+               return CanReceiveMessage(source, dest, SilenceEntry::SF_INVITE) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
        }
 
-       ModResult MatchPattern(User* dest, User* source, int pattern)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               /* Server source */
-               if (!source || !dest)
-                       return MOD_RES_ALLOW;
+               std::string ctcpname;
+               bool is_ctcp = details.IsCTCP(ctcpname) && !irc::equals(ctcpname, "ACTION");
+
+               SilenceEntry::SilenceFlags flag = SilenceEntry::SF_NONE;
+               if (target.type == MessageTarget::TYPE_CHANNEL)
+               {
+                       if (is_ctcp)
+                               flag = SilenceEntry::SF_CTCP_CHANNEL;
+                       else if (details.type == MSG_NOTICE)
+                               flag = SilenceEntry::SF_NOTICE_CHANNEL;
+                       else if (details.type == MSG_PRIVMSG)
+                               flag = SilenceEntry::SF_PRIVMSG_CHANNEL;
+
+                       return BuildChannelExempts(user, target.Get<Channel>(), flag, details.exemptions);
+               }
 
-               silencelist* sl = cmdsilence.ext.get(dest);
-               if (sl)
+               if (target.type == MessageTarget::TYPE_USER)
                {
-                       for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
+                       if (is_ctcp)
+                               flag = SilenceEntry::SF_CTCP_USER;
+                       else if (details.type == MSG_NOTICE)
+                               flag = SilenceEntry::SF_NOTICE_USER;
+                       else if (details.type == MSG_PRIVMSG)
+                               flag = SilenceEntry::SF_PRIVMSG_USER;
+
+                       if (!CanReceiveMessage(user, target.Get<User>(), flag))
                        {
-                               if (((((c->second & pattern) > 0)) || ((c->second & SILENCE_ALL) > 0)) && (InspIRCd::Match(source->GetFullHost(), c->first)))
-                                       return (c->second & SILENCE_EXCLUDE) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
+                               details.echo_original = true;
+                               return MOD_RES_DENY;
                        }
                }
+
                return MOD_RES_PASSTHRU;
        }
 
-       ~ModuleSilence()
+       ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
        {
+               if (target.type == MessageTarget::TYPE_CHANNEL)
+                       return BuildChannelExempts(user, target.Get<Channel>(), SilenceEntry::SF_TAGMSG_CHANNEL, details.exemptions);
+
+               if (target.type == MessageTarget::TYPE_USER && !CanReceiveMessage(user, target.Get<User>(), SilenceEntry::SF_TAGMSG_USER))
+               {
+                       details.echo_original = true;
+                       return MOD_RES_DENY;
+               }
+
+               return MOD_RES_PASSTHRU;
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for the /SILENCE command", VF_OPTCOMMON | VF_VENDOR);
+               return Version("Provides support for blocking users with the SILENCE command", VF_OPTCOMMON | VF_VENDOR);
        }
 };
 
index 16043b2aadba47bb1813084ebc2609edadb5f6de..d6c33d2ff9835b4b99a885a5b42c5e63e0c08a81 100644 (file)
 #include "inspircd.h"
 #include "xline.h"
 
-#include "treesocket.h"
 #include "treeserver.h"
 #include "utils.h"
+#include "commands.h"
 
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-bool TreeSocket::AddLine(const std::string &prefix, parameterlist &params)
+CmdResult CommandAddLine::Handle(User* usr, Params& params)
 {
-       if (params.size() < 6)
-       {
-               std::string servername = MyRoot->GetName();
-               ServerInstance->SNO->WriteToSnoMask('d', "%s sent me a malformed ADDLINE", servername.c_str());
-               return true;
-       }
-
        XLineFactory* xlf = ServerInstance->XLines->GetFactory(params[0]);
-
-       std::string setter = "<unknown>";
-       User* usr = ServerInstance->FindNick(prefix);
-       if (usr)
-               setter = usr->nick;
-       else
-       {
-               TreeServer* t = Utils->FindServer(prefix);
-               if (t)
-                       setter = t->GetName();
-       }
+       const std::string& setter = usr->nick;
 
        if (!xlf)
        {
-               ServerInstance->SNO->WriteToSnoMask('d',"%s sent me an unknown ADDLINE type (%s).",setter.c_str(),params[0].c_str());
-               return true;
+               ServerInstance->SNO->WriteToSnoMask('x', "%s sent me an unknown ADDLINE type (%s).", setter.c_str(), params[0].c_str());
+               return CMD_FAILURE;
        }
 
-       long created = atol(params[3].c_str()), expires = atol(params[4].c_str());
-       if (created < 0 || expires < 0)
-               return true;
-
        XLine* xl = NULL;
        try
        {
-               xl = xlf->Generate(ServerInstance->Time(), expires, params[2], params[5], params[1]);
+               xl = xlf->Generate(ServerInstance->Time(), ConvToNum<unsigned long>(params[4]), params[2], params[5], params[1]);
        }
        catch (ModuleException &e)
        {
-               ServerInstance->SNO->WriteToSnoMask('d',"Unable to ADDLINE type %s from %s: %s", params[0].c_str(), setter.c_str(), e.GetReason());
-               return true;
+               ServerInstance->SNO->WriteToSnoMask('x', "Unable to ADDLINE type %s from %s: %s", params[0].c_str(), setter.c_str(), e.GetReason().c_str());
+               return CMD_FAILURE;
        }
-       xl->SetCreateTime(created);
+       xl->SetCreateTime(ConvToNum<time_t>(params[3]));
        if (ServerInstance->XLines->AddLine(xl, NULL))
        {
                if (xl->duration)
                {
-                       std::string timestr = ServerInstance->TimeString(xl->expiry);
-                       ServerInstance->SNO->WriteToSnoMask('X',"%s added %s%s on %s to expire on %s: %s",setter.c_str(),params[0].c_str(),params[0].length() == 1 ? "-line" : "",
-                                       params[1].c_str(), timestr.c_str(), params[5].c_str());
+                       ServerInstance->SNO->WriteToSnoMask('X', "%s added timed %s%s for %s, expires in %s (on %s): %s",
+                               setter.c_str(), params[0].c_str(), params[0].length() == 1 ? "-line" : "",
+                               params[1].c_str(), InspIRCd::DurationString(xl->duration).c_str(),
+                               InspIRCd::TimeString(xl->expiry).c_str(), params[5].c_str());
                }
                else
                {
-                       ServerInstance->SNO->WriteToSnoMask('X',"%s added permanent %s%s on %s: %s",setter.c_str(),params[0].c_str(),params[0].length() == 1 ? "-line" : "",
-                                       params[1].c_str(),params[5].c_str());
+                       ServerInstance->SNO->WriteToSnoMask('X', "%s added permanent %s%s on %s: %s",
+                               setter.c_str(), params[0].c_str(), params[0].length() == 1 ? "-line" : "",
+                               params[1].c_str(), params[5].c_str());
                }
-               params[5] = ":" + params[5];
 
-               User* u = ServerInstance->FindNick(prefix);
-               Utils->DoOneToAllButSender(prefix, "ADDLINE", params, u ? u->server : prefix);
-               TreeServer *remoteserver = Utils->FindServer(u ? u->server : prefix);
+               TreeServer* remoteserver = TreeServer::Get(usr);
 
-               if (!remoteserver->bursting)
+               if (!remoteserver->IsBursting())
                {
                        ServerInstance->XLines->ApplyLines();
                }
+               return CMD_SUCCESS;
        }
        else
+       {
                delete xl;
-
-       return true;
+               return CMD_FAILURE;
+       }
 }
 
+CommandAddLine::Builder::Builder(XLine* xline, User* user)
+       : CmdBuilder(user, "ADDLINE")
+{
+       push(xline->type);
+       push(xline->Displayable());
+       push(xline->source);
+       push_int(xline->set_time);
+       push_int(xline->duration);
+       push_last(xline->reason);
+}
index ed97c48cdfe7c9e49b055cd8c943fa9d79d0935b..62300580f2ba7584b57d633f5ad02c65f27d5b6e 100644 (file)
 
 #include "main.h"
 #include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
+#include "commands.h"
 
-bool TreeSocket::Away(const std::string &prefix, parameterlist &params)
+CmdResult CommandAway::HandleRemote(::RemoteUser* u, Params& params)
 {
-       User* u = ServerInstance->FindNick(prefix);
-       if ((!u) || (IS_SERVER(u)))
-               return true;
-       if (params.size())
+       if (!params.empty())
        {
-               FOREACH_MOD(I_OnSetAway, OnSetAway(u, params[params.size() - 1]));
-
                if (params.size() > 1)
-                       u->awaytime = atoi(params[0].c_str());
+                       u->awaytime = ConvToNum<time_t>(params[0]);
                else
                        u->awaytime = ServerInstance->Time();
 
-               u->awaymsg = params[params.size() - 1];
-
-               params[params.size() - 1] = ":" + params[params.size() - 1];
+               u->awaymsg = params.back();
+               FOREACH_MOD_CUSTOM(awayevprov, Away::EventListener, OnUserAway, (u));
        }
        else
        {
-               FOREACH_MOD(I_OnSetAway, OnSetAway(u, ""));
+               u->awaytime = 0;
                u->awaymsg.clear();
+               FOREACH_MOD_CUSTOM(awayevprov, Away::EventListener, OnUserBack, (u));
        }
-       Utils->DoOneToAllButSender(prefix,"AWAY",params,u->server);
-       return true;
+       return CMD_SUCCESS;
+}
+
+CommandAway::Builder::Builder(User* user)
+       : CmdBuilder(user, "AWAY")
+{
+       if (!user->awaymsg.empty())
+               push_int(user->awaytime).push_last(user->awaymsg);
 }
diff --git a/src/modules/m_spanningtree/cachetimer.cpp b/src/modules/m_spanningtree/cachetimer.cpp
deleted file mode 100644 (file)
index be43865..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   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/>.
- */
-
-
-#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-
-#include "cachetimer.h"
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "link.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/cachetimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
-
-CacheRefreshTimer::CacheRefreshTimer(SpanningTreeUtilities *Util) : Timer(3600, ServerInstance->Time(), true), Utils(Util)
-{
-}
-
-void CacheRefreshTimer::Tick(time_t TIME)
-{
-       Utils->RefreshIPCache();
-}
-
index bad1b7419e5649250484007463c0d2954df4a64d..489194b869b2a36befd775c2271e5925dee4cfdf 100644 (file)
  */
 
 
-#ifndef M_SPANNINGTREE_CACHETIMER_H
-#define M_SPANNINGTREE_CACHETIMER_H
+#pragma once
 
-#include "timer.h"
-
-class ModuleSpanningTree;
-class SpanningTreeUtilities;
-
-/** 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
 {
- private:
-       SpanningTreeUtilities *Utils;
  public:
-       CacheRefreshTimer(SpanningTreeUtilities* Util);
-       virtual void Tick(time_t TIME);
+       CacheRefreshTimer();
+       bool Tick(time_t TIME) CXX11_OVERRIDE;
 };
-
-#endif
index 0ab815fef71e8eaef8c38516f8d6d236552071b0..ea11a917e90a36f10f1fae3f38a5fc88c8d4dec7 100644 (file)
 
 
 #include "inspircd.h"
-#include "xline.h"
 
-#include "treesocket.h"
 #include "treeserver.h"
 #include "utils.h"
 #include "link.h"
 #include "main.h"
-#include "../hash.h"
 
-std::string TreeSocket::MyModules(int filter)
+struct CompatMod
+{
+       const char* name;
+       ModuleFlags listflag;
+};
+
+static CompatMod compatmods[] =
 {
-       std::vector<std::string> modlist = ServerInstance->Modules->GetAllModuleNames(filter);
+       { "m_watch.so", VF_OPTCOMMON }
+};
 
-       if (filter == VF_COMMON && proto_version != ProtocolVersion)
-               CompatAddModules(modlist);
+std::string TreeSocket::MyModules(int filter)
+{
+       const ModuleManager::ModuleMap& modlist = ServerInstance->Modules->GetModules();
 
        std::string capabilities;
-       sort(modlist.begin(),modlist.end());
-       for (std::vector<std::string>::const_iterator i = modlist.begin(); i != modlist.end(); ++i)
+       for (ModuleManager::ModuleMap::const_iterator i = modlist.begin(); i != modlist.end(); ++i)
        {
-               if (i != modlist.begin())
-                       capabilities.push_back(proto_version > 1201 ? ' ' : ',');
-               capabilities.append(*i);
-               Module* m = ServerInstance->Modules->Find(*i);
-               if (m && proto_version > 1201)
+               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)
                {
-                       Version v = m->GetVersion();
-                       if (!v.link_data.empty())
+                       for (size_t j = 0; j < sizeof(compatmods)/sizeof(compatmods[0]); j++)
                        {
-                               capabilities.push_back('=');
-                               capabilities.append(v.link_data);
+                               if ((compatmods[j].listflag & filter) && (mod->ModuleSourceFile == compatmods[j].name))
+                               {
+                                       do_compat_include = true;
+                                       break;
+                               }
                        }
                }
+
+               Version v = i->second->GetVersion();
+               if ((!do_compat_include) && (!(v.Flags & filter)))
+                       continue;
+
+               if (i != modlist.begin())
+                       capabilities.push_back(' ');
+               capabilities.append(i->first);
+               if (!v.link_data.empty())
+               {
+                       capabilities.push_back('=');
+                       capabilities.append(v.link_data);
+               }
+       }
+
+       // If we are linked in a 2.0 server and have an ascii casemapping
+       // advertise it as m_ascii.so from inspircd-extras
+       if ((filter & VF_COMMON) && ServerInstance->Config->CaseMapping == "ascii" && proto_version == 1202)
+       {
+               if (!capabilities.empty())
+                       capabilities += "m_ascii.so";
        }
+
        return capabilities;
 }
 
-static std::string BuildModeList(ModeType type)
+std::string TreeSocket::BuildModeList(ModeType mtype)
 {
        std::vector<std::string> modes;
-       for(char c='A'; c <= 'z'; c++)
+       const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes.GetModes(mtype);
+       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;
+               const PrefixMode* const pm = mh->IsPrefixMode();
+               std::string mdesc;
+               if (proto_version != 1202)
+               {
+                       if (pm)
+                               mdesc.append("prefix:").append(ConvToStr(pm->GetPrefixRank())).push_back(':');
+                       else if (mh->IsListMode())
+                               mdesc.append("list:");
+                       else if (mh->NeedsParam(true))
+                               mdesc.append(mh->NeedsParam(false) ? "param:" : "param-set:");
+                       else
+                               mdesc.append("simple:");
+               }
+               mdesc.append(mh->name);
+               mdesc.push_back('=');
+               if (pm)
                {
-                       std::string mdesc = mh->name;
-                       mdesc.push_back('=');
-                       if (mh->GetPrefix())
-                               mdesc.push_back(mh->GetPrefix());
-                       if (mh->GetModeChar())
-                               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());
-       irc::stringjoiner line(" ", modes, 0, modes.size() - 1);
-       return line.GetJoined();
+       std::sort(modes.begin(), modes.end());
+       return stdalgo::string::join(modes);
 }
 
 void TreeSocket::SendCapabilities(int phase)
@@ -91,7 +134,7 @@ void TreeSocket::SendCapabilities(int phase)
        if (phase < 2)
                return;
 
-       char sep = proto_version > 1201 ? ' ' : ',';
+       const char sep = ' ';
        irc::sepstream modulelist(MyModules(VF_COMMON), sep);
        irc::sepstream optmodulelist(MyModules(VF_OPTCOMMON), sep);
        /* Send module names, split at 509 length */
@@ -135,13 +178,21 @@ void TreeSocket::SendCapabilities(int phase)
 
        std::string extra;
        /* Do we have sha256 available? If so, we send a challenge */
-       if (Utils->ChallengeResponse && (ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256")))
+       if (ServerInstance->Modules->FindService(SERVICE_DATA, "hash/sha256"))
        {
                SetOurChallenge(ServerInstance->GenRandomStr(20));
                extra = " CHALLENGE=" + this->GetOurChallenge();
        }
-       if (proto_version < 1202)
-               extra += ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL) ? " HALFOP=1" : " HALFOP=0";
+
+       // 2.0 needs these keys.
+       if (proto_version == 1202)
+       {
+               extra.append(" PROTOCOL="+ConvToStr(ProtocolVersion))
+                       .append(" MAXGECOS="+ConvToStr(ServerInstance->Config->Limits.MaxReal))
+                       .append(" CHANMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL))
+                       .append(" USERMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_USER))
+                       .append(" PREFIX="+ ServerInstance->Modes->BuildPrefixes());
+       }
 
        this->WriteLine("CAPAB CAPABILITIES " /* Preprocessor does this one. */
                        ":NICKMAX="+ConvToStr(ServerInstance->Config->Limits.NickMax)+
@@ -151,20 +202,18 @@ void TreeSocket::SendCapabilities(int phase)
                        " MAXQUIT="+ConvToStr(ServerInstance->Config->Limits.MaxQuit)+
                        " MAXTOPIC="+ConvToStr(ServerInstance->Config->Limits.MaxTopic)+
                        " MAXKICK="+ConvToStr(ServerInstance->Config->Limits.MaxKick)+
-                       " MAXGECOS="+ConvToStr(ServerInstance->Config->Limits.MaxGecos)+
+                       " MAXREAL="+ConvToStr(ServerInstance->Config->Limits.MaxReal)+
                        " MAXAWAY="+ConvToStr(ServerInstance->Config->Limits.MaxAway)+
-                       " IP6SUPPORT=1"+
-                       " PROTOCOL="+ConvToStr(ProtocolVersion)+extra+
-                       " PREFIX="+ServerInstance->Modes->BuildPrefixes()+
-                       " CHANMODES="+ServerInstance->Modes->GiveModeList(MASK_CHANNEL)+
-                       " USERMODES="+ServerInstance->Modes->GiveModeList(MASK_USER)+
+                       " MAXHOST="+ConvToStr(ServerInstance->Config->Limits.MaxHost)+
+                       extra+
+                       " CASEMAPPING="+ServerInstance->Config->CaseMapping+
                        // 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")+
-                       " SVSPART=1");
+                       (ServerInstance->Modules->Find("m_globops.so") != NULL ? " GLOBOPS=1" : " GLOBOPS=0")
+                       );
 
        this->WriteLine("CAPAB END");
 }
@@ -196,7 +245,7 @@ void TreeSocket::ListDifference(const std::string &one, const std::string &two,
        }
 }
 
-bool TreeSocket::Capab(const parameterlist &params)
+bool TreeSocket::Capab(const CommandBase::Params& params)
 {
        if (params.size() < 1)
        {
@@ -209,7 +258,23 @@ bool TreeSocket::Capab(const parameterlist &params)
                capab->OptModuleList.clear();
                capab->CapKeys.clear();
                if (params.size() > 1)
-                       proto_version = atoi(params[1].c_str());
+                       proto_version = ConvToNum<unsigned int>(params[1]);
+
+               if (proto_version < MinCompatProtocol)
+               {
+                       SendError("CAPAB negotiation failed: Server is using protocol version " + (proto_version ? ConvToStr(proto_version) : "1201 or older")
+                               + " which is too old to link with this server (version " + ConvToStr(ProtocolVersion)
+                               + (ProtocolVersion != MinCompatProtocol ? ", links with " + ConvToStr(MinCompatProtocol) + " and above)" : ")"));
+                       return false;
+               }
+
+               // Special case, may be removed in the future
+               if (proto_version == 1203 || proto_version == 1204)
+               {
+                       SendError("CAPAB negotiation failed: InspIRCd 2.1 beta is not supported");
+                       return false;
+               }
+
                SendCapabilities(2);
        }
        else if (params[0] == "END")
@@ -219,7 +284,7 @@ bool TreeSocket::Capab(const parameterlist &params)
                if ((this->capab->ModuleList != this->MyModules(VF_COMMON)) && (this->capab->ModuleList.length()))
                {
                        std::string diffIneed, diffUneed;
-                       ListDifference(this->capab->ModuleList, this->MyModules(VF_COMMON), proto_version > 1201 ? ' ' : ',', diffIneed, diffUneed);
+                       ListDifference(this->capab->ModuleList, this->MyModules(VF_COMMON), ', diffIneed, diffUneed);
                        if (diffIneed.length() || diffUneed.length())
                        {
                                reason = "Modules incorrectly matched on these servers.";
@@ -231,6 +296,7 @@ bool TreeSocket::Capab(const parameterlist &params)
                                return false;
                        }
                }
+
                if (this->capab->OptModuleList != this->MyModules(VF_OPTCOMMON) && this->capab->OptModuleList.length())
                {
                        std::string diffIneed, diffUneed;
@@ -246,7 +312,7 @@ bool TreeSocket::Capab(const parameterlist &params)
                                }
                                else
                                {
-                                       reason = "Optional modules incorrectly matched on these servers, and options::allowmismatch not set.";
+                                       reason = "Optional modules incorrectly matched on these servers and <options:allowmismatch> is not enabled.";
                                        if (diffIneed.length())
                                                reason += " Not loaded here:" + diffIneed;
                                        if (diffUneed.length())
@@ -257,24 +323,6 @@ bool TreeSocket::Capab(const parameterlist &params)
                        }
                }
 
-               if (this->capab->CapKeys.find("PROTOCOL") == this->capab->CapKeys.end())
-               {
-                       reason = "Protocol version not specified";
-               }
-               else
-               {
-                       proto_version = atoi(capab->CapKeys.find("PROTOCOL")->second.c_str());
-                       if (proto_version < MinCompatProtocol)
-                       {
-                               reason = "Server is using protocol version " + ConvToStr(proto_version) +
-                                       " which is too old to link with this server (version " + ConvToStr(ProtocolVersion)
-                                       + (ProtocolVersion != MinCompatProtocol ? ", links with " + ConvToStr(MinCompatProtocol) + " and above)" : ")");
-                       }
-               }
-
-               if(this->capab->CapKeys.find("PREFIX") != this->capab->CapKeys.end() && this->capab->CapKeys.find("PREFIX")->second != ServerInstance->Modes->BuildPrefixes())
-                       reason = "One or more of the prefixes on the remote server are invalid on this server.";
-
                if (!capab->ChanModes.empty())
                {
                        if (capab->ChanModes != BuildModeList(MODETYPE_CHANNEL))
@@ -291,10 +339,25 @@ bool TreeSocket::Capab(const parameterlist &params)
                                }
                        }
                }
-               else if (this->capab->CapKeys.find("CHANMODES") != this->capab->CapKeys.end())
+               else if (proto_version == 1202)
                {
-                       if (this->capab->CapKeys.find("CHANMODES")->second != ServerInstance->Modes->GiveModeList(MASK_CHANNEL))
-                               reason = "One or more of the channel modes on the remote server are invalid on this server.";
+                       if (this->capab->CapKeys.find("CHANMODES") != this->capab->CapKeys.end())
+                       {
+                               if (this->capab->CapKeys.find("CHANMODES")->second != ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL))
+                                       reason = "One or more of the channel modes on the remote server are invalid on this server.";
+                       }
+
+                       else if (this->capab->CapKeys.find("PREFIX") != this->capab->CapKeys.end())
+                       {
+                               if (this->capab->CapKeys.find("PREFIX")->second != ServerInstance->Modes->BuildPrefixes())
+                                       reason = "One or more of the prefixes on the remote server are invalid on this server.";
+                       }
+               }
+
+               if (!reason.empty())
+               {
+                       this->SendError("CAPAB negotiation failed: " + reason);
+                       return false;
                }
 
                if (!capab->UserModes.empty())
@@ -313,15 +376,34 @@ bool TreeSocket::Capab(const parameterlist &params)
                                }
                        }
                }
-               else if (this->capab->CapKeys.find("USERMODES") != this->capab->CapKeys.end())
+               else if (proto_version == 1202 && this->capab->CapKeys.find("USERMODES") != this->capab->CapKeys.end())
                {
-                       if (this->capab->CapKeys.find("USERMODES")->second != ServerInstance->Modes->GiveModeList(MASK_USER))
+                       if (this->capab->CapKeys.find("USERMODES")->second != ServerInstance->Modes->GiveModeList(MODETYPE_USER))
                                reason = "One or more of the user modes on the remote server are invalid on this server.";
                }
 
+               if (!reason.empty())
+               {
+                       this->SendError("CAPAB negotiation failed: " + reason);
+                       return false;
+               }
+
+               if (this->capab->CapKeys.find("CASEMAPPING") != this->capab->CapKeys.end())
+               {
+                       const std::string casemapping = this->capab->CapKeys.find("CASEMAPPING")->second;
+                       if (casemapping != ServerInstance->Config->CaseMapping)
+                       {
+                               reason = "The casemapping of the remote server differs to that of the local server."
+                                       " Local casemapping: " + ServerInstance->Config->CaseMapping +
+                                       " Remote casemapping: " + casemapping;
+                               this->SendError("CAPAB negotiation failed: " + reason);
+                               return false;
+                       }
+               }
+
                /* Challenge response, store their challenge for our password */
                std::map<std::string,std::string>::iterator n = this->capab->CapKeys.find("CHALLENGE");
-               if (Utils->ChallengeResponse && (n != this->capab->CapKeys.end()) && (ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256")))
+               if ((n != this->capab->CapKeys.end()) && (ServerInstance->Modules->FindService(SERVICE_DATA, "hash/sha256")))
                {
                        /* Challenge-response is on now */
                        this->SetTheirChallenge(n->second);
@@ -333,19 +415,13 @@ bool TreeSocket::Capab(const parameterlist &params)
                }
                else
                {
-                       /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */
+                       // They didn't specify a challenge or we don't have sha256, we use plaintext
                        if (this->LinkState == CONNECTING)
                        {
                                this->SendCapabilities(2);
                                this->WriteLine("SERVER "+ServerInstance->Config->ServerName+" "+capab->link->SendPass+" 0 "+ServerInstance->Config->GetSID()+" :"+ServerInstance->Config->ServerDesc);
                        }
                }
-
-               if (reason.length())
-               {
-                       this->SendError("CAPAB negotiation failed: "+reason);
-                       return false;
-               }
        }
        else if ((params[0] == "MODULES") && (params.size() == 2))
        {
@@ -355,7 +431,7 @@ bool TreeSocket::Capab(const parameterlist &params)
                }
                else
                {
-                       capab->ModuleList.push_back(proto_version > 1201 ? ' ' : ',');
+                       capab->ModuleList.push_back(');
                        capab->ModuleList.append(params[1]);
                }
        }
@@ -381,7 +457,7 @@ bool TreeSocket::Capab(const parameterlist &params)
        }
        else if ((params[0] == "CAPABILITIES") && (params.size() == 2))
        {
-               irc::tokenstream capabs(params[1]);
+               irc::spacesepstream capabs(params[1]);
                std::string item;
                while (capabs.GetToken(item))
                {
@@ -389,12 +465,11 @@ 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;
                        }
                }
        }
        return true;
 }
-
diff --git a/src/modules/m_spanningtree/commandbuilder.h b/src/modules/m_spanningtree/commandbuilder.h
new file mode 100644 (file)
index 0000000..4bbb60e
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 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 "utils.h"
+
+class TreeServer;
+
+class CmdBuilder
+{
+ protected:
+       std::string content;
+
+ public:
+       CmdBuilder(const char* cmd)
+               : content(1, ':')
+       {
+               content.append(ServerInstance->Config->GetSID());
+               push(cmd);
+       }
+
+       CmdBuilder(const std::string& src, const char* cmd)
+               : content(1, ':')
+       {
+               content.append(src);
+               push(cmd);
+       }
+
+       CmdBuilder(User* src, const char* cmd)
+               : content(1, ':')
+       {
+               content.append(src->uuid);
+               push(cmd);
+       }
+
+       CmdBuilder& push_raw(const std::string& s)
+       {
+               content.append(s);
+               return *this;
+       }
+
+       CmdBuilder& push_raw(const char* s)
+       {
+               content.append(s);
+               return *this;
+       }
+
+       CmdBuilder& push_raw(char c)
+       {
+               content.push_back(c);
+               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(' ');
+               content.append(s);
+               return *this;
+       }
+
+       CmdBuilder& push(const char* s)
+       {
+               content.push_back(' ');
+               content.append(s);
+               return *this;
+       }
+
+       CmdBuilder& push(char c)
+       {
+               content.push_back(' ');
+               content.push_back(c);
+               return *this;
+       }
+
+       template <typename T>
+       CmdBuilder& push_int(T i)
+       {
+               content.push_back(' ');
+               content.append(ConvToStr(i));
+               return *this;
+       }
+
+       CmdBuilder& push_last(const std::string& s)
+       {
+               content.push_back(' ');
+               content.push_back(':');
+               content.append(s);
+               return *this;
+       }
+
+       CmdBuilder& push_tags(const ClientProtocol::TagMap& tags)
+       {
+               if (!tags.empty())
+               {
+                       char separator = '@';
+                       std::string taglist;
+                       for (ClientProtocol::TagMap::const_iterator iter = tags.begin(); iter != tags.end(); ++iter)
+                       {
+                               taglist.push_back(separator);
+                               separator = ';';
+
+                               taglist.append(iter->first);
+                               if (!iter->second.value.empty())
+                               {
+                                       taglist.push_back('=');
+                                       taglist.append(iter->second.value);
+                               }
+                       }
+                       taglist.push_back(' ');
+                       content.insert(0, taglist);
+               }
+               return *this;
+       }
+
+       template<typename T>
+       CmdBuilder& insert(const T& cont)
+       {
+               for (typename T::const_iterator i = cont.begin(); i != cont.end(); ++i)
+                       push(*i);
+               return *this;
+       }
+
+       void push_back(const std::string& s) { push(s); }
+
+       const std::string& str() const { return content; }
+       operator const std::string&() const { return str(); }
+
+       void Broadcast() const
+       {
+               Utils->DoOneToMany(*this);
+       }
+
+       void Forward(TreeServer* omit) const
+       {
+               Utils->DoOneToAllButSender(*this, omit);
+       }
+
+       void Unicast(User* target) const
+       {
+               Utils->DoOneToOne(*this, target->server);
+       }
+};
index 3b5b499c11d8cf8d3f3c83e2a6c9060f88720e22..434528e4601b74840655969a4b9de8675b06a7c6 100644 (file)
  */
 
 
-#ifndef M_SPANNINGTREE_COMMANDS_H
-#define M_SPANNINGTREE_COMMANDS_H
+#pragma once
 
-#include "main.h"
+#include "servercommand.h"
+#include "commandbuilder.h"
+#include "remoteuser.h"
+#include "modules/away.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
  */
 class CommandRConnect : public Command
 {
-        SpanningTreeUtilities* Utils;  /* Utility class */
  public:
-        CommandRConnect (Module* Callback, SpanningTreeUtilities* Util);
-        CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-               RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+       CommandRConnect(Module* Creator);
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
 
 class CommandRSQuit : public Command
 {
-        SpanningTreeUtilities* Utils;  /* Utility class */
  public:
-        CommandRSQuit(Module* Callback, SpanningTreeUtilities* Util);
-        CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-               RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
-        void NoticeUser(User* user, const std::string &msg);
+       CommandRSQuit(Module* Creator);
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
 
-class CommandSVSJoin : public Command
+class CommandMap : public Command
 {
  public:
-       CommandSVSJoin(Module* Creator) : Command(Creator, "SVSJOIN", 2) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+       CommandMap(Module* Creator);
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
-class CommandSVSPart : public Command
+
+class CommandSVSJoin : public ServerCommand
 {
  public:
-       CommandSVSPart(Module* Creator) : Command(Creator, "SVSPART", 2) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+       CommandSVSJoin(Module* Creator) : ServerCommand(Creator, "SVSJOIN", 2) { }
+       CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
-class CommandSVSNick : public Command
+
+class CommandSVSPart : public ServerCommand
 {
  public:
-       CommandSVSNick(Module* Creator) : Command(Creator, "SVSNICK", 3) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+       CommandSVSPart(Module* Creator) : ServerCommand(Creator, "SVSPART", 2) { }
+       CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
-class CommandMetadata : public Command
+
+class CommandSVSNick : public ServerCommand
 {
  public:
-       CommandMetadata(Module* Creator) : Command(Creator, "METADATA", 2) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+       CommandSVSNick(Module* Creator) : ServerCommand(Creator, "SVSNICK", 3) { }
+       CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
-class CommandUID : public Command
+
+class CommandMetadata : public ServerCommand
 {
  public:
-       CommandUID(Module* Creator) : Command(Creator, "UID", 10) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+       CommandMetadata(Module* Creator) : ServerCommand(Creator, "METADATA", 2) { }
+       CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(User* user, const std::string& key, const std::string& val);
+               Builder(Channel* chan, const std::string& key, const std::string& val);
+               Builder(const std::string& key, const std::string& val);
+       };
 };
-class CommandOpertype : public Command
+
+class CommandUID : public ServerOnlyServerCommand<CommandUID>
 {
  public:
-       CommandOpertype(Module* Creator) : Command(Creator, "OPERTYPE", 1) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+       CommandUID(Module* Creator) : ServerOnlyServerCommand<CommandUID>(Creator, "UID", 10) { }
+       CmdResult HandleServer(TreeServer* server, CommandBase::Params& params);
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(User* user);
+       };
 };
-class CommandFJoin : public Command
+
+class CommandOpertype : public UserOnlyServerCommand<CommandOpertype>
 {
  public:
-       CommandFJoin(Module* Creator) : Command(Creator, "FJOIN", 3) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+       CommandOpertype(Module* Creator) : UserOnlyServerCommand<CommandOpertype>(Creator, "OPERTYPE", 1) { }
+       CmdResult HandleRemote(RemoteUser* user, Params& params);
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(User* user);
+       };
+};
+
+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.
         */
-       void RemoveStatus(User* source, parameterlist &params);
+       static void RemoveStatus(Channel* c);
+
+       /**
+        * Lowers the TS on the given channel: removes all modes, unsets all extensions,
+        * clears the topic and removes all pending invites.
+        * @param chan The target channel whose TS to lower
+        * @param TS The new TS to set
+        * @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, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist, FwdFJoinBuilder& fwdfjoin);
+ public:
+       CommandFJoin(Module* Creator) : ServerCommand(Creator, "FJOIN", 3) { }
+       CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE { 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
+{
+ public:
+       CommandFMode(Module* Creator) : ServerCommand(Creator, "FMODE", 3) { }
+       CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
 };
-class CommandFMode : public Command
+
+class CommandFTopic : public ServerCommand
 {
  public:
-       CommandFMode(Module* Creator) : Command(Creator, "FMODE", 3) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+       CommandFTopic(Module* Creator) : ServerCommand(Creator, "FTOPIC", 4, 5) { }
+       CmdResult Handle(User* user, Params& params) CXX11_OVERRIDE;
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(Channel* chan);
+               Builder(User* user, Channel* chan);
+       };
 };
-class CommandFTopic : public Command
+
+class CommandFHost : public UserOnlyServerCommand<CommandFHost>
+{
+ public:
+       CommandFHost(Module* Creator) : UserOnlyServerCommand<CommandFHost>(Creator, "FHOST", 1) { }
+       CmdResult HandleRemote(RemoteUser* user, Params& params);
+};
+
+class CommandFIdent : public UserOnlyServerCommand<CommandFIdent>
 {
  public:
-       CommandFTopic(Module* Creator) : Command(Creator, "FTOPIC", 4) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+       CommandFIdent(Module* Creator) : UserOnlyServerCommand<CommandFIdent>(Creator, "FIDENT", 1) { }
+       CmdResult HandleRemote(RemoteUser* user, Params& params);
 };
-class CommandFHost : public Command
+
+class CommandFName : public UserOnlyServerCommand<CommandFName>
 {
  public:
-       CommandFHost(Module* Creator) : Command(Creator, "FHOST", 1) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+       CommandFName(Module* Creator) : UserOnlyServerCommand<CommandFName>(Creator, "FNAME", 1) { }
+       CmdResult HandleRemote(RemoteUser* user, Params& params);
 };
-class CommandFIdent : public Command
+
+class CommandIJoin : public UserOnlyServerCommand<CommandIJoin>
 {
  public:
-       CommandFIdent(Module* Creator) : Command(Creator, "FIDENT", 1) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+       CommandIJoin(Module* Creator) : UserOnlyServerCommand<CommandIJoin>(Creator, "IJOIN", 2) { }
+       CmdResult HandleRemote(RemoteUser* user, Params& params);
 };
-class CommandFName : public Command
+
+class CommandResync : public ServerOnlyServerCommand<CommandResync>
 {
  public:
-       CommandFName(Module* Creator) : Command(Creator, "FNAME", 1) { flags_needed = FLAG_SERVERONLY; }
-       CmdResult Handle (const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+       CommandResync(Module* Creator) : ServerOnlyServerCommand<CommandResync>(Creator, "RESYNC", 1) { }
+       CmdResult HandleServer(TreeServer* server, Params& parameters);
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE { return ROUTE_LOCALONLY; }
+};
+
+class SpanningTree::CommandAway : public UserOnlyServerCommand<SpanningTree::CommandAway>
+{
+ private:
+       Away::EventProvider awayevprov;
+
+ public:
+       CommandAway(Module* Creator)
+               : UserOnlyServerCommand<SpanningTree::CommandAway>(Creator, "AWAY", 0, 2)
+               , awayevprov(Creator)
+       {
+       }
+       CmdResult HandleRemote(::RemoteUser* user, Params& parameters);
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(User* user);
+       };
+};
+
+class XLine;
+class CommandAddLine : public ServerCommand
+{
+ public:
+       CommandAddLine(Module* Creator) : ServerCommand(Creator, "ADDLINE", 6, 6) { }
+       CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(XLine* xline, User* user = ServerInstance->FakeClient);
+       };
+};
+
+class CommandDelLine : public ServerCommand
+{
+ public:
+       CommandDelLine(Module* Creator) : ServerCommand(Creator, "DELLINE", 2, 2) { }
+       CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+};
+
+class CommandEncap : public ServerCommand
+{
+ public:
+       CommandEncap(Module* Creator) : ServerCommand(Creator, "ENCAP", 2) { }
+       CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+};
+
+class CommandIdle : public UserOnlyServerCommand<CommandIdle>
+{
+ public:
+       CommandIdle(Module* Creator) : UserOnlyServerCommand<CommandIdle>(Creator, "IDLE", 1) { }
+       CmdResult HandleRemote(RemoteUser* user, Params& parameters);
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class SpanningTree::CommandNick : public UserOnlyServerCommand<SpanningTree::CommandNick>
+{
+ public:
+       CommandNick(Module* Creator) : UserOnlyServerCommand<SpanningTree::CommandNick>(Creator, "NICK", 2) { }
+       CmdResult HandleRemote(::RemoteUser* user, Params& parameters);
+};
+
+class SpanningTree::CommandPing : public ServerCommand
+{
+ public:
+       CommandPing(Module* Creator) : ServerCommand(Creator, "PING", 1) { }
+       CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class SpanningTree::CommandPong : public ServerOnlyServerCommand<SpanningTree::CommandPong>
+{
+ public:
+       CommandPong(Module* Creator) : ServerOnlyServerCommand<SpanningTree::CommandPong>(Creator, "PONG", 1) { }
+       CmdResult HandleServer(TreeServer* server, Params& parameters);
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE { 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, Params& parameters) CXX11_OVERRIDE;
+};
+
+class SpanningTree::CommandServer : public ServerOnlyServerCommand<SpanningTree::CommandServer>
+{
+       static void HandleExtra(TreeServer* newserver, Params& params);
+
+ public:
+       CommandServer(Module* Creator) : ServerOnlyServerCommand<SpanningTree::CommandServer>(Creator, "SERVER", 3) { }
+       CmdResult HandleServer(TreeServer* server, Params& 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);
+       };
+};
+
+class CommandSQuit : public ServerOnlyServerCommand<CommandSQuit>
+{
+ public:
+       CommandSQuit(Module* Creator) : ServerOnlyServerCommand<CommandSQuit>(Creator, "SQUIT", 2) { }
+       CmdResult HandleServer(TreeServer* server, Params& parameters);
+};
+
+class CommandSNONotice : public ServerCommand
+{
+ public:
+       CommandSNONotice(Module* Creator) : ServerCommand(Creator, "SNONOTICE", 2) { }
+       CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE;
+};
+
+class CommandEndBurst : public ServerOnlyServerCommand<CommandEndBurst>
+{
+ public:
+       CommandEndBurst(Module* Creator) : ServerOnlyServerCommand<CommandEndBurst>(Creator, "ENDBURST") { }
+       CmdResult HandleServer(TreeServer* server, Params& parameters);
+};
+
+class CommandSInfo : public ServerOnlyServerCommand<CommandSInfo>
+{
+ public:
+       CommandSInfo(Module* Creator) : ServerOnlyServerCommand<CommandSInfo>(Creator, "SINFO", 2) { }
+       CmdResult HandleServer(TreeServer* server, Params& parameters);
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(TreeServer* server, const char* type, const std::string& value);
+       };
+};
+
+class CommandNum : public ServerOnlyServerCommand<CommandNum>
+{
+ public:
+       CommandNum(Module* Creator) : ServerOnlyServerCommand<CommandNum>(Creator, "NUM", 3) { }
+       CmdResult HandleServer(TreeServer* server, Params& parameters);
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+       class Builder : public CmdBuilder
+       {
+        public:
+               Builder(SpanningTree::RemoteUser* target, const Numeric::Numeric& numeric);
+       };
 };
 
 class SpanningTreeCommands
 {
  public:
-       CommandRConnect rconnect;
-       CommandRSQuit rsquit;
        CommandSVSJoin svsjoin;
        CommandSVSPart svspart;
        CommandSVSNick svsnick;
@@ -144,12 +408,27 @@ class SpanningTreeCommands
        CommandUID uid;
        CommandOpertype opertype;
        CommandFJoin fjoin;
+       CommandIJoin ijoin;
+       CommandResync resync;
        CommandFMode fmode;
        CommandFTopic ftopic;
        CommandFHost fhost;
        CommandFIdent fident;
        CommandFName fname;
+       SpanningTree::CommandAway away;
+       CommandAddLine addline;
+       CommandDelLine delline;
+       CommandEncap encap;
+       CommandIdle idle;
+       SpanningTree::CommandNick nick;
+       SpanningTree::CommandPing ping;
+       SpanningTree::CommandPong pong;
+       CommandSave save;
+       SpanningTree::CommandServer server;
+       CommandSQuit squit;
+       CommandSNONotice snonotice;
+       CommandEndBurst endburst;
+       CommandSInfo sinfo;
+       CommandNum num;
        SpanningTreeCommands(ModuleSpanningTree* module);
 };
-
-#endif
index ec0cdb03661d3e99a45d15a6487828d0e59bded1..aacae5deab8a65fc9781c056ee723c393b0ff303 100644 (file)
 #include "inspircd.h"
 #include "main.h"
 #include "treesocket.h"
+#include "treeserver.h"
 
-static const char* const forge_common_1201[] = {
-       "m_allowinvite.so",
-       "m_alltime.so",
-       "m_auditorium.so",
-       "m_banexception.so",
-       "m_blockcaps.so",
-       "m_blockcolor.so",
-       "m_botmode.so",
-       "m_censor.so",
-       "m_chanfilter.so",
-       "m_chanhistory.so",
-       "m_channelban.so",
-       "m_chanprotect.so",
-       "m_chghost.so",
-       "m_chgname.so",
-       "m_commonchans.so",
-       "m_customtitle.so",
-       "m_deaf.so",
-       "m_delayjoin.so",
-       "m_delaymsg.so",
-       "m_exemptchanops.so",
-       "m_gecosban.so",
-       "m_globops.so",
-       "m_helpop.so",
-       "m_hidechans.so",
-       "m_hideoper.so",
-       "m_invisible.so",
-       "m_inviteexception.so",
-       "m_joinflood.so",
-       "m_kicknorejoin.so",
-       "m_knock.so",
-       "m_messageflood.so",
-       "m_muteban.so",
-       "m_nickflood.so",
-       "m_nicklock.so",
-       "m_noctcp.so",
-       "m_nokicks.so",
-       "m_nonicks.so",
-       "m_nonotice.so",
-       "m_nopartmsg.so",
-       "m_ojoin.so",
-       "m_operprefix.so",
-       "m_permchannels.so",
-       "m_redirect.so",
-       "m_regex_glob.so",
-       "m_regex_pcre.so",
-       "m_regex_posix.so",
-       "m_regex_tre.so",
-       "m_remove.so",
-       "m_sajoin.so",
-       "m_sakick.so",
-       "m_sanick.so",
-       "m_sapart.so",
-       "m_saquit.so",
-       "m_serverban.so",
-       "m_services_account.so",
-       "m_servprotect.so",
-       "m_setident.so",
-       "m_showwhois.so",
-       "m_silence.so",
-       "m_sslmodes.so",
-       "m_stripcolor.so",
-       "m_swhois.so",
-       "m_uninvite.so",
-       "m_watch.so"
-};
-
-static std::string wide_newline("\r\n");
 static std::string newline("\n");
 
-void TreeSocket::CompatAddModules(std::vector<std::string>& modlist)
+void TreeSocket::WriteLineNoCompat(const std::string& line)
 {
-       if (proto_version < 1202)
-       {
-               // you MUST have chgident loaded in order to be able to translate FIDENT
-               modlist.push_back("m_chgident.so");
-               for(int i=0; i * sizeof(char*) < sizeof(forge_common_1201); i++)
-               {
-                       if (ServerInstance->Modules->Find(forge_common_1201[i]))
-                               modlist.push_back(forge_common_1201[i]);
-               }
-               // module was merged
-               if (ServerInstance->Modules->Find("m_operchans.so"))
-               {
-                       modlist.push_back("m_operchans.so");
-                       modlist.push_back("m_operinvex.so");
-               }
-       }
+       ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), line.c_str());
+       this->WriteData(line);
+       this->WriteData(newline);
 }
 
-void TreeSocket::WriteLine(std::string line)
+void TreeSocket::WriteLine(const std::string& original_line)
 {
        if (LinkState == CONNECTED)
        {
-               if (line[0] != ':')
-               {
-                       ServerInstance->Logs->Log("m_spanningtree", DEFAULT, "Sending line without server prefix!");
-                       line = ":" + ServerInstance->Config->GetSID() + " " + line;
-               }
                if (proto_version != ProtocolVersion)
                {
+                       std::string line = original_line;
                        std::string::size_type a = line.find(' ');
+                       if (line[0] == '@')
+                       {
+                               // The line contains tags which the 1202 protocol can't handle.
+                               line.erase(0, a + 1);
+                               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 < 1202 && command == "FIDENT")
-                       {
-                               ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Rewriting FIDENT for 1201-protocol server");
-                               line = ":" + ServerInstance->Config->GetSID() + " CHGIDENT " +  line.substr(1,a-1) + line.substr(b);
-                       }
-                       else if (proto_version < 1202 && command == "SAVE")
-                       {
-                               ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Rewriting SAVE for 1201-protocol server");
-                               std::string::size_type c = line.find(' ', b + 1);
-                               std::string uid = line.substr(b, c - b);
-                               line = ":" + ServerInstance->Config->GetSID() + " SVSNICK" + uid + line.substr(b);
-                       }
-                       else if (proto_version < 1202 && command == "AWAY")
+                       if (proto_version < 1205)
                        {
-                               if (b != std::string::npos)
+                               if (command == "IJOIN")
                                {
-                                       ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Stripping AWAY timestamp for 1201-protocol server");
+                                       // Convert
+                                       // :<uid> IJOIN <chan> <membid> [<ts> [<flags>]]
+                                       // to
+                                       // :<sid> FJOIN <chan> <ts> + [<flags>],<uuid>
                                        std::string::size_type c = line.find(' ', b + 1);
-                                       line.erase(b,c-b);
+                                       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, b+1, c-b-1);
+                                               Channel* chan = ServerInstance->FindChan(channame);
+                                               if (!chan)
+                                                       return;
+
+                                               line.push_back(' ');
+                                               line.append(ConvToStr(chan->age));
+                                               line.append(" + ,");
+                                       }
+                                       else
+                                       {
+                                               d = line.find(' ', c + 1);
+                                               if (d == std::string::npos)
+                                               {
+                                                       // TS present, no modes
+                                                       // :22DAAAAAC IJOIN #chan 12345
+                                                       line.append(" + ,");
+                                               }
+                                               else
+                                               {
+                                                       // Both TS and modes are present
+                                                       // :22DAAAAAC IJOIN #chan 12345 ov
+                                                       std::string::size_type e = line.find(' ', d + 1);
+                                                       if (e != std::string::npos)
+                                                               line.erase(e);
+
+                                                       line.insert(d, " +");
+                                                       line.push_back(',');
+                                               }
+                                       }
+
+                                       // Move the uuid to the end and replace the I with an F
+                                       line.append(line.substr(1, 9));
+                                       line.erase(4, 6);
+                                       line[5] = 'F';
                                }
-                       }
-                       else if (proto_version < 1202 && command == "ENCAP")
-                       {
-                               // :src ENCAP target command [args...]
-                               //     A     B      C       D
-                               // Therefore B and C cannot be npos in a valid command
-                               if (b == std::string::npos)
-                                       return;
-                               std::string::size_type c = line.find(' ', b + 1);
-                               if (c == std::string::npos)
+                               else if (command == "RESYNC")
                                        return;
-                               std::string::size_type d = line.find(' ', c + 1);
-                               std::string subcmd = line.substr(c + 1, d - c - 1);
+                               else if (command == "METADATA")
+                               {
+                                       // Drop TS for channel METADATA, translate METADATA operquit into an OPERQUIT command
+                                       // :sid METADATA #target TS extname ...
+                                       //     A        B       C  D
+                                       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;
 
-                               if (subcmd == "CHGIDENT" && d != std::string::npos)
+                                       if (line[b + 1] == '#')
+                                       {
+                                               // We're sending channel metadata
+                                               line.erase(c, d-c);
+                                       }
+                                       else if (!line.compare(c, d-c, " operquit", 9))
+                                       {
+                                               // ":22D METADATA 22DAAAAAX operquit :message" -> ":22DAAAAAX OPERQUIT :message"
+                                               line = ":" + line.substr(b+1, c-b) + "OPERQUIT" + line.substr(d);
+                                       }
+                               }
+                               else if (command == "FTOPIC")
                                {
+                                       // Drop channel TS for FTOPIC
+                                       // :sid FTOPIC #target TS TopicTS setter :newtopic
+                                       //     A      B       C  D       E      F
+                                       // :uid FTOPIC #target TS TopicTS :newtopic
+                                       //     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 (e == std::string::npos)
-                                               return; // not valid
-                                       std::string target = line.substr(d + 1, e - d - 1);
+                                       if (line[e+1] == ':')
+                                       {
+                                               line.erase(c, e-c);
+                                               line.erase(a+1, 1);
+                                       }
+                                       else
+                                               line.erase(c, d-c);
+                               }
+                               else if ((command == "PING") || (command == "PONG"))
+                               {
+                                       // :22D PING 20D
+                                       if (line.length() < 13)
+                                               return;
 
-                                       ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Forging acceptance of CHGIDENT from 1201-protocol server");
-                                       recvq.insert(0, ":" + target + " FIDENT " + line.substr(e) + "\n");
+                                       // Insert the source SID (and a space) between the command and the first parameter
+                                       line.insert(10, line.substr(1, 4));
+                               }
+                               else if (command == "OPERTYPE")
+                               {
+                                       std::string::size_type colon = line.find(':', b);
+                                       if (colon != std::string::npos)
+                                       {
+                                               for (std::string::iterator i = line.begin()+colon; i != line.end(); ++i)
+                                               {
+                                                       if (*i == ' ')
+                                                               *i = '_';
+                                               }
+                                               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;
 
-                               Command* thiscmd = ServerInstance->Parser->GetHandler(subcmd);
-                               if (thiscmd && subcmd != "WHOISNOTICE")
+                                       // 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")
                                {
-                                       Version ver = thiscmd->creator->GetVersion();
-                                       if (ver.Flags & VF_OPTCOMMON)
+                                       // :001 SERVER inspircd.test 002 [<anything> ...] :description
+                                       //     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)
                                        {
-                                               ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Removing ENCAP on '%s' for 1201-protocol server",
-                                                       subcmd.c_str());
-                                               line.erase(a, c-a);
+                                               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);
+                               }
+                               else if (command == "TAGMSG")
+                               {
+                                       // Drop IRCv3 tag messages as v2 has no message tag support.
+                                       return;
+                               }
                        }
+                       WriteLineNoCompat(line);
+                       return;
                }
        }
 
-       ServerInstance->Logs->Log("m_spanningtree", RAWIO, "S[%d] O %s", this->GetFd(), line.c_str());
-       this->WriteData(line);
-       if (proto_version < 1202)
-               this->WriteData(wide_newline);
-       else
-               this->WriteData(newline);
+       WriteLineNoCompat(original_line);
+}
+
+namespace
+{
+       bool InsertCurrentChannelTS(CommandBase::Params& params, unsigned int chanindex = 0, unsigned int pos = 1)
+       {
+               Channel* chan = ServerInstance->FindChan(params[chanindex]);
+               if (!chan)
+                       return false;
+
+               // Insert the current TS of the channel after the pos-th parameter
+               params.insert(params.begin()+pos, ConvToStr(chan->age));
+               return true;
+       }
+}
+
+bool TreeSocket::PreProcessOldProtocolMessage(User*& who, std::string& cmd, CommandBase::Params& params)
+{
+       if ((cmd == "METADATA") && (params.size() >= 3) && (params[0][0] == '#'))
+       {
+               // :20D METADATA #channel extname :extdata
+               return InsertCurrentChannelTS(params);
+       }
+       else if ((cmd == "FTOPIC") && (params.size() >= 4))
+       {
+               // :20D FTOPIC #channel 100 Attila :topic text
+               return InsertCurrentChannelTS(params);
+       }
+       else if ((cmd == "PING") || (cmd == "PONG"))
+       {
+               if (params.size() == 1)
+               {
+                       // If it's a PING with 1 parameter, reply with a PONG now, if it's a PONG with 1 parameter (weird), do nothing
+                       if (cmd[1] == 'I')
+                               this->WriteData(":" + ServerInstance->Config->GetSID() + " PONG " + params[0] + newline);
+
+                       // Don't process this message further
+                       return false;
+               }
+
+               // :20D PING 20D 22D
+               // :20D PONG 20D 22D
+               // Drop the first parameter
+               params.erase(params.begin());
+
+               // If the target is a server name, translate it to a SID
+               if (!InspIRCd::IsSID(params[0]))
+               {
+                       TreeServer* server = Utils->FindServer(params[0]);
+                       if (!server)
+                       {
+                               // We've no idea what this is, log and stop processing
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Received a " + cmd + " with an unknown target: \"" + params[0] + "\", command dropped");
+                               return false;
+                       }
+
+                       params[0] = server->GetID();
+               }
+       }
+       else if ((cmd == "GLINE") || (cmd == "KLINE") || (cmd == "ELINE") || (cmd == "ZLINE") || (cmd == "QLINE"))
+       {
+               // Fix undocumented protocol usage: translate GLINE, ZLINE, etc. into ADDLINE or DELLINE
+               if ((params.size() != 1) && (params.size() != 3))
+                       return false;
+
+               CommandBase::Params p;
+               p.push_back(cmd.substr(0, 1));
+               p.push_back(params[0]);
+
+               if (params.size() == 3)
+               {
+                       cmd = "ADDLINE";
+                       p.push_back(who->nick);
+                       p.push_back(ConvToStr(ServerInstance->Time()));
+                       p.push_back(ConvToStr(InspIRCd::Duration(params[1])));
+                       p.push_back(params[2]);
+               }
+               else
+                       cmd = "DELLINE";
+
+               params.swap(p);
+       }
+       else if (cmd == "SVSMODE")
+       {
+               cmd = "MODE";
+       }
+       else if (cmd == "OPERQUIT")
+       {
+               // Translate OPERQUIT into METADATA
+               if (params.empty())
+                       return false;
+
+               cmd = "METADATA";
+               params.insert(params.begin(), who->uuid);
+               params.insert(params.begin()+1, "operquit");
+               who = MyRoot->ServerUser;
+       }
+       else if ((cmd == "TOPIC") && (params.size() >= 2))
+       {
+               // :20DAAAAAC TOPIC #chan :new topic
+               cmd = "FTOPIC";
+               if (!InsertCurrentChannelTS(params))
+                       return false;
+
+               params.insert(params.begin()+2, ConvToStr(ServerInstance->Time()));
+       }
+       else if (cmd == "MODENOTICE")
+       {
+               // 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";
+       }
+       else if (cmd == "RULES")
+       {
+               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 or any of its parents are bursting, then new servers it
+               // introduces are not bursting.
+               bool bursting = false;
+               for (TreeServer* server = TreeServer::Get(who); server; server = server->GetParent())
+               {
+                       if (server->IsBursting())
+                       {
+                               bursting = true;
+                               break;
+                       }
+               }
+
+               if (!bursting)
+                       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 == "SVSSILENCE")
+       {
+               // SVSSILENCE 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.GetMiddle(srcstr);
+               srcstr.erase(0, 1);
+
+               std::string token;
+               ts.GetMiddle(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 = ConvToNum<unsigned int>(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.GetMiddle(token);
+
+                       // Rest of the tokens are the numeric parameters, add them to NUM
+                       while (ts.GetTrailing(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.GetMiddle(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.GetTrailing(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
 }
index 540ca50792dcf25a4e822fd4dd6182731f0771ed..e376147fdb80a883905ba4affa80736c7ab783f3 100644 (file)
 #include "inspircd.h"
 #include "xline.h"
 
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
+#include "commands.h"
 
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-
-bool TreeSocket::DelLine(const std::string &prefix, parameterlist &params)
+CmdResult CommandDelLine::Handle(User* user, Params& params)
 {
-       if (params.size() < 2)
-               return true;
-
-       std::string setter = "<unknown>";
-
-       User* user = ServerInstance->FindNick(prefix);
-       if (user)
-               setter = user->nick;
-       else
-       {
-               TreeServer* t = Utils->FindServer(prefix);
-               if (t)
-                       setter = t->GetName();
-       }
+       const std::string& setter = user->nick;
+       std::string reason;
 
-
-       /* NOTE: No check needed on 'user', this function safely handles NULL */
-       if (ServerInstance->XLines->DelLine(params[1].c_str(), params[0], user))
+       // XLineManager::DelLine() returns true if the xline existed, false if it didn't
+       if (ServerInstance->XLines->DelLine(params[1].c_str(), params[0], reason, user))
        {
-               ServerInstance->SNO->WriteToSnoMask('X',"%s removed %s%s on %s", setter.c_str(),
-                               params[0].c_str(), params[0].length() == 1 ? "-line" : "", params[1].c_str());
-               Utils->DoOneToAllButSender(prefix,"DELLINE", params, prefix);
+               ServerInstance->SNO->WriteToSnoMask('X', "%s removed %s%s on %s: %s", setter.c_str(),
+                               params[0].c_str(), params[0].length() == 1 ? "-line" : "", params[1].c_str(), reason.c_str());
+               return CMD_SUCCESS;
        }
-       return true;
+       return CMD_FAILURE;
 }
-
index dabfc086bce5542b138d4638b074b8edbfd0acfe..4bc321065dde862a520a67d06ccb9c5943c50498 100644 (file)
 
 
 #include "inspircd.h"
-#include "xline.h"
 
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
+#include "commands.h"
+#include "main.h"
 
 /** ENCAP */
-void TreeSocket::Encap(User* who, parameterlist &params)
+CmdResult CommandEncap::Handle(User* user, Params& params)
 {
-       if (params.size() > 1)
+       if (ServerInstance->Config->GetSID() == params[0] || InspIRCd::Match(ServerInstance->Config->ServerName, params[0]))
        {
-               if (ServerInstance->Config->GetSID() == params[0] || InspIRCd::Match(ServerInstance->Config->ServerName, params[0]))
-               {
-                       parameterlist plist(params.begin() + 2, params.end());
-                       ServerInstance->Parser->CallHandler(params[1], plist, who);
-                       // discard return value, ENCAP shall succeed even if the command does not exist
-               }
-               
-               params[params.size() - 1] = ":" + params[params.size() - 1];
+               CommandBase::Params plist(params.begin() + 2, params.end());
 
-               if (params[0].find_first_of("*?") != std::string::npos)
+               // XXX: Workaround for SVS* commands provided by spanningtree not being registered in the core
+               if ((params[1] == "SVSNICK") || (params[1] == "SVSJOIN") || (params[1] == "SVSPART"))
                {
-                       Utils->DoOneToAllButSender(who->uuid, "ENCAP", params, who->server);
+                       ServerCommand* const scmd = Utils->Creator->CmdManager.GetHandler(params[1]);
+                       if (scmd)
+                               scmd->Handle(user, plist);
+                       return CMD_SUCCESS;
                }
-               else
-                       Utils->DoOneToOne(who->uuid, "ENCAP", params, params[0]);
+
+               Command* cmd = NULL;
+               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))
+                       return CMD_FAILURE;
        }
+       return CMD_SUCCESS;
 }
 
+RouteDescriptor CommandEncap::GetRouting(User* user, const Params& params)
+{
+       if (params[0].find_first_of("*?") != std::string::npos)
+               return ROUTE_BROADCAST;
+       return ROUTE_UNICAST(params[0]);
+}
index 4ec6e1dbb0f3d502209775cbde367ce325455ba3..a6c52e41b66af85c8eb9d392d38cfd0a5a686624 100644 (file)
 #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(const std::vector<std::string>& params, User *srcuser)
+CmdResult CommandFJoin::Handle(User* srcuser, Params& params)
 {
-       SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
-       /* 1.1 FJOIN works as follows:
+       /* 1.1+ FJOIN works as follows:
         *
         * Each FJOIN is sent along with a timestamp, and the side with the lowest
         * timestamp 'wins'. From this point on we will refer to this side as the
@@ -54,204 +69,277 @@ CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *src
         * The winning side on the other hand will ignore all user modes from the
         * 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> :[<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 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.
+        *
         */
 
-       irc::modestacker modestack(true);                       /* Modes to apply from the users in the user list */
-       User* who = NULL;                                               /* User we are currently checking */
-       std::string channel = params[0];                                /* Channel name, as a string */
-       time_t TS = atoi(params[1].c_str());                            /* Timestamp given to us for remote side */
-       irc::tokenstream users((params.size() > 3) ? params[params.size() - 1] : "");   /* users from the user list */
-       bool apply_other_sides_modes = true;                            /* True if we are accepting the other side's modes */
-       Channel* chan = ServerInstance->FindChan(channel);              /* The channel we're sending joins to */
-       bool created = !chan;                                           /* True if the channel doesnt exist here yet */
-       std::string item;                                               /* One item in the list of nicks */
-
-       TreeServer* src_server = Utils->FindServer(srcuser->server);
-       TreeSocket* src_socket = src_server->GetRoute()->GetSocket();
+       time_t TS = ServerCommand::ExtractTS(params[1]);
 
-       if (!TS)
-       {
-               ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
-               ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", srcuser->server.c_str());
-               return CMD_INVALID;
-       }
+       const std::string& channel = params[0];
+       Channel* chan = ServerInstance->FindChan(channel);
+       bool apply_other_sides_modes = true;
+       TreeServer* const sourceserver = TreeServer::Get(srcuser);
 
-       if (created)
+       if (!chan)
        {
                chan = new Channel(channel, TS);
-               ServerInstance->SNO->WriteToSnoMask('d', "Creation FJOIN received for %s, timestamp: %lu", chan->name.c_str(), (unsigned long)TS);
        }
        else
        {
                time_t ourTS = chan->age;
-
                if (TS != ourTS)
-                       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)
                {
-                       ServerInstance->SNO->WriteToSnoMask('d', "NOT Applying modes from other side");
-                       apply_other_sides_modes = false;
-               }
-               else if (ourTS > TS)
-               {
-                       /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
-                       ServerInstance->SNO->WriteToSnoMask('d', "Removing our modes, accepting remote");
-                       parameterlist param_list;
-                       if (Utils->AnnounceTSChange)
-                               chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), channel.c_str(), (unsigned long) ourTS, (unsigned long) TS);
-                       // while the name is equal in case-insensitive compare, it might differ in case; use the remote version
-                       chan->name = channel;
-                       chan->age = TS;
-                       chan->ClearInvites();
-                       param_list.push_back(channel);
-                       this->RemoveStatus(ServerInstance->FakeClient, param_list);
-
-                       // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
-                       // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
-                       // deleted later) as soon as the permchan mode is removed from them.
-                       if (ServerInstance->FindChan(channel) == NULL)
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "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)
                        {
-                               chan = new Channel(channel, 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)
+                       {
+                               // Our TS is greater than theirs, remove all modes, extensions, etc. from the channel
+                               LowerTS(chan, TS, channel);
+
+                               // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
+                               // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
+                               // deleted later) as soon as the permchan mode is removed from them.
+                               if (ServerInstance->FindChan(channel) == NULL)
+                               {
+                                       chan = new Channel(channel, TS);
+                               }
                        }
                }
-               // The silent case here is ourTS == TS, we don't need to remove modes here, just to merge them later on.
        }
 
-       /* First up, apply their 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;
+               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();
+       }
 
-                       std::string modeparam;
-                       if ((paramit != lastparamit) && (mh->GetNumParams(true)))
-                       {
-                               modeparam = *paramit;
-                               ++paramit;
-                       }
+       // 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);
 
-                       stack.Push(*i, modeparam);
-               }
+       // Process every member in the message
+       irc::spacesepstream users(params.back());
+       std::string item;
+       Modes::ChangeList* modechangelistptr = (apply_other_sides_modes ? &modechangelist : NULL);
+       while (users.GetToken(item))
+       {
+               ProcessModeUUIDPair(item, sourceserver, chan, modechangelistptr, fwdfjoin);
+       }
 
-               std::vector<std::string> modelist;
+       fwdfjoin.finalize();
+       fwdfjoin.Forward(sourceserver->GetRoute());
 
-               // Mode parser needs to know what channel to act on.
-               modelist.push_back(params[0]);
+       // Set prefix modes on their users if we lost the FJOIN or had equal TS
+       if (apply_other_sides_modes)
+               ServerInstance->Modes->Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY);
 
-               while (stack.GetStackedLine(modelist))
-               {
-                       ServerInstance->Modes->Process(modelist, srcuser, true);
-                       modelist.erase(modelist.begin() + 1, modelist.end());
-               }
+       return CMD_SUCCESS;
+}
+
+void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist, FwdFJoinBuilder& fwdfjoin)
+{
+       std::string::size_type comma = item.find(',');
 
-               ServerInstance->Modes->Process(modelist, srcuser, true);
+       // Comma not required anymore if the user has no modes
+       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)
+       {
+               // Probably KILLed, ignore
+               return;
        }
 
-       /* Now, process every 'modes,nick' pair */
-       while (users.GetToken(item))
+       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)
        {
-               const char* usr = item.c_str();
-               if (usr && *usr)
+               return;
+       }
+
+       std::string::const_iterator modeendit = item.begin(); // End of the "ov" mode string
+       /* Check if the user received at least one mode */
+       if ((modechangelist) && (comma != std::string::npos))
+       {
+               modeendit += comma;
+               /* Iterate through the modes and see if they are valid here, if so, apply */
+               for (std::string::const_iterator i = item.begin(); i != modeendit; ++i)
                {
-                       const char* unparsedmodes = usr;
-                       std::string modes;
+                       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 */
+                       modechangelist->push_add(mh, who->nick);
+               }
+       }
 
-                       /* Iterate through all modes for this user and check they are valid. */
-                       while ((*unparsedmodes) && (*unparsedmodes != ','))
-                       {
-                               ModeHandler *mh = ServerInstance->Modes->FindMode(*unparsedmodes, MODETYPE_CHANNEL);
-                               if (!mh)
-                               {
-                                       ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Unrecognised mode %c, dropping link", *unparsedmodes);
-                                       return CMD_INVALID;
-                               }
+       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;
+       }
 
-                               modes += *unparsedmodes;
-                               usr++;
-                               unparsedmodes++;
-                       }
+       // 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;
 
-                       /* Advance past the comma, to the nick */
-                       usr++;
+       // Add member to fwdfjoin with prefix modes
+       fwdfjoin.add(memb, item.begin(), modeendit);
+}
 
-                       /* Check the user actually exists */
-                       who = ServerInstance->FindUUID(usr);
-                       if (who)
-                       {
-                               /* Check that the user's 'direction' is correct */
-                               TreeServer* route_back_again = Utils->BestRouteTo(who->server);
-                               if ((!route_back_again) || (route_back_again->GetSocket() != src_socket))
-                                       continue;
+void CommandFJoin::RemoveStatus(Channel* c)
+{
+       Modes::ChangeList changelist;
 
-                               /* Add any modes this user had to the mode stack */
-                               for (std::string::iterator x = modes.begin(); x != modes.end(); ++x)
-                                       modestack.Push(*x, who->nick);
+       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;
 
-                               Channel::JoinUser(who, channel.c_str(), true, "", src_server->bursting, TS);
-                       }
-                       else
-                       {
-                               ServerInstance->Logs->Log("m_spanningtree",SPARSE, "Ignored nonexistent user %s in fjoin to %s (probably quit?)", usr, channel.c_str());
-                               continue;
-                       }
-               }
+               // Add the removal of this mode to the changelist. This handles all kinds of modes, including prefix modes.
+               mh->RemoveMode(c, changelist);
        }
 
-       /* Flush mode stacker if we lost the FJOIN or had equal TS */
-       if (apply_other_sides_modes)
-       {
-               parameterlist stackresult;
-               stackresult.push_back(channel);
+       ServerInstance->Modes->Process(ServerInstance->FakeClient, c, NULL, changelist, ModeParser::MODE_LOCALONLY);
+}
 
-               while (modestack.GetStackedLine(stackresult))
-               {
-                       ServerInstance->SendMode(stackresult, srcuser);
-                       stackresult.erase(stackresult.begin() + 1, stackresult.end());
-               }
-       }
-       return CMD_SUCCESS;
+void CommandFJoin::LowerTS(Channel* chan, time_t TS, const std::string& newname)
+{
+       if (Utils->AnnounceTSChange)
+               chan->WriteNotice(InspIRCd::Format("Creation time of %s changed from %s to %s", newname.c_str(),
+                       InspIRCd::TimeString(chan->age).c_str(), InspIRCd::TimeString(TS).c_str()));
+
+       // While the name is equal in case-insensitive compare, it might differ in case; use the remote version
+       chan->name = newname;
+       chan->age = TS;
+
+       // Clear all modes
+       CommandFJoin::RemoveStatus(chan);
+
+       // Unset all extensions
+       chan->FreeAllExtItems();
+
+       // Clear the topic
+       chan->SetTopic(ServerInstance->FakeClient, std::string(), 0);
+       chan->setby.clear();
 }
 
-void CommandFJoin::RemoveStatus(User* srcuser, parameterlist &params)
+CommandFJoin::Builder::Builder(Channel* chan, TreeServer* source)
+       : CmdBuilder(source->GetID(), "FJOIN")
 {
-       if (params.size() < 1)
-               return;
+       push(chan->name).push_int(chan->age).push_raw(" +");
+       pos = str().size();
+       push_raw(chan->ChanModes(true)).push_raw(" :");
+}
 
-       Channel* c = ServerInstance->FindChan(params[0]);
+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(' ');
+}
 
-       if (c)
-       {
-               irc::modestacker stack(false);
-               parameterlist stackresult;
-               stackresult.push_back(c->name);
+bool CommandFJoin::Builder::has_room(std::string::size_type nummodes) const
+{
+       return ((str().size() + nummodes + UIDGenerator::UUID_LENGTH + 2 + membid_max_digits + 1) <= maxline);
+}
 
-               for (char modeletter = 'A'; modeletter <= 'z'; ++modeletter)
-               {
-                       ModeHandler* mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
-
-                       /* 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
-                        */
-                       if (mh)
-                               mh->RemoveMode(c, &stack);
-               }
+void CommandFJoin::Builder::clear()
+{
+       content.erase(pos);
+       push_raw(" :");
+}
 
-               while (stack.GetStackedLine(stackresult))
-               {
-                       ServerInstance->SendMode(stackresult, srcuser);
-                       stackresult.erase(stackresult.begin() + 1, stackresult.end());
-               }
-       }
+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)))
+       {
+               finalize();
+               Forward(sourceserver);
+               clear();
+       }
+       // Add the member and their modes exactly as they sent them
+       CommandFJoin::Builder::add(memb, mbegin, mend);
+}
index c1e452db627bd28d0a647609536eb75d396c8248..a15b5ddc219b0b4c96d40148eba494b81bb8c0a1 100644 (file)
 #include "inspircd.h"
 #include "commands.h"
 
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
-/** FMODE command - server mode with timestamp checks */
-CmdResult CommandFMode::Handle(const std::vector<std::string>& params, User *who)
+/** FMODE command - channel mode change with timestamp checks */
+CmdResult CommandFMode::Handle(User* who, Params& params)
 {
-       std::string sourceserv = who->server;
-
-       std::vector<std::string> modelist;
-       time_t TS = 0;
-       for (unsigned int q = 0; (q < params.size()) && (q < 64); q++)
-       {
-               if (q == 1)
-               {
-                       /* The timestamp is in this position.
-                        * We don't want to pass that up to the
-                        * server->client protocol!
-                        */
-                       TS = atoi(params[q].c_str());
-               }
-               else
-               {
-                       /* Everything else is fine to append to the modelist */
-                       modelist.push_back(params[q]);
-               }
+       time_t TS = ServerCommand::ExtractTS(params[1]);
 
-       }
-       /* Extract the TS value of the object, either User or Channel */
-       User* dst = ServerInstance->FindNick(params[0]);
-       Channel* chan = NULL;
-       time_t ourTS = 0;
+       Channel* const chan = ServerInstance->FindChan(params[0]);
+       if (!chan)
+               // Channel doesn't exist
+               return CMD_FAILURE;
 
-       if (dst)
-       {
-               ourTS = dst->age;
-       }
-       else
-       {
-               chan = ServerInstance->FindChan(params[0]);
-               if (chan)
-               {
-                       ourTS = chan->age;
-               }
-               else
-                       /* Oops, channel doesnt exist! */
-                       return CMD_FAILURE;
-       }
+       // Extract the TS of the channel in question
+       time_t ourTS = chan->age;
 
-       if (!TS)
-       {
-               ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"*** BUG? *** TS of 0 sent to FMODE. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
-               ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FMODE with a TS of zero. Total craq. Mode was dropped.", sourceserv.c_str());
-               return CMD_INVALID;
-       }
-
-       /* TS is equal or less: Merge the mode changes into ours and pass on.
+       /* If the TS is greater than ours, we drop the mode and don't pass it anywhere.
         */
-       if (TS <= ourTS)
-       {
-               bool merge = (TS == ourTS) && IS_SERVER(who);
-               ServerInstance->Modes->Process(modelist, who, merge);
-               return CMD_SUCCESS;
-       }
-       /* If the TS is greater than ours, we drop the mode and dont pass it anywhere.
+       if (TS > ourTS)
+               return CMD_FAILURE;
+
+       /* TS is equal or less: apply the mode change locally and forward the message
         */
-       return CMD_FAILURE;
-}
 
+       // 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(who, chan, NULL, changelist, flags);
+       return CMD_SUCCESS;
+}
index d559c6ae5c642bb3ba8f23bd9f03c489a390f443..01826e8f66b1ce573ae3389a142e58e6acdb2d9b 100644 (file)
 #include "inspircd.h"
 #include "commands.h"
 
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
 /** FTOPIC command */
-CmdResult CommandFTopic::Handle(const std::vector<std::string>& params, User *user)
+CmdResult CommandFTopic::Handle(User* user, Params& params)
 {
-       time_t ts = atoi(params[1].c_str());
        Channel* c = ServerInstance->FindChan(params[0]);
-       if (c)
+       if (!c)
+               return CMD_FAILURE;
+
+       if (c->age < ServerCommand::ExtractTS(params[1]))
+               // Our channel TS is older, nothing to do
+               return CMD_FAILURE;
+
+       // Channel::topicset is initialized to 0 on channel creation, so their ts will always win if we never had a topic
+       time_t ts = ServerCommand::ExtractTS(params[2]);
+       if (ts < c->topicset)
+               return CMD_FAILURE;
+
+       // The topic text is always the last parameter
+       const std::string& newtopic = params.back();
+
+       // If there is a setter in the message use that, otherwise use the message source
+       const std::string& setter = ((params.size() > 4) ? params[3] : (ServerInstance->Config->FullHostInTopic ? user->GetFullHost() : user->nick));
+
+       /*
+        * If the topics were updated at the exact same second, accept
+        * the remote only when it's "bigger" than ours as defined by
+        * string comparision, so non-empty topics always overridde
+        * empty topics if their timestamps are equal
+        *
+        * Similarly, if the topic texts are equal too, keep one topic
+        * setter and discard the other
+        */
+       if (ts == c->topicset)
        {
-               if ((ts >= c->topicset) || (c->topic.empty()))
-               {
-                       if (c->topic != params[3])
-                       {
-                               // Update topic only when it differs from current topic
-                               c->topic.assign(params[3], 0, ServerInstance->Config->Limits.MaxTopic);
-                               c->WriteChannel(user, "TOPIC %s :%s", c->name.c_str(), c->topic.c_str());
-                       }
-
-                       // Always update setter and settime.
-                       c->setby.assign(params[2], 0, 127);
-                       c->topicset = ts;
-               }
+               // Discard if their topic text is "smaller"
+               if (c->topic > newtopic)
+                       return CMD_FAILURE;
+
+               // If the texts are equal in addition to the timestamps, decide which setter to keep
+               if ((c->topic == newtopic) && (c->setby >= setter))
+                       return CMD_FAILURE;
        }
+
+       c->SetTopic(user, newtopic, ts, &setter);
        return CMD_SUCCESS;
 }
 
+// Used when bursting and in reply to RESYNC, contains topic setter as the 4th parameter
+CommandFTopic::Builder::Builder(Channel* chan)
+       : CmdBuilder("FTOPIC")
+{
+       push(chan->name);
+       push_int(chan->age);
+       push_int(chan->topicset);
+       push(chan->setby);
+       push_last(chan->topic);
+}
+
+// Used when changing the topic, the setter is the message source
+CommandFTopic::Builder::Builder(User* user, Channel* chan)
+       : CmdBuilder(user, "FTOPIC")
+{
+       push(chan->name);
+       push_int(chan->age);
+       push_int(chan->topicset);
+       push_last(chan->topic);
+}
index d990e1fbf4dfa1c680fbbc9640660e7896ccdfd2..2001d560de06e578f00fb51cabde35484fe6f932 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "../hash.h"
-#include "../ssl.h"
-#include "socketengine.h"
+#include "modules/hash.h"
+#include "modules/ssl.h"
 
 #include "main.h"
-#include "utils.h"
-#include "treeserver.h"
 #include "link.h"
 #include "treesocket.h"
-#include "resolvers.h"
 
 const std::string& TreeSocket::GetOurChallenge()
 {
@@ -57,44 +51,15 @@ std::string TreeSocket::MakePass(const std::string &password, const std::string
        /* This is a simple (maybe a bit hacky?) HMAC algorithm, thanks to jilles for
         * suggesting the use of HMAC to secure the password against various attacks.
         *
-        * Note: If m_sha256.so is not loaded, we MUST fall back to plaintext with no
+        * Note: If an sha256 provider is not available, we MUST fall back to plaintext with no
         *       HMAC challenge/response.
         */
        HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256");
-       if (Utils->ChallengeResponse && sha256 && !challenge.empty())
-       {
-               if (proto_version < 1202)
-               {
-                       /* This is how HMAC is done in InspIRCd 1.2:
-                        *
-                        * sha256( (pass xor 0x5c) + sha256((pass xor 0x36) + m) )
-                        *
-                        * 5c and 36 were chosen as part of the HMAC standard, because they
-                        * flip the bits in a way likely to strengthen the function.
-                        */
-                       std::string hmac1, hmac2;
-
-                       for (size_t n = 0; n < password.length(); n++)
-                       {
-                               hmac1.push_back(static_cast<char>(password[n] ^ 0x5C));
-                               hmac2.push_back(static_cast<char>(password[n] ^ 0x36));
-                       }
-
-                       hmac2.append(challenge);
-                       hmac2 = sha256->hexsum(hmac2);
-               
-                       std::string hmac = hmac1 + hmac2;
-                       hmac = sha256->hexsum(hmac);
-
-                       return "HMAC-SHA256:"+ hmac;
-               }
-               else
-               {
-                       return "AUTH:" + BinToBase64(sha256->hmac(password, challenge));
-               }
-       }
-       else if (!challenge.empty() && !sha256)
-               ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!");
+       if (sha256 && !challenge.empty())
+               return "AUTH:" + BinToBase64(sha256->hmac(password, challenge));
+
+       if (!challenge.empty() && !sha256)
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Not authenticating to server using SHA256/HMAC because we don't have an SHA256 provider (e.g. m_sha256) loaded!");
 
        return password;
 }
@@ -104,13 +69,16 @@ 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;
-       if (GetIOHook())
+       std::string fp = SSLClientCert::GetFingerprint(this);
+       if (capab->auth_fingerprint)
        {
-               SocketCertificateRequest req(this, Utils->Creator);
-               if (req.cert)
+               /* Require fingerprint to exist and match */
+               if (link.Fingerprint != fp)
                {
-                       fp = req.cert->GetFingerprint();
+                       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;
                }
        }
 
@@ -118,32 +86,24 @@ bool TreeSocket::ComparePass(const Link& link, const std::string &theirs)
        {
                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;
        }
 
-       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;
 }
index 18aeb0ad5ff26aff90cfc8a2af14f17349a3b3ba..11e665531c54ff43aa6fbeabddf89d467f50a620 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-
-#include "main.h"
 #include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
+#include "commands.h"
 
-bool TreeSocket::Whois(const std::string &prefix, parameterlist &params)
+CmdResult CommandIdle::HandleRemote(RemoteUser* issuer, Params& params)
 {
-       if (params.size() < 1)
-               return true;
-       User* u = ServerInstance->FindNick(prefix);
-       if (u)
+       /**
+        * There are two forms of IDLE: request and reply. Requests have one parameter,
+        * replies have more than one.
+        *
+        * If this is a request, 'issuer' did a /whois and its server wants to learn the
+        * idle time of the user in params[0].
+        *
+        * If this is a reply, params[0] is the user who did the whois and params.back() is
+        * the number of seconds 'issuer' has been idle.
+        */
+
+       User* target = ServerInstance->FindUUID(params[0]);
+       if ((!target) || (target->registered != REG_ALL))
+               return CMD_FAILURE;
+
+       LocalUser* localtarget = IS_LOCAL(target);
+       if (!localtarget)
        {
-               // an incoming request
-               if (params.size() == 1)
-               {
-                       User* x = ServerInstance->FindNick(params[0]);
-                       if ((x) && (IS_LOCAL(x)))
-                       {
-                               long idle = labs((long)((x->idle_lastmsg) - ServerInstance->Time()));
-                               parameterlist par;
-                               par.push_back(prefix);
-                               par.push_back(ConvToStr(x->signon));
-                               par.push_back(ConvToStr(idle));
-                               // ours, we're done, pass it BACK
-                               Utils->DoOneToOne(params[0], "IDLE", par, u->server);
-                       }
-                       else
-                       {
-                               // not ours pass it on
-                               if (x)
-                                       Utils->DoOneToOne(prefix, "IDLE", params, x->server);
-                       }
-               }
-               else if (params.size() == 3)
-               {
-                       std::string who_did_the_whois = params[0];
-                       User* who_to_send_to = ServerInstance->FindNick(who_did_the_whois);
-                       if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)) && (who_to_send_to->registered == REG_ALL))
-                       {
-                               // an incoming reply to a whois we sent out
-                               std::string nick_whoised = prefix;
-                               unsigned long signon = atoi(params[1].c_str());
-                               unsigned long idle = atoi(params[2].c_str());
-                               if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
-                               {
-                                       ServerInstance->DoWhois(who_to_send_to, u, signon, idle, nick_whoised.c_str());
-                               }
-                       }
-                       else
-                       {
-                               // not ours, pass it on
-                               if (who_to_send_to)
-                                       Utils->DoOneToOne(prefix, "IDLE", params, who_to_send_to->server);
-                       }
-               }
+               // Forward to target's server
+               return CMD_SUCCESS;
        }
-       return true;
-}
 
+       if (params.size() >= 2)
+       {
+               ServerInstance->Parser.CallHandler("WHOIS", params, issuer);
+       }
+       else
+       {
+               // A server is asking us the idle time of our user
+               unsigned int idle;
+               if (localtarget->idle_lastmsg >= ServerInstance->Time())
+                       // Possible case when our clock ticked backwards
+                       idle = 0;
+               else
+                       idle = ((unsigned int) (ServerInstance->Time() - localtarget->idle_lastmsg));
+
+               CmdBuilder reply(params[0], "IDLE");
+               reply.push_back(issuer->uuid);
+               reply.push_back(ConvToStr(target->signon));
+               reply.push_back(ConvToStr(idle));
+               reply.Unicast(issuer);
+       }
 
+       return CMD_SUCCESS;
+}
diff --git a/src/modules/m_spanningtree/ijoin.cpp b/src/modules/m_spanningtree/ijoin.cpp
new file mode 100644 (file)
index 0000000..85838cc
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2012-2013 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 "commands.h"
+#include "utils.h"
+#include "treeserver.h"
+#include "treesocket.h"
+
+CmdResult CommandIJoin::HandleRemote(RemoteUser* user, Params& params)
+{
+       Channel* chan = ServerInstance->FindChan(params[0]);
+       if (!chan)
+       {
+               // Desync detected, recover
+               // Ignore the join and send RESYNC, this will result in the remote server sending all channel data to us
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received IJOIN for nonexistent channel: " + params[0]);
+
+               CmdBuilder("RESYNC").push(params[0]).Unicast(user);
+
+               return CMD_FAILURE;
+       }
+
+       bool apply_modes;
+       if (params.size() > 3)
+       {
+               time_t RemoteTS = ServerCommand::ExtractTS(params[2]);
+               apply_modes = (RemoteTS <= chan->age);
+       }
+       else
+               apply_modes = false;
+
+       // 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;
+}
+
+CmdResult CommandResync::HandleServer(TreeServer* server, CommandBase::Params& params)
+{
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resyncing " + params[0]);
+       Channel* chan = ServerInstance->FindChan(params[0]);
+       if (!chan)
+       {
+               // This can happen for a number of reasons, safe to ignore
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Channel does not exist");
+               return CMD_FAILURE;
+       }
+
+       if (!server->IsLocal())
+               throw ProtocolException("RESYNC from a server that is not directly connected");
+
+       // Send all known information about the channel
+       server->GetSocket()->SyncChannel(chan);
+       return CMD_SUCCESS;
+}
index 797f108d86588ca3efe3f0495e66c3eb0073481e..5b9361fcdaad98922069e45bfcfc845ddb864792 100644 (file)
  */
 
 
-#ifndef M_SPANNINGTREE_LINK_H
-#define M_SPANNINGTREE_LINK_H
+#pragma once
 
 class Link : public refcountbase
 {
  public:
        reference<ConfigTag> tag;
-       irc::string Name;
+       std::string Name;
        std::string IPAddr;
-       int Port;
+       unsigned int Port;
        std::string SendPass;
        std::string RecvPass;
        std::string Fingerprint;
-       std::string AllowMask;
+       std::vector<std::string> AllowMasks;
        bool HiddenFromStats;
        std::string Hook;
-       int Timeout;
+       unsigned int Timeout;
        std::string Bind;
        bool Hidden;
        Link(ConfigTag* Tag) : tag(Tag) {}
@@ -51,5 +50,3 @@ class Autoconnect : public refcountbase
        int position;
        Autoconnect(ConfigTag* Tag) : tag(Tag) {}
 };
-
-#endif
index 78d202c477c764fc4dab8ad733af35d220f0ea0f..8b24b1e226b7a3e61d996b831679287272c8e473 100644 (file)
  */
 
 
-/* $ModDesc: Provides a spanning tree server link protocol */
-
 #include "inspircd.h"
 #include "socket.h"
 #include "xline.h"
+#include "iohook.h"
+#include "modules/server.h"
 
-#include "cachetimer.h"
 #include "resolvers.h"
 #include "main.h"
 #include "utils.h"
 #include "link.h"
 #include "treesocket.h"
 #include "commands.h"
-#include "protocolinterface.h"
+#include "translate.h"
 
 ModuleSpanningTree::ModuleSpanningTree()
-       : KeepNickTS(false)
+       : Away::EventListener(this)
+       , Stats::EventListener(this)
+       , rconnect(this)
+       , rsquit(this)
+       , map(this)
+       , commands(this)
+       , currmembid(0)
+       , eventprov(this, "event/server")
+       , sslapi(this)
+       , DNS(this, "DNS")
+       , tagevprov(this, "event/messagetag")
+       , loopCall(false)
 {
-       Utils = new SpanningTreeUtilities(this);
-       commands = new SpanningTreeCommands(this);
-       RefreshTimer = NULL;
 }
 
 SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module)
-       : rconnect(module, module->Utils), rsquit(module, module->Utils),
-       svsjoin(module), svspart(module), svsnick(module), metadata(module),
-       uid(module), opertype(module), fjoin(module), fmode(module), ftopic(module),
-       fhost(module), fident(module), fname(module)
+       : svsjoin(module), svspart(module), svsnick(module), metadata(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), save(module),
+       server(module), squit(module), snonotice(module),
+       endburst(module), sinfo(module), num(module)
 {
 }
 
+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 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->Modules->AddService(commands->rconnect);
-       ServerInstance->Modules->AddService(commands->rsquit);
-       ServerInstance->Modules->AddService(commands->svsjoin);
-       ServerInstance->Modules->AddService(commands->svspart);
-       ServerInstance->Modules->AddService(commands->svsnick);
-       ServerInstance->Modules->AddService(commands->metadata);
-       ServerInstance->Modules->AddService(commands->uid);
-       ServerInstance->Modules->AddService(commands->opertype);
-       ServerInstance->Modules->AddService(commands->fjoin);
-       ServerInstance->Modules->AddService(commands->fmode);
-       ServerInstance->Modules->AddService(commands->ftopic);
-       ServerInstance->Modules->AddService(commands->fhost);
-       ServerInstance->Modules->AddService(commands->fident);
-       ServerInstance->Modules->AddService(commands->fname);
-       RefreshTimer = new CacheRefreshTimer(Utils);
-       ServerInstance->Timers->AddTimer(RefreshTimer);
-
-       Implementation eventlist[] =
-       {
-               I_OnPreCommand, I_OnGetServerDescription, I_OnUserInvite, I_OnPostTopicChange,
-               I_OnWallops, I_OnUserNotice, I_OnUserMessage, I_OnBackgroundTimer, I_OnUserJoin,
-               I_OnChangeHost, I_OnChangeName, I_OnChangeIdent, I_OnUserPart, I_OnUnloadModule,
-               I_OnUserQuit, I_OnUserPostNick, I_OnUserKick, I_OnRemoteKill, I_OnRehash, I_OnPreRehash,
-               I_OnOper, I_OnAddLine, I_OnDelLine, I_OnMode, I_OnLoadModule, I_OnStats,
-               I_OnSetAway, I_OnPostCommand, I_OnUserConnect, I_OnAcceptConnection
-       };
-       ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
-       delete ServerInstance->PI;
-       ServerInstance->PI = new SpanningTreeProtocolInterface(Utils);
-       loopCall = false;
-
-       // update our local user count
-       Utils->TreeRoot->SetUserCount(ServerInstance->Users->LocalUserCount());
+       ServerInstance->SNO->EnableSnomask('l', "LINK");
+
+       ResetMembershipIds();
+
+       Utils = new SpanningTreeUtilities(this);
+       Utils->TreeRoot = new TreeServer;
+
+       ServerInstance->PI = &protocolinterface;
+
+       delete ServerInstance->FakeClient->server;
+       SetLocalUsersServer(Utils->TreeRoot);
 }
 
 void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
@@ -98,44 +110,39 @@ void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
        {
                Parent = Current->GetParent()->GetName();
        }
-       for (unsigned int q = 0; q < Current->ChildCount(); q++)
+
+       const TreeServer::ChildServers& children = Current->GetChildren();
+       for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
        {
-               if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName()))))
+               TreeServer* server = *i;
+               if ((server->Hidden) || ((Utils->HideULines) && (server->IsULine())))
                {
-                       if (IS_OPER(user))
+                       if (user->IsOper())
                        {
-                                ShowLinks(Current->GetChild(q),user,hops+1);
+                                ShowLinks(server, user, hops+1);
                        }
                }
                else
                {
-                       ShowLinks(Current->GetChild(q),user,hops+1);
+                       ShowLinks(server, user, hops+1);
                }
        }
        /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
-       if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName())) && (!IS_OPER(user)))
+       if ((Utils->HideULines) && (Current->IsULine()) && (!user->IsOper()))
                return;
        /* Or if the server is hidden and they're not an oper */
-       else if ((Current->Hidden) && (!IS_OPER(user)))
+       else if ((Current->Hidden) && (!user->IsOper()))
                return;
 
-       std::string servername = Current->GetName();
-       user->WriteNumeric(364, "%s %s %s :%d %s",      user->nick.c_str(), servername.c_str(),
-                       (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
-                       (Utils->FlatLinks && (!IS_OPER(user))) ? 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()));
 }
 
-int ModuleSpanningTree::CountServs()
-{
-       return Utils->serverlist.size();
-}
-
-void ModuleSpanningTree::HandleLinks(const std::vector<std::string>& parameters, User* user)
+void ModuleSpanningTree::HandleLinks(const CommandBase::Params& parameters, User* user)
 {
        ShowLinks(Utils->TreeRoot,user,0);
-       user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick.c_str());
-       return;
+       user->WriteNumeric(RPL_ENDOFLINKS, '*', "End of /LINKS list.");
 }
 
 std::string ModuleSpanningTree::TimeToStr(time_t secs)
@@ -152,79 +159,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;
-
-               if (s->GetSocket() && s->GetSocket()->GetLinkState() == DYING)
-               {
-                       s->GetSocket()->Close();
-                       goto restart;
-               }
-
-               // Fix for bug #792, do not ping servers that are not connected yet!
-               // Remote servers have Socket == NULL and local connected servers have
-               // Socket->LinkState == CONNECTED
-               if (s->GetSocket() && s->GetSocket()->GetLinkState() != CONNECTED)
-                       continue;
-
-               // Now do PING checks on all servers
-               TreeServer *mts = Utils->BestRouteTo(s->GetID());
-
-               if (mts)
-               {
-                       // 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);
-                                       TreeSocket *tsock = mts->GetSocket();
-
-                                       // ... if we can find a proper route to them
-                                       if (tsock)
-                                       {
-                                               tsock->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " +
-                                                               ServerInstance->Config->GetSID() + " " + s->GetID());
-                                               s->LastPingMsec = ts;
-                                       }
-                               }
-                               else
-                               {
-                                       // They didn't answer the last ping, if they are locally connected, get rid of them.
-                                       TreeSocket *sock = s->GetSocket();
-                                       if (sock)
-                                       {
-                                               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 */
-                               std::string servername = s->GetName();
-                               ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", servername.c_str(), Utils->PingWarnTime);
-                               s->Warned = true;
-                       }
-               }
-       }
-}
-
 void ModuleSpanningTree::ConnectServer(Autoconnect* a, bool on_timer)
 {
        if (!a)
@@ -264,33 +198,38 @@ void ModuleSpanningTree::ConnectServer(Autoconnect* a, bool on_timer)
 
 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;
        }
 
-       QueryType start_type = DNS_QUERY_AAAA;
-       if (strchr(x->IPAddr.c_str(),':'))
+       irc::sockets::sockaddrs sa;
+#ifndef _WIN32
+       if (x->IPAddr.find('/') != std::string::npos)
        {
-               in6_addr n;
-               if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)
-                       ipvalid = false;
+               struct stat sb;
+               if (stat(x->IPAddr.c_str(), &sb) == -1 || !S_ISSOCK(sb.st_mode) || !irc::sockets::untosa(x->IPAddr, sa))
+               {
+                       // We don't use the family() != AF_UNSPEC check below for UNIX sockets as
+                       // that results in a DNS lookup.
+                       ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s is not a UNIX socket!",
+                               x->Name.c_str(), x->IPAddr.c_str());
+                       return;
+               }
        }
        else
+#endif
        {
-               in_addr n;
-               if (inet_aton(x->IPAddr.c_str(),&n) < 1)
-                       ipvalid = false;
+               // If this fails then the IP sa will be AF_UNSPEC.
+               irc::sockets::aptosa(x->IPAddr, x->Port, sa);
        }
-
+       
        /* Do we already have an IP? If so, no need to resolve it. */
-       if (ipvalid)
+       if (sa.family() != AF_UNSPEC)
        {
-               /* Gave a hook, but it wasnt one we know */
-               TreeSocket* newsocket = new TreeSocket(Utils, x, y, x->IPAddr);
+               // Create a TreeServer object that will start connecting immediately in the background
+               TreeSocket* newsocket = new TreeSocket(x, y, sa);
                if (newsocket->GetFd() > -1)
                {
                        /* Handled automatically on success */
@@ -302,17 +241,30 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
                        ServerInstance->GlobalCulls.AddItem(newsocket);
                }
        }
+       else if (!DNS)
+       {
+               ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Hostname given and core_dns is not loaded, unable to resolve.", x->Name.c_str());
+       }
        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.family() == AF_INET)
+                               start_type = DNS::QUERY_A;
+               }
+
+               ServernameResolver* snr = new ServernameResolver(*DNS, x->IPAddr, x, start_type, y);
                try
                {
-                       bool cached = false;
-                       ServernameResolver* snr = new ServernameResolver(Utils, x->IPAddr, x, cached, start_type, y);
-                       ServerInstance->AddResolver(snr, cached);
+                       DNS->Process(snr);
                }
-               catch (ModuleException& e)
+               catch (DNS::Exception& e)
                {
-                       ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
+                       delete snr;
+                       ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason().c_str());
                        ConnectServer(y, false);
                }
        }
@@ -333,12 +285,12 @@ void ModuleSpanningTree::AutoConnectServers(time_t curtime)
 
 void ModuleSpanningTree::DoConnectTimeout(time_t curtime)
 {
-       std::map<TreeSocket*, std::pair<std::string, int> >::iterator i = Utils->timeoutlist.begin();
+       SpanningTreeUtilities::TimeoutList::iterator i = Utils->timeoutlist.begin();
        while (i != Utils->timeoutlist.end())
        {
                TreeSocket* s = i->first;
-               std::pair<std::string, int> p = i->second;
-               std::map<TreeSocket*, std::pair<std::string, int> >::iterator me = i;
+               std::pair<std::string, unsigned int> p = i->second;
+               SpanningTreeUtilities::TimeoutList::iterator me = i;
                i++;
                if (s->GetLinkState() == DYING)
                {
@@ -347,233 +299,145 @@ void ModuleSpanningTree::DoConnectTimeout(time_t curtime)
                }
                else if (curtime > s->age + p.second)
                {
-                       ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002 (timeout of %d seconds)",p.first.c_str(),p.second);
+                       ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002 (timeout of %u seconds)",p.first.c_str(),p.second);
                        Utils->timeoutlist.erase(me);
                        s->Close();
                }
        }
 }
 
-ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& parameters, User* user)
+ModResult ModuleSpanningTree::HandleVersion(const CommandBase::Params& 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)
        {
-               std::string Version = found->GetVersion();
-               user->WriteNumeric(351, "%s :%s",user->nick.c_str(),Version.c_str());
                if (found == Utils->TreeRoot)
                {
-                       ServerInstance->Config->Send005(user);
+                       // Pass to default VERSION handler.
+                       return MOD_RES_PASSTHRU;
                }
+
+               // 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()));
+
+               Numeric::Numeric numeric(RPL_VERSION);
+               irc::tokenstream tokens(showfull ? found->GetFullVersion() : found->GetVersion());
+               for (std::string token; tokens.GetTrailing(token); )
+                       numeric.push(token);
+               user->WriteNumeric(numeric);
        }
        else
        {
-               user->WriteNumeric(402, "%s %s :No such server",user->nick.c_str(),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, ...)
-{
-       char text[MAXBUF];
-       va_list argsPtr;
-
-       va_start(argsPtr, format);
-       vsnprintf(text, MAXBUF, format, argsPtr);
-       va_end(argsPtr);
-
-       if (IS_LOCAL(user))
-               user->WriteServ("NOTICE %s :%s", user->nick.c_str(), text);
-       else
-               ServerInstance->PI->SendUserNotice(user, text);
-}
-
-ModResult ModuleSpanningTree::HandleConnect(const std::vector<std::string>& parameters, User* user)
+ModResult ModuleSpanningTree::HandleConnect(const CommandBase::Params& 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
                        {
-                               std::string servername = CheckDupe->GetParent()->GetName();
-                               RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), servername.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::OnGetServerDescription(const std::string &servername,std::string &description)
-{
-       TreeServer* s = Utils->FindServer(servername);
-       if (s)
-       {
-               description = s->GetDesc();
-       }
-}
-
-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))
        {
-               parameterlist params;
+               CmdBuilder params(source, "INVITE");
                params.push_back(dest->uuid);
                params.push_back(channel->name);
+               params.push_int(channel->age);
                params.push_back(ConvToStr(expiry));
-               Utils->DoOneToMany(source->uuid,"INVITE",params);
+               params.Broadcast();
        }
 }
 
-void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std::string &topic)
-{
-       // Drop remote events on the floor.
-       if (!IS_LOCAL(user))
-               return;
-
-       parameterlist params;
-       params.push_back(chan->name);
-       params.push_back(":"+topic);
-       Utils->DoOneToMany(user->uuid,"TOPIC",params);
-}
-
-void ModuleSpanningTree::OnWallops(User* user, const std::string &text)
+ModResult ModuleSpanningTree::OnPreTopicChange(User* user, Channel* chan, const std::string& topic)
 {
-       if (IS_LOCAL(user))
+       // 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))
        {
-               parameterlist params;
-               params.push_back(":"+text);
-               Utils->DoOneToMany(user->uuid,"WALLOPS",params);
+               user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, "Retry topic change later");
+               return MOD_RES_DENY;
        }
+       return MOD_RES_PASSTHRU;
 }
 
-void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std::string &topic)
 {
-       /* Server origin */
-       if (user == NULL)
+       // Drop remote events on the floor.
+       if (!IS_LOCAL(user))
                return;
 
-       if (target_type == TYPE_USER)
-       {
-               User* d = (User*)dest;
-               if (!IS_LOCAL(d) && IS_LOCAL(user))
-               {
-                       parameterlist params;
-                       params.push_back(d->uuid);
-                       params.push_back(":"+text);
-                       Utils->DoOneToOne(user->uuid,"NOTICE",params,d->server);
-               }
-       }
-       else if (target_type == TYPE_CHANNEL)
-       {
-               if (IS_LOCAL(user))
-               {
-                       Channel *c = (Channel*)dest;
-                       if (c)
-                       {
-                               std::string cname = c->name;
-                               if (status)
-                                       cname = status + cname;
-                               TreeServerList list;
-                               Utils->GetListOfServersForChannel(c,list,status,exempt_list);
-                               for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
-                               {
-                                       TreeSocket* Sock = i->second->GetSocket();
-                                       if (Sock)
-                                               Sock->WriteLine(":"+std::string(user->uuid)+" NOTICE "+cname+" :"+text);
-                               }
-                       }
-               }
-       }
-       else if (target_type == TYPE_SERVER)
-       {
-               if (IS_LOCAL(user))
-               {
-                       char* target = (char*)dest;
-                       parameterlist par;
-                       par.push_back(target);
-                       par.push_back(":"+text);
-                       Utils->DoOneToMany(user->uuid,"NOTICE",par);
-               }
-       }
+       CommandFTopic::Builder(user, chan).Broadcast();
 }
 
-void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+void ModuleSpanningTree::OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details)
 {
-       /* Server origin */
-       if (user == NULL)
+       if (!IS_LOCAL(user))
                return;
 
-       if (target_type == TYPE_USER)
+       const char* message_type = (details.type == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
+       if (target.type == MessageTarget::TYPE_USER)
        {
-               // route private messages which are targetted at clients only to the server
-               // which needs to receive them
-               User* d = (User*)dest;
-               if (!IS_LOCAL(d) && (IS_LOCAL(user)))
+               User* d = target.Get<User>();
+               if (!IS_LOCAL(d))
                {
-                       parameterlist params;
+                       CmdBuilder params(user, message_type);
+                       params.push_tags(details.tags_out);
                        params.push_back(d->uuid);
-                       params.push_back(":"+text);
-                       Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server);
+                       params.push_last(details.text);
+                       params.Unicast(d);
                }
        }
-       else if (target_type == TYPE_CHANNEL)
+       else if (target.type == MessageTarget::TYPE_CHANNEL)
        {
-               if (IS_LOCAL(user))
-               {
-                       Channel *c = (Channel*)dest;
-                       if (c)
-                       {
-                               std::string cname = c->name;
-                               if (status)
-                                       cname = status + cname;
-                               TreeServerList list;
-                               Utils->GetListOfServersForChannel(c,list,status,exempt_list);
-                               for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
-                               {
-                                       TreeSocket* Sock = i->second->GetSocket();
-                                       if (Sock)
-                                               Sock->WriteLine(":"+std::string(user->uuid)+" PRIVMSG "+cname+" :"+text);
-                               }
-                       }
-               }
+               Utils->SendChannelMessage(user->uuid, target.Get<Channel>(), details.text, target.status, details.tags_out, details.exemptions, message_type);
        }
-       else if (target_type == TYPE_SERVER)
+       else if (target.type == MessageTarget::TYPE_SERVER)
        {
-               if (IS_LOCAL(user))
-               {
-                       char* target = (char*)dest;
-                       parameterlist par;
-                       par.push_back(target);
-                       par.push_back(":"+text);
-                       Utils->DoOneToMany(user->uuid,"PRIVMSG",par);
-               }
+               const std::string* serverglob = target.Get<std::string>();
+               CmdBuilder par(user, message_type);
+               par.push_tags(details.tags_out);
+               par.push_back(*serverglob);
+               par.push_last(details.text);
+               par.Broadcast();
        }
 }
 
 void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
 {
        AutoConnectServers(curtime);
-       DoPingChecks(curtime);
        DoConnectTimeout(curtime);
 }
 
@@ -582,25 +446,14 @@ void ModuleSpanningTree::OnUserConnect(LocalUser* user)
        if (user->quitting)
                return;
 
-       parameterlist params;
-       params.push_back(user->uuid);
-       params.push_back(ConvToStr(user->age));
-       params.push_back(user->nick);
-       params.push_back(user->host);
-       params.push_back(user->dhost);
-       params.push_back(user->ident);
-       params.push_back(user->GetIPString());
-       params.push_back(ConvToStr(user->signon));
-       params.push_back("+"+std::string(user->FormatModes(true)));
-       params.push_back(":"+user->fullname);
-       Utils->DoOneToMany(ServerInstance->Config->GetSID(), "UID", params);
+       // Create the lazy ssl_cert metadata for this user if not already created.
+       if (sslapi)
+               sslapi->GetCertificate(user);
 
-       if (IS_OPER(user))
-       {
-               params.clear();
-               params.push_back(user->oper->name);
-               Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
-       }
+       CommandUID::Builder(user).Broadcast();
+
+       if (user->IsOper())
+               CommandOpertype::Builder(user).Broadcast();
 
        for(Extensible::ExtensibleStore::const_iterator i = user->GetExtList().begin(); i != user->GetExtList().end(); i++)
        {
@@ -610,23 +463,36 @@ void ModuleSpanningTree::OnUserConnect(LocalUser* user)
                        ServerInstance->PI->SendMetaData(user, item->name, value);
        }
 
-       Utils->TreeRoot->SetUserCount(1); // increment by 1
+       Utils->TreeRoot->UserCount++;
 }
 
-void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created_by_local, CUList& excepts)
 {
        // Only do this for local users
-       if (IS_LOCAL(memb->user))
+       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)
+       {
+               CommandFJoin::Builder params(memb->chan);
+               params.add(memb);
+               params.finalize();
+               params.Broadcast();
+       }
+       else
        {
-               parameterlist params;
-               // set up their permissions and the channel TS with FJOIN.
-               // All users are FJOINed now, because a module may specify
-               // new joining permissions for the user.
+               CmdBuilder params(memb->user, "IJOIN");
                params.push_back(memb->chan->name);
-               params.push_back(ConvToStr(memb->chan->age));
-               params.push_back(std::string("+") + memb->chan->ChanModes(true));
-               params.push_back(memb->modes+","+memb->user->uuid);
-               Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
+               params.push_int(memb->id);
+               if (!memb->modes.empty())
+               {
+                       params.push_back(ConvToStr(memb->chan->age));
+                       params.push_back(memb->modes);
+               }
+               params.Broadcast();
        }
 }
 
@@ -635,19 +501,15 @@ void ModuleSpanningTree::OnChangeHost(User* user, const std::string &newhost)
        if (user->registered != REG_ALL || !IS_LOCAL(user))
                return;
 
-       parameterlist params;
-       params.push_back(newhost);
-       Utils->DoOneToMany(user->uuid,"FHOST",params);
+       CmdBuilder(user, "FHOST").push(newhost).Broadcast();
 }
 
-void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
+void ModuleSpanningTree::OnChangeRealName(User* user, const std::string& real)
 {
        if (user->registered != REG_ALL || !IS_LOCAL(user))
                return;
 
-       parameterlist params;
-       params.push_back(":" + gecos);
-       Utils->DoOneToMany(user->uuid,"FNAME",params);
+       CmdBuilder(user, "FNAME").push_last(real).Broadcast();
 }
 
 void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
@@ -655,121 +517,104 @@ void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
        if ((user->registered != REG_ALL) || (!IS_LOCAL(user)))
                return;
 
-       parameterlist params;
-       params.push_back(ident);
-       Utils->DoOneToMany(user->uuid,"FIDENT",params);
+       CmdBuilder(user, "FIDENT").push(ident).Broadcast();
 }
 
 void ModuleSpanningTree::OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
 {
        if (IS_LOCAL(memb->user))
        {
-               parameterlist params;
+               CmdBuilder params(memb->user, "PART");
                params.push_back(memb->chan->name);
                if (!partmessage.empty())
-                       params.push_back(":"+partmessage);
-               Utils->DoOneToMany(memb->user->uuid,"PART",params);
+                       params.push_last(partmessage);
+               params.Broadcast();
        }
 }
 
 void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
 {
-       if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
+       if (IS_LOCAL(user))
        {
-               parameterlist params;
-
                if (oper_message != reason)
+                       ServerInstance->PI->SendMetaData(user, "operquit", oper_message);
+
+               CmdBuilder(user, "QUIT").push_last(reason).Broadcast();
+       }
+       else
+       {
+               // 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
+               TreeServer* server = TreeServer::Get(user);
+               bool hide = (((server->IsDead()) && (Utils->quiet_bursts)) || (server->IsSilentULine()));
+               if (!hide)
                {
-                       params.push_back(":"+oper_message);
-                       Utils->DoOneToMany(user->uuid,"OPERQUIT",params);
+                       ServerInstance->SNO->WriteToSnoMask('Q', "Client exiting on server %s: %s (%s) [%s]",
+                               user->server->GetName().c_str(), user->GetFullRealHost().c_str(), user->GetIPString().c_str(), oper_message.c_str());
                }
-               params.clear();
-               params.push_back(":"+reason);
-               Utils->DoOneToMany(user->uuid,"QUIT",params);
        }
 
-       // Regardless, We need to modify the user Counts..
-       TreeServer* SourceServer = Utils->FindServer(user->server);
-       if (SourceServer)
-       {
-               SourceServer->SetUserCount(-1); // decrement by 1
-       }
+       // Regardless, update the UserCount
+       TreeServer::Get(user)->UserCount--;
 }
 
 void ModuleSpanningTree::OnUserPostNick(User* user, const std::string &oldnick)
 {
        if (IS_LOCAL(user))
        {
-               parameterlist params;
+               // The nick TS is updated by the core, we don't do it
+               CmdBuilder params(user, "NICK");
                params.push_back(user->nick);
-
-               /** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick!
-                */
-               if ((irc::string(user->nick.c_str()) != assign(oldnick)) && (!this->KeepNickTS))
-                       user->age = ServerInstance->Time();
-
                params.push_back(ConvToStr(user->age));
-               Utils->DoOneToMany(user->uuid,"NICK",params);
-               this->KeepNickTS = false;
+               params.Broadcast();
        }
-       else if (!loopCall && user->nick == user->uuid)
+       else if (!loopCall)
        {
-               parameterlist params;
-               params.push_back(user->uuid);
-               params.push_back(ConvToStr(user->age));
-               Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params);
+               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);
        }
 }
 
 void ModuleSpanningTree::OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
 {
-       parameterlist params;
+       if ((!IS_LOCAL(source)) && (source != ServerInstance->FakeClient))
+               return;
+
+       CmdBuilder params(source, "KICK");
        params.push_back(memb->chan->name);
        params.push_back(memb->user->uuid);
-       params.push_back(":"+reason);
-       if (IS_LOCAL(source))
-       {
-               Utils->DoOneToMany(source->uuid,"KICK",params);
-       }
-       else if (source == ServerInstance->FakeClient)
-       {
-               Utils->DoOneToMany(ServerInstance->Config->GetSID(),"KICK",params);
-       }
-}
-
-void ModuleSpanningTree::OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason)
-{
-       if (!IS_LOCAL(source))
-               return; // Only start routing if we're origin.
-
-       ServerInstance->OperQuit.set(dest, operreason);
-       parameterlist params;
-       params.push_back(":"+operreason);
-       Utils->DoOneToMany(dest->uuid,"OPERQUIT",params);
-       params.clear();
-       params.push_back(dest->uuid);
-       params.push_back(":"+reason);
-       Utils->DoOneToMany(source->uuid,"KILL",params);
+       // 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();
 }
 
 void ModuleSpanningTree::OnPreRehash(User* user, const std::string &parameter)
 {
-       if (loopCall)
-               return; // Don't generate a REHASH here if we're in the middle of processing a message that generated this one
-
-       ServerInstance->Logs->Log("remoterehash", DEBUG, "called with param %s", parameter.c_str());
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnPreRehash called with param %s", parameter.c_str());
 
        // Send out to other servers
        if (!parameter.empty() && parameter[0] != '-')
        {
-               parameterlist params;
+               CmdBuilder params((user ? user->uuid : ServerInstance->Config->GetSID()), "REHASH");
                params.push_back(parameter);
-               Utils->DoOneToAllButSender(user ? user->uuid : ServerInstance->Config->GetSID(), "REHASH", params, user ? user->server : ServerInstance->Config->ServerName);
+               params.Forward(user ? TreeServer::Get(user)->GetRoute() : NULL);
        }
 }
 
-void ModuleSpanningTree::OnRehash(User* user)
+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
        {
@@ -783,8 +628,8 @@ void ModuleSpanningTree::OnRehash(User* user)
                std::string msg = "Error in configuration: ";
                msg.append(e.GetReason());
                ServerInstance->SNO->WriteToSnoMask('l', msg);
-               if (user && !IS_LOCAL(user))
-                       ServerInstance->PI->SendSNONotice("L", msg);
+               if (status.srcuser && !IS_LOCAL(status.srcuser))
+                       ServerInstance->PI->SendSNONotice('L', msg);
        }
 }
 
@@ -799,24 +644,41 @@ void ModuleSpanningTree::OnLoadModule(Module* mod)
                data.push_back('=');
                data.append(v.link_data);
        }
-       ServerInstance->PI->SendMetaData(NULL, "modules", data);
+       ServerInstance->PI->SendMetaData("modules", data);
 }
 
 void ModuleSpanningTree::OnUnloadModule(Module* mod)
 {
-       ServerInstance->PI->SendMetaData(NULL, "modules", "-" + mod->ModuleSourceFile);
+       if (!Utils)
+               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(), ServerEventListener, OnServerSplit, (server));
+               }
+               return;
+       }
+
+       // Some other module is being unloaded. If it provides an IOHook we use, we must close that server connection now.
 
 restart:
-       unsigned int items = Utils->TreeRoot->ChildCount();
-       for(unsigned int x = 0; x < items; x++)
+       // 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)
        {
-               TreeServer* srv = Utils->TreeRoot->GetChild(x);
-               TreeSocket* sock = srv->GetSocket();
-               if (sock && sock->GetIOHook() == mod)
+               TreeSocket* sock = (*i)->GetSocket();
+               if (sock->GetModHook(mod))
                {
                        sock->SendError("SSL module unloaded");
                        sock->Close();
-                       // XXX: The list we're iterating is modified by TreeSocket::Squit() which is called by Close()
+                       // XXX: The list we're iterating is modified by TreeServer::SQuit() which is called by Close()
                        goto restart;
                }
        }
@@ -824,169 +686,100 @@ restart:
        for (SpanningTreeUtilities::TimeoutList::const_iterator i = Utils->timeoutlist.begin(); i != Utils->timeoutlist.end(); ++i)
        {
                TreeSocket* sock = i->first;
-               if (sock->GetIOHook() == 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;
-       parameterlist params;
-       params.push_back(opertype);
-       Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
+
+       // Note: The protocol does not allow direct umode +o;
+       // sending OPERTYPE infers +o modechange locally.
+       CommandOpertype::Builder(user).Broadcast();
 }
 
 void ModuleSpanningTree::OnAddLine(User* user, XLine *x)
 {
-       if (!x->IsBurstable() || loopCall)
+       if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
                return;
 
-       parameterlist params;
-       params.push_back(x->type);
-       params.push_back(x->Displayable());
-       params.push_back(x->source);
-       params.push_back(ConvToStr(x->set_time));
-       params.push_back(ConvToStr(x->duration));
-       params.push_back(":" + x->reason);
-
        if (!user)
-       {
-               /* Server-set lines */
-               Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params);
-       }
-       else if (IS_LOCAL(user))
-       {
-               /* User-set lines */
-               Utils->DoOneToMany(user->uuid, "ADDLINE", params);
-       }
+               user = ServerInstance->FakeClient;
+
+       CommandAddLine::Builder(x, user).Broadcast();
 }
 
 void ModuleSpanningTree::OnDelLine(User* user, XLine *x)
 {
-       if (!x->IsBurstable() || loopCall)
+       if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
                return;
 
-       parameterlist params;
-       params.push_back(x->type);
-       params.push_back(x->Displayable());
-
        if (!user)
-       {
-               /* Server-unset lines */
-               Utils->DoOneToMany(ServerInstance->Config->GetSID(), "DELLINE", params);
-       }
-       else if (IS_LOCAL(user))
-       {
-               /* User-unset lines */
-               Utils->DoOneToMany(user->uuid, "DELLINE", params);
-       }
-}
-
-void ModuleSpanningTree::OnMode(User* user, void* dest, int target_type, const parameterlist &text, const std::vector<TranslateType> &translate)
-{
-       if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
-       {
-               parameterlist params;
-               std::string output_text;
-
-               ServerInstance->Parser->TranslateUIDs(translate, text, output_text);
+               user = ServerInstance->FakeClient;
 
-               if (target_type == TYPE_USER)
-               {
-                       User* u = (User*)dest;
-                       params.push_back(u->uuid);
-                       params.push_back(output_text);
-                       Utils->DoOneToMany(user->uuid, "MODE", params);
-               }
-               else
-               {
-                       Channel* c = (Channel*)dest;
-                       params.push_back(c->name);
-                       params.push_back(ConvToStr(c->age));
-                       params.push_back(output_text);
-                       Utils->DoOneToMany(user->uuid, "FMODE", params);
-               }
-       }
+       CmdBuilder params(user, "DELLINE");
+       params.push_back(x->type);
+       params.push_back(x->Displayable());
+       params.Broadcast();
 }
 
-ModResult ModuleSpanningTree::OnSetAway(User* user, const std::string &awaymsg)
+void ModuleSpanningTree::OnUserAway(User* user)
 {
        if (IS_LOCAL(user))
-       {
-               parameterlist params;
-               if (!awaymsg.empty())
-               {
-                       params.push_back(ConvToStr(ServerInstance->Time()));
-                       params.push_back(":" + awaymsg);
-               }
-               Utils->DoOneToMany(user->uuid, "AWAY", params);
-       }
-
-       return MOD_RES_PASSTHRU;
+               CommandAway::Builder(user).Broadcast();
 }
 
-void ModuleSpanningTree::OnRequest(Request& request)
+void ModuleSpanningTree::OnUserBack(User* user)
 {
-       if (!strcmp(request.id, "rehash"))
-               Utils->Rehash();
+       if (IS_LOCAL(user))
+               CommandAway::Builder(user).Broadcast();
 }
 
-void ModuleSpanningTree::ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const parameterlist &modeline, const std::vector<TranslateType> &translate)
+void ModuleSpanningTree::OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags)
 {
-       TreeSocket* s = (TreeSocket*)opaque;
-       std::string output_text;
+       if (processflags & ModeParser::MODE_LOCALONLY)
+               return;
 
-       ServerInstance->Parser->TranslateUIDs(translate, modeline, output_text);
+       if (u)
+       {
+               if (u->registered != REG_ALL)
+                       return;
 
-       if (target)
+               CmdBuilder params(source, "MODE");
+               params.push(u->uuid);
+               params.push(ClientProtocol::Messages::Mode::ToModeLetters(modes));
+               params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
+               params.Broadcast();
+       }
+       else
        {
-               if (target_type == TYPE_USER)
-               {
-                       User* u = (User*)target;
-                       s->WriteLine(":"+ServerInstance->Config->GetSID()+" MODE "+u->uuid+" "+output_text);
-               }
-               else if (target_type == TYPE_CHANNEL)
-               {
-                       Channel* c = (Channel*)target;
-                       s->WriteLine(":"+ServerInstance->Config->GetSID()+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+output_text);
-               }
+               CmdBuilder params(source, "FMODE");
+               params.push(c->name);
+               params.push_int(c->age);
+               params.push(ClientProtocol::Messages::Mode::ToModeLetters(modes));
+               params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
+               params.Broadcast();
        }
 }
 
-void ModuleSpanningTree::ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata)
-{
-       TreeSocket* s = static_cast<TreeSocket*>(opaque);
-       User* u = dynamic_cast<User*>(target);
-       Channel* c = dynamic_cast<Channel*>(target);
-       if (u)
-               s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
-       else if (c)
-               s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
-       else if (!target)
-               s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
-}
-
 CullResult ModuleSpanningTree::cull()
 {
-       Utils->cull();
-       ServerInstance->Timers->DelTimer(RefreshTimer);
+       if (Utils)
+               Utils->cull();
        return this->Module::cull();
 }
 
 ModuleSpanningTree::~ModuleSpanningTree()
 {
-       delete ServerInstance->PI;
-       ServerInstance->PI = new ProtocolInterface;
+       ServerInstance->PI = &ServerInstance->DefaultProtocolInterface;
 
-       /* This will also free the listeners */
-       delete Utils;
+       Server* newsrv = new Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc);
+       SetLocalUsersServer(newsrv);
 
-       delete commands;
+       delete Utils;
 }
 
 Version ModuleSpanningTree::GetVersion()
@@ -998,12 +791,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)
index 80758763aa2b17822331499e61f241d99e6b9a0d..989fa1311244c26bb7304e61f10a51da4953b5d2 100644 (file)
  */
 
 
-#ifndef M_SPANNINGTREE_MAIN_H
-#define M_SPANNINGTREE_MAIN_H
+#pragma once
 
 #include "inspircd.h"
-#include <stdarg.h>
+#include "event.h"
+#include "modules/dns.h"
+#include "modules/ssl.h"
+#include "modules/stats.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.
  * Failure to document your protocol changes will result in a painfully
  * painful death by pain. You have been warned.
  */
-const long ProtocolVersion = 1202;
-const long MinCompatProtocol = 1201;
+const unsigned int ProtocolVersion = 1205;
+const unsigned int MinCompatProtocol = 1202;
 
 /** Forward declarations
  */
-class SpanningTreeCommands;
 class SpanningTreeUtilities;
 class CacheRefreshTimer;
 class TreeServer;
@@ -50,60 +54,69 @@ class Autoconnect;
 
 /** This is the main class for the spanningtree module
  */
-class ModuleSpanningTree : public Module
+class ModuleSpanningTree
+       : public Module
+       , public Away::EventListener
+       , public Stats::EventListener
 {
-       SpanningTreeCommands* commands;
+       /** Client to server commands, registered in the core
+        */
+       CommandRConnect rconnect;
+       CommandRSQuit rsquit;
+       CommandMap map;
+
+       /** Server to server only commands, not registered in the core
+        */
+       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;
+
+       /** API for accessing user SSL certificates. */
+       UserCertificateAPI sslapi;
 
  public:
-       SpanningTreeUtilities* Utils;
+       dynamic_reference<DNS::Manager> DNS;
+
+       /** Event provider for message tags. */
+       Events::ModuleEventProvider tagevprov;
+
+       ServerCommandManager CmdManager;
 
-       CacheRefreshTimer *RefreshTimer;
        /** Set to true if inside a spanningtree call, to prevent sending
         * xlines and other things back to their source
         */
        bool loopCall;
 
-       /** If true OnUserPostNick() won't update the nick TS before sending the NICK,
-        * used when handling SVSNICK.
-        */
-       bool KeepNickTS;
-
        /** Constructor
         */
        ModuleSpanningTree();
-       void init();
+       void init() CXX11_OVERRIDE;
 
        /** Shows /LINKS
         */
        void ShowLinks(TreeServer* Current, User* user, int hops);
 
-       /** Counts local and remote servers
-        */
-       int CountServs();
-
        /** Handle LINKS command
         */
-       void HandleLinks(const std::vector<std::string>& parameters, User* user);
-
-       /** Show MAP output to a user (recursive)
-        */
-       void ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats);
-
-       /** Handle MAP command
-        */
-       bool HandleMap(const std::vector<std::string>& parameters, User* user);
+       void HandleLinks(const CommandBase::Params& parameters, User* user);
 
        /** Handle SQUIT
         */
-       ModResult HandleSquit(const std::vector<std::string>& parameters, User* user);
+       ModResult HandleSquit(const CommandBase::Params& parameters, User* user);
 
        /** Handle remote WHOIS
         */
-       ModResult HandleRemoteWhois(const std::vector<std::string>& parameters, User* user);
-
-       /** Ping all local servers
-        */
-       void DoPingChecks(time_t curtime);
+       ModResult HandleRemoteWhois(const CommandBase::Params& parameters, User* user);
 
        /** Connect a server locally
         */
@@ -123,66 +136,52 @@ class ModuleSpanningTree : public Module
 
        /** Handle remote VERSON
         */
-       ModResult HandleVersion(const std::vector<std::string>& parameters, User* user);
+       ModResult HandleVersion(const CommandBase::Params& parameters, User* user);
 
        /** Handle CONNECT
         */
-       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);
-
-       /** Returns oper-specific MAP information
-        */
-       const std::string MapOperInfo(TreeServer* Current);
+       ModResult HandleConnect(const CommandBase::Params& parameters, User* user);
 
        /** Display a time as a human readable string
         */
-       std::string TimeToStr(time_t secs);
+       static std::string TimeToStr(time_t secs);
+
+       const Events::ModuleEventProvider& GetEventProvider() const { return eventprov; }
 
        /**
         ** *** MODULE EVENTS ***
         **/
 
-       ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line);
-       void OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line);
-       void OnGetServerDescription(const std::string &servername,std::string &description);
-       void OnUserConnect(LocalUser* source);
-       void OnUserInvite(User* source,User* dest,Channel* channel, time_t);
-       void OnPostTopicChange(User* user, Channel* chan, const std::string &topic);
-       void OnWallops(User* user, const std::string &text);
-       void OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
-       void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
-       void OnBackgroundTimer(time_t curtime);
-       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts);
-       void OnChangeHost(User* user, const std::string &newhost);
-       void OnChangeName(User* user, const std::string &gecos);
-       void OnChangeIdent(User* user, const std::string &ident);
-       void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts);
-       void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message);
-       void OnUserPostNick(User* user, const std::string &oldnick);
-       void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts);
-       void OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason);
-       void OnPreRehash(User* user, const std::string &parameter);
-       void OnRehash(User* user);
-       void OnOper(User* user, const std::string &opertype);
-       void OnLine(User* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason);
-       void OnAddLine(User *u, XLine *x);
-       void OnDelLine(User *u, XLine *x);
-       void OnMode(User* user, void* dest, int target_type, const std::vector<std::string> &text, const std::vector<TranslateType> &translate);
-       ModResult OnStats(char statschar, User* user, string_list &results);
-       ModResult OnSetAway(User* user, const std::string &awaymsg);
-       void ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const std::vector<std::string> &modeline, const std::vector<TranslateType> &translate);
-       void ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata);
-       void OnLoadModule(Module* mod);
-       void OnUnloadModule(Module* mod);
-       ModResult OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
-       void OnRequest(Request& request);
-       CullResult cull();
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE;
+       void OnPostCommand(Command*, const CommandBase::Params& parameters, LocalUser* user, CmdResult result, bool loop) CXX11_OVERRIDE;
+       void OnUserConnect(LocalUser* source) 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 OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE;
+       void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE;
+       void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE;
+       void OnChangeHost(User* user, const std::string &newhost) CXX11_OVERRIDE;
+       void OnChangeRealName(User* user, const std::string& real) CXX11_OVERRIDE;
+       void OnChangeIdent(User* user, const std::string &ident) CXX11_OVERRIDE;
+       void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE;
+       void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE;
+       void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE;
+       void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) CXX11_OVERRIDE;
+       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 OnAddLine(User *u, XLine *x) CXX11_OVERRIDE;
+       void OnDelLine(User *u, XLine *x) CXX11_OVERRIDE;
+       ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE;
+       void OnUserAway(User* user) CXX11_OVERRIDE;
+       void OnUserBack(User* user) 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 OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags) CXX11_OVERRIDE;
+       CullResult cull() CXX11_OVERRIDE;
        ~ModuleSpanningTree();
-       Version GetVersion();
-       void Prioritize();
+       Version GetVersion() CXX11_OVERRIDE;
+       void Prioritize() CXX11_OVERRIDE;
 };
-
-#endif
index a584f8fa8b6fcd8494d3c44537e8a324617e5184..52267c522923a7fb80ae49fac543f75385e2954e 100644 (file)
 #include "inspircd.h"
 #include "commands.h"
 
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
-CmdResult CommandMetadata::Handle(const std::vector<std::string>& params, User *srcuser)
+CmdResult CommandMetadata::Handle(User* srcuser, Params& params)
 {
-       std::string value = params.size() < 3 ? "" : params[2];
-       ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]);
        if (params[0] == "*")
        {
-               FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(NULL,params[1],value));
+               std::string value = params.size() < 3 ? "" : params[2];
+               FOREACH_MOD(OnDecodeMetaData, (NULL,params[1],value));
+               return CMD_SUCCESS;
        }
-       else if (*(params[0].c_str()) == '#')
+
+       if (params[0][0] == '#')
        {
+               // Channel METADATA has an additional parameter: the channel TS
+               // :22D METADATA #channel 12345 extname :extdata
+               if (params.size() < 3)
+                       throw ProtocolException("Insufficient parameters for channel METADATA");
+
                Channel* c = ServerInstance->FindChan(params[0]);
-               if (c)
-               {
-                       if (item)
-                               item->unserialize(FORMAT_NETWORK, c, value);
-                       FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(c,params[1],value));
-               }
+               if (!c)
+                       return CMD_FAILURE;
+
+               time_t ChanTS = ServerCommand::ExtractTS(params[1]);
+               if (c->age < ChanTS)
+                       // Their TS is newer than ours, discard this command and do not propagate
+                       return CMD_FAILURE;
+
+               std::string value = params.size() < 4 ? "" : params[3];
+
+               ExtensionItem* item = ServerInstance->Extensions.GetItem(params[2]);
+               if ((item) && (item->type == ExtensionItem::EXT_CHANNEL))
+                       item->unserialize(FORMAT_NETWORK, c, value);
+               FOREACH_MOD(OnDecodeMetaData, (c,params[2],value));
        }
-       else if (*(params[0].c_str()) != '#')
+       else
        {
                User* u = ServerInstance->FindUUID(params[0]);
-               if ((u) && (!IS_SERVER(u)))
+               if (u)
                {
-                       if (item)
+                       ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]);
+                       std::string value = params.size() < 3 ? "" : params[2];
+
+                       if ((item) && (item->type == ExtensionItem::EXT_USER))
                                item->unserialize(FORMAT_NETWORK, u, value);
-                       FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(u,params[1],value));
+                       FOREACH_MOD(OnDecodeMetaData, (u,params[1],value));
                }
        }
 
        return CMD_SUCCESS;
 }
 
+CommandMetadata::Builder::Builder(User* user, const std::string& key, const std::string& val)
+       : CmdBuilder("METADATA")
+{
+       push(user->uuid);
+       push(key);
+       push_last(val);
+}
+
+CommandMetadata::Builder::Builder(Channel* chan, const std::string& key, const std::string& val)
+       : CmdBuilder("METADATA")
+{
+       push(chan->name);
+       push_int(chan->age);
+       push(key);
+       push_last(val);
+}
+
+CommandMetadata::Builder::Builder(const std::string& key, const std::string& val)
+       : CmdBuilder("METADATA")
+{
+       push("*");
+       push(key);
+       push_last(val);
+}
diff --git a/src/modules/m_spanningtree/misccommands.cpp b/src/modules/m_spanningtree/misccommands.cpp
new file mode 100644 (file)
index 0000000..8fc1b17
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2007-2008, 2012 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ *   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/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "main.h"
+#include "commands.h"
+#include "treeserver.h"
+
+CmdResult CommandSNONotice::Handle(User* user, Params& params)
+{
+       ServerInstance->SNO->WriteToSnoMask(params[0][0], "From " + user->nick + ": " + params[1]);
+       return CMD_SUCCESS;
+}
+
+CmdResult CommandEndBurst::HandleServer(TreeServer* server, Params& params)
+{
+       server->FinishBurst();
+       return CMD_SUCCESS;
+}
index 3bce90eda30895bcb1be9cf67d013ffcfe607e41..ed15591e9027e4267be29c977eca01e5ceaecc26 100644 (file)
 
 #include "inspircd.h"
 #include "xline.h"
+#include "listmode.h"
 
 #include "treesocket.h"
 #include "treeserver.h"
-#include "utils.h"
 #include "main.h"
+#include "commands.h"
+#include "modules/server.h"
+
+/**
+ * Creates FMODE messages, used only when syncing channels
+ */
+class FModeBuilder : public CmdBuilder
+{
+       static const size_t maxline = 480;
+       std::string params;
+       unsigned int modes;
+       std::string::size_type startpos;
+
+ public:
+       FModeBuilder(Channel* chan)
+               : CmdBuilder("FMODE"), modes(0)
+       {
+               push(chan->name).push_int(chan->age).push_raw(" +");
+               startpos = str().size();
+       }
+
+       /** Add a mode to the message
+        */
+       void push_mode(const char modeletter, const std::string& mask)
+       {
+               push_raw(modeletter);
+               params.push_back(' ');
+               params.append(mask);
+               modes++;
+       }
+
+       /** Remove all modes from the message
+        */
+       void clear()
+       {
+               content.erase(startpos);
+               params.clear();
+               modes = 0;
+       }
+
+       /** Prepare the message for sending, next mode can only be added after clear()
+        */
+       const std::string& finalize()
+       {
+               return push_raw(params);
+       }
+
+       /** Returns true if the given mask can be added to the message, false if the message
+        * has no room for the mask
+        */
+       bool has_room(const std::string& mask) const
+       {
+               return ((str().size() + params.size() + mask.size() + 2 <= maxline) &&
+                               (modes < ServerInstance->Config->Limits.MaxModes));
+       }
+
+       /** Returns true if this message is empty (has no modes)
+        */
+       bool empty() const
+       {
+               return (modes == 0);
+       }
+};
+
+struct TreeSocket::BurstState
+{
+       SpanningTreeProtocolInterface::Server server;
+       BurstState(TreeSocket* sock) : server(sock) { }
+};
 
 /** This function is called when we want to send a netburst to a local
  * server. There is a set order we must do this, because for example
  */
 void TreeSocket::DoBurst(TreeServer* s)
 {
-       std::string servername = s->GetName();
-       ServerInstance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s%s).",
-               servername.c_str(),
-               capab->auth_fingerprint ? "SSL Fingerprint and " : "",
+       ServerInstance->SNO->WriteToSnoMask('l',"Bursting to \002%s\002 (Authentication: %s%s).",
+               s->GetName().c_str(),
+               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->SendServers(Utils->TreeRoot,s,1);
-       /* Send users and their oper status */
-       this->SendUsers();
-       /* Send everything else (channel modes, xlines etc) */
-       this->SendChannelModes();
+       this->WriteLine(CmdBuilder("BURST").push_int(ServerInstance->Time()));
+       // Introduce all servers behind us
+       this->SendServers(Utils->TreeRoot, s);
+
+       BurstState bs(this);
+       // 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(I_OnSyncNetwork,OnSyncNetwork(Utils->Creator,(void*)this));
-       this->WriteLine(":" + ServerInstance->Config->GetSID() + " ENDBURST");
-       ServerInstance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+ s->GetName()+"\2.");
+       FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), ServerEventListener, OnSyncNetwork, (bs.server));
+       this->WriteLine(CmdBuilder("ENDBURST"));
+       ServerInstance->SNO->WriteToSnoMask('l',"Finished bursting to \002"+ s->GetName()+"\002.");
+
+       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()));
+
+       // Send the raw version string that just contains the base info
+       this->WriteLine(CommandSInfo::Builder(from, "rawversion", from->GetRawVersion()));
 }
 
-/** Recursively send the server tree with distances as hops.
+/** Recursively send the server tree.
  * This is used during network burst to inform the other server
  * (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 hopcounts given
- * by this function are relative, this doesn't matter so long as
- * they are all >1, as all the remote servers re-calculate them
- * to be relative too, with themselves as hop 0.
+ * end, our connection may be terminated.
  */
-void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops)
+void TreeSocket::SendServers(TreeServer* Current, TreeServer* s)
 {
-       char command[MAXBUF];
-       for (unsigned int q = 0; q < Current->ChildCount(); q++)
+       SendServerInfo(Current);
+
+       const TreeServer::ChildServers& children = Current->GetChildren();
+       for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
        {
-               TreeServer* recursive_server = Current->GetChild(q);
+               TreeServer* recursive_server = *i;
                if (recursive_server != s)
                {
-                       std::string recursive_servername = recursive_server->GetName();
-                       snprintf(command, MAXBUF, ":%s SERVER %s * %d %s :%s", Current->GetID().c_str(), recursive_servername.c_str(), hops,
-                                       recursive_server->GetID().c_str(),
-                                       recursive_server->GetDesc().c_str());
-                       this->WriteLine(command);
-                       this->WriteLine(":"+recursive_server->GetID()+" VERSION :"+recursive_server->GetVersion());
+                       this->WriteLine(CommandServer::Builder(recursive_server));
                        /* down to next level */
-                       this->SendServers(recursive_server, s, hops+1);
+                       this->SendServers(recursive_server, 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.
+ * If the length of a single line is too long, it is split over multiple lines.
  */
 void TreeSocket::SendFJoins(Channel* c)
 {
-       std::string buffer;
-       char list[MAXBUF];
-
-       size_t curlen, headlen;
-       curlen = headlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu +%s :",
-               ServerInstance->Config->GetSID().c_str(), c->name.c_str(), (unsigned long)c->age, c->ChanModes(true));
-       int numusers = 0;
-       char* ptr = list + curlen;
-       bool looped_once = false;
-
-       const UserMembList *ulist = c->GetUsers();
-       std::string modes;
-       std::string params;
+       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)
        {
-               size_t ptrlen = 0;
-               std::string modestr = i->second->modes;
-
-               if ((curlen + modestr.length() + i->first->uuid.length() + 4) > 480)
+               Membership* memb = i->second;
+               if (!fjoin.has_room(memb))
                {
-                       // remove the final space
-                       if (ptr[-1] == ' ')
-                               ptr[-1] = '\0';
-                       buffer.append(list).append("\r\n");
-                       curlen = headlen;
-                       ptr = list + headlen;
-                       numusers = 0;
+                       // No room for this user, send the line and prepare a new one
+                       this->WriteLine(fjoin.finalize());
+                       fjoin.clear();
                }
-
-               ptrlen = snprintf(ptr, MAXBUF-curlen, "%s,%s ", modestr.c_str(), i->first->uuid.c_str());
-
-               looped_once = true;
-
-               curlen += ptrlen;
-               ptr += ptrlen;
-
-               numusers++;
+               fjoin.add(memb);
        }
-
-       // Okay, permanent channels will (of course) need this \r\n anyway, numusers check is if there
-       // actually were people in the channel (looped_once == true)
-       if (!looped_once || numusers > 0)
-       {
-               // remove the final space
-               if (ptr[-1] == ' ')
-                       ptr[-1] = '\0';
-               buffer.append(list).append("\r\n");
-       }
-
-       unsigned int linesize = 1;
-       for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
-       {
-               unsigned int size = b->data.length() + 2; // "b" and " "
-               unsigned int nextsize = linesize + size;
-
-               if ((modes.length() >= ServerInstance->Config->Limits.MaxModes) || (nextsize > FMODE_MAX_LENGTH))
-               {
-                       /* Wrap */
-                       buffer.append(":").append(ServerInstance->Config->GetSID()).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params).append("\r\n");
-
-                       modes.clear();
-                       params.clear();
-                       linesize = 1;
-               }
-
-               modes.push_back('b');
-
-               params.push_back(' ');
-               params.append(b->data);
-
-               linesize += size;
-       }
-
-       /* Only send these if there are any */
-       if (!modes.empty())
-               buffer.append(":").append(ServerInstance->Config->GetSID()).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params);
-
-       this->WriteLine(buffer);
+       this->WriteLine(fjoin.finalize());
 }
 
 /** Send all XLines we know about */
 void TreeSocket::SendXLines()
 {
-       char data[MAXBUF];
-       std::string n = ServerInstance->Config->GetSID();
-       const char* sn = n.c_str();
-
        std::vector<std::string> types = ServerInstance->XLines->GetAllTypes();
-       time_t current = ServerInstance->Time();
 
-       for (std::vector<std::string>::iterator it = types.begin(); it != types.end(); ++it)
+       for (std::vector<std::string>::const_iterator it = types.begin(); it != types.end(); ++it)
        {
+               /* Expired lines are removed in XLineManager::GetAll() */
                XLineLookup* lookup = ServerInstance->XLines->GetAll(*it);
 
+               /* lookup cannot be NULL in this case but a check won't hurt */
                if (lookup)
                {
                        for (LookupIter i = lookup->begin(); i != lookup->end(); ++i)
@@ -195,96 +208,99 @@ void TreeSocket::SendXLines()
                                if (!i->second->IsBurstable())
                                        break;
 
-                               /* If it's expired, don't bother to burst it
-                                */
-                               if (i->second->duration && current > i->second->expiry)
-                                       continue;
-
-                               snprintf(data,MAXBUF,":%s ADDLINE %s %s %s %lu %lu :%s",sn, it->c_str(), i->second->Displayable(),
-                                               i->second->source.c_str(),
-                                               (unsigned long)i->second->set_time,
-                                               (unsigned long)i->second->duration,
-                                               i->second->reason.c_str());
-                               this->WriteLine(data);
+                               this->WriteLine(CommandAddLine::Builder(i->second));
                        }
                }
        }
 }
 
-/** Send channel topic, modes and metadata */
-void TreeSocket::SendChannelModes()
+void TreeSocket::SendListModes(Channel* chan)
 {
-       char data[MAXBUF];
-       std::string n = ServerInstance->Config->GetSID();
-       const char* sn = n.c_str();
-
-       for (chan_hash::iterator c = ServerInstance->chanlist->begin(); c != ServerInstance->chanlist->end(); c++)
+       FModeBuilder fmode(chan);
+       const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+       for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
        {
-               SendFJoins(c->second);
-               if (!c->second->topic.empty())
+               ListModeBase* mh = *i;
+               ListModeBase::ModeList* list = mh->GetList(chan);
+               if (!list)
+                       continue;
+
+               // Add all items on the list to the FMODE, send it whenever it becomes too long
+               const char modeletter = mh->GetModeChar();
+               for (ListModeBase::ModeList::const_iterator j = list->begin(); j != list->end(); ++j)
                {
-                       snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s", sn, c->second->name.c_str(), (unsigned long)c->second->topicset, c->second->setby.c_str(), c->second->topic.c_str());
-                       this->WriteLine(data);
+                       const std::string& mask = j->mask;
+                       if (!fmode.has_room(mask))
+                       {
+                               // No room for this mask, send the current line as-is then add the mask to a
+                               // new, empty FMODE message
+                               this->WriteLine(fmode.finalize());
+                               fmode.clear();
+                       }
+                       fmode.push_mode(modeletter, mask);
                }
+       }
 
-               for(Extensible::ExtensibleStore::const_iterator i = c->second->GetExtList().begin(); i != c->second->GetExtList().end(); i++)
-               {
-                       ExtensionItem* item = i->first;
-                       std::string value = item->serialize(FORMAT_NETWORK, c->second, i->second);
-                       if (!value.empty())
-                               Utils->Creator->ProtoSendMetaData(this, c->second, item->name, value);
-               }
+       if (!fmode.empty())
+               this->WriteLine(fmode.finalize());
+}
+
+/** Send channel users, topic, modes and global metadata */
+void TreeSocket::SyncChannel(Channel* chan, BurstState& bs)
+{
+       SendFJoins(chan);
 
-               FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(c->second,Utils->Creator,this));
+       // If the topic was ever set, send it, even if it's empty now
+       // because a new empty topic should override an old non-empty topic
+       if (chan->topicset != 0)
+               this->WriteLine(CommandFTopic::Builder(chan));
+
+       SendListModes(chan);
+
+       for (Extensible::ExtensibleStore::const_iterator i = chan->GetExtList().begin(); i != chan->GetExtList().end(); i++)
+       {
+               ExtensionItem* item = i->first;
+               std::string value = item->serialize(FORMAT_NETWORK, chan, i->second);
+               if (!value.empty())
+                       this->WriteLine(CommandMetadata::Builder(chan, item->name, value));
        }
+
+       FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), ServerEventListener, OnSyncChannel, (chan, bs.server));
 }
 
-/** send all users and their oper state/modes */
-void TreeSocket::SendUsers()
+void TreeSocket::SyncChannel(Channel* chan)
 {
-       char data[MAXBUF];
-       for (user_hash::iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); u++)
+       BurstState bs(this);
+       SyncChannel(chan, bs);
+}
+
+/** Send all users and their state, including oper and away status and global metadata */
+void TreeSocket::SendUsers(BurstState& bs)
+{
+       const user_hash& users = ServerInstance->Users->GetUsers();
+       for (user_hash::const_iterator u = users.begin(); u != users.end(); ++u)
        {
-               if (u->second->registered == REG_ALL)
-               {
-                       TreeServer* theirserver = Utils->FindServer(u->second->server);
-                       if (theirserver)
-                       {
-                               snprintf(data,MAXBUF,":%s UID %s %lu %s %s %s %s %s %lu +%s :%s",
-                                               theirserver->GetID().c_str(),   /* Prefix: SID */
-                                               u->second->uuid.c_str(),        /* 0: UUID */
-                                               (unsigned long)u->second->age,  /* 1: TS */
-                                               u->second->nick.c_str(),        /* 2: Nick */
-                                               u->second->host.c_str(),        /* 3: Displayed Host */
-                                               u->second->dhost.c_str(),       /* 4: Real host */
-                                               u->second->ident.c_str(),       /* 5: Ident */
-                                               u->second->GetIPString(),       /* 6: IP string */
-                                               (unsigned long)u->second->signon, /* 7: Signon time for WHOWAS */
-                                               u->second->FormatModes(true),   /* 8...n: Modes and params */
-                                               u->second->fullname.c_str());   /* size-1: GECOS */
-                               this->WriteLine(data);
-                               if (IS_OPER(u->second))
-                               {
-                                       snprintf(data,MAXBUF,":%s OPERTYPE %s", u->second->uuid.c_str(), u->second->oper->name.c_str());
-                                       this->WriteLine(data);
-                               }
-                               if (IS_AWAY(u->second))
-                               {
-                                       snprintf(data,MAXBUF,":%s AWAY %ld :%s", u->second->uuid.c_str(), (long)u->second->awaytime, u->second->awaymsg.c_str());
-                                       this->WriteLine(data);
-                               }
-                       }
+               User* user = u->second;
+               if (user->registered != REG_ALL)
+                       continue;
 
-                       for(Extensible::ExtensibleStore::const_iterator i = u->second->GetExtList().begin(); i != u->second->GetExtList().end(); i++)
-                       {
-                               ExtensionItem* item = i->first;
-                               std::string value = item->serialize(FORMAT_NETWORK, u->second, i->second);
-                               if (!value.empty())
-                                       Utils->Creator->ProtoSendMetaData(this, u->second, item->name, value);
-                       }
+               this->WriteLine(CommandUID::Builder(user));
+
+               if (user->IsOper())
+                       this->WriteLine(CommandOpertype::Builder(user));
+
+               if (user->IsAway())
+                       this->WriteLine(CommandAway::Builder(user));
 
-                       FOREACH_MOD(I_OnSyncUser,OnSyncUser(u->second,Utils->Creator,this));
+               const Extensible::ExtensibleStore& exts = user->GetExtList();
+               for (Extensible::ExtensibleStore::const_iterator i = exts.begin(); i != exts.end(); ++i)
+               {
+                       ExtensionItem* item = i->first;
+                       std::string value = item->serialize(FORMAT_NETWORK, u->second, i->second);
+                       if (!value.empty())
+                               this->WriteLine(CommandMetadata::Builder(user, item->name, value));
                }
+
+               FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), ServerEventListener, OnSyncUser, (user, bs.server));
        }
 }
-
diff --git a/src/modules/m_spanningtree/nick.cpp b/src/modules/m_spanningtree/nick.cpp
new file mode 100644 (file)
index 0000000..4f53941
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2007-2008, 2012 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ *   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/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "main.h"
+#include "utils.h"
+#include "commands.h"
+#include "treeserver.h"
+
+CmdResult CommandNick::HandleRemote(::RemoteUser* user, Params& params)
+{
+       if ((isdigit(params[0][0])) && (params[0] != user->uuid))
+               throw ProtocolException("Attempted to change nick to an invalid or non-matching UUID");
+
+       // 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) && (x->registered == REG_ALL))
+       {
+               // '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, 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->ChangeNick(params[0], newts);
+
+       return CMD_SUCCESS;
+}
index 38d59affb401e530fa5daddf722e25b595a22461..62e200921eb8f92699346b31d4b4f16080d06a76 100644 (file)
 
 
 #include "inspircd.h"
-#include "xline.h"
 
 #include "treesocket.h"
 #include "treeserver.h"
 #include "utils.h"
-
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.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 TreeSocket::DoCollision(User *u, 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.
@@ -56,21 +58,14 @@ int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remotei
        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)
+       // 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)
        {
-               /* equal. fuck them both! do nada, let the handler at the bottom figure this out. */
-       }
-       else
-       {
-               /* 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);
 
@@ -81,19 +76,22 @@ int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remotei
                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
@@ -107,38 +105,23 @@ int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remotei
        {
                /*
                 * 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.
                 */
-               parameterlist params;
+               CmdBuilder params("SAVE");
                params.push_back(u->uuid);
                params.push_back(ConvToStr(u->age));
-               Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params);
-
-               u->ForceNickChange(u->uuid.c_str());
+               params.Broadcast();
 
-               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.
                 */
-               WriteLine(":"+ServerInstance->Config->GetSID()+" SAVE "+remoteuid+" "+ ConvToStr(remotets));
-
-               if (remote)
-               {
-                       /* nick change collide. Force change their nick. */
-                       remote->ForceNickChange(remoteuid.c_str());
-               }
-
-               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 (file)
index 0000000..564b808
--- /dev/null
@@ -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, CommandBase::Params& 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(ConvToNum<unsigned int>(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 Params& 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 CommandBase::Params& params = numeric.GetParams();
+       if (!params.empty())
+       {
+               for (CommandBase::Params::const_iterator i = params.begin(); i != params.end()-1; ++i)
+                       push(*i);
+               push_last(params.back());
+       }
+}
diff --git a/src/modules/m_spanningtree/operquit.cpp b/src/modules/m_spanningtree/operquit.cpp
deleted file mode 100644 (file)
index af2e04e..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 "xline.h"
-
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-
-bool TreeSocket::OperQuit(const std::string &prefix, parameterlist &params)
-{
-       if (params.size() < 1)
-               return true;
-
-       User* u = ServerInstance->FindUUID(prefix);
-
-       if ((u) && (!IS_SERVER(u)))
-       {
-               ServerInstance->OperQuit.set(u, params[0]);
-               params[0] = ":" + params[0];
-               Utils->DoOneToAllButSender(prefix,"OPERQUIT",params,prefix);
-       }
-       return true;
-}
-
index 97a4de8c20ee5fc53f6534c691dd0c8ced78eaa7..692588b5e360ed2d79eef5abce279c7d2fcf710c 100644 (file)
 /** Because the core won't let users or even SERVERS set +o,
  * we use the OPERTYPE command to do this.
  */
-CmdResult CommandOpertype::Handle(const std::vector<std::string>& params, User *u)
+CmdResult CommandOpertype::HandleRemote(RemoteUser* u, CommandBase::Params& params)
 {
-       SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
-       std::string opertype = params[0];
-       if (!IS_OPER(u))
+       const std::string& opertype = params[0];
+       if (!u->IsOper())
                ServerInstance->Users->all_opers.push_back(u);
-       u->modes[UM_OPERATOR] = 1;
-       OperIndex::iterator iter = ServerInstance->Config->oper_blocks.find(" " + opertype);
-       if (iter != ServerInstance->Config->oper_blocks.end())
+
+       ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
+       if (opermh)
+               u->SetMode(opermh, true);
+
+       ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->OperTypes.find(opertype);
+       if (iter != ServerInstance->Config->OperTypes.end())
                u->oper = iter->second;
        else
-       {
-               u->oper = new OperInfo;
-               u->oper->name = opertype;
-       }
+               u->oper = new OperInfo(opertype);
 
        if (Utils->quiet_bursts)
        {
@@ -48,12 +48,17 @@ CmdResult CommandOpertype::Handle(const std::vector<std::string>& params, User *
                 * If quiet bursts are enabled, and server is bursting or silent uline (i.e. services),
                 * then do nothing. -- w00t
                 */
-               TreeServer* remoteserver = Utils->FindServer(u->server);
-               if (remoteserver->bursting || ServerInstance->SilentULine(u->server))
+               TreeServer* remoteserver = TreeServer::Get(u);
+               if (remoteserver->IsBehindBursting() || remoteserver->IsSilentULine())
                        return CMD_SUCCESS;
        }
 
-       ServerInstance->SNO->WriteToSnoMask('O',"From %s: User %s (%s@%s) is now an IRC operator of type %s",u->server.c_str(), u->nick.c_str(),u->ident.c_str(), u->host.c_str(), irc::Spacify(opertype.c_str()));
+       ServerInstance->SNO->WriteToSnoMask('O', "From %s: User %s (%s@%s) is now a server operator of type %s", u->server->GetName().c_str(), u->nick.c_str(),u->ident.c_str(), u->GetRealHost().c_str(), opertype.c_str());
        return CMD_SUCCESS;
 }
 
+CommandOpertype::Builder::Builder(User* user)
+       : CmdBuilder(user, "OPERTYPE")
+{
+       push_last(user->oper->name);
+}
index 04fa4bcab31b53a61aaaab3b6b5e9abd3c25c102..693b07bade23e478818fd7f71c2b03beb57f993e 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2014 Adam <Adam@anope.org>
  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
  *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
  */
 
 
-/* $ModDesc: Provides a spanning tree server link protocol */
-
 #include "inspircd.h"
 
 #include "main.h"
 #include "utils.h"
 #include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
 
-const std::string ModuleSpanningTree::MapOperInfo(TreeServer* Current)
+CommandMap::CommandMap(Module* Creator)
+       : Command(Creator, "MAP", 0, 1)
 {
-       time_t secs_up = ServerInstance->Time() - Current->age;
-       return " [Up: " + TimeToStr(secs_up) + (Current->rtt == 0 ? "]" : " Lag: " + ConvToStr(Current->rtt) + "ms]");
+       Penalty = 2;
 }
 
-void ModuleSpanningTree::ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats)
+static inline bool IsHidden(User* user, TreeServer* server)
 {
-       ServerInstance->Logs->Log("map",DEBUG,"ShowMap depth %d on line %d", depth, line);
-       float percent;
-
-       if (ServerInstance->Users->clientlist->size() == 0)
+       if (!user->IsOper())
        {
-               // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
-               percent = 0;
+               if (server->Hidden)
+                       return true;
+               if (Utils->HideULines && server->IsULine())
+                       return true;
        }
-       else
+
+       return false;
+}
+
+// Calculate the map depth the servers go, and the longest server name
+static void GetDepthAndLen(TreeServer* current, unsigned int depth, unsigned int& max_depth, unsigned int& max_len)
+{
+       if (depth > max_depth)
+               max_depth = depth;
+       if (current->GetName().length() > max_len)
+               max_len = current->GetName().length();
+
+       const TreeServer::ChildServers& servers = current->GetChildren();
+       for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i)
        {
-               percent = Current->GetUserCount() * 100.0 / ServerInstance->Users->clientlist->size();
+               TreeServer* child = *i;
+               GetDepthAndLen(child, depth + 1, max_depth, max_len);
        }
+}
 
-       const std::string operdata = IS_OPER(user) ? MapOperInfo(Current) : "";
-
-       char* myname = names + 100 * line;
-       char* mystat = stats + 50 * line;
-       memset(myname, ' ', depth);
-       int w = depth;
+static std::vector<std::string> GetMap(User* user, TreeServer* current, unsigned int max_len, unsigned int depth)
+{
+       float percent = 0;
 
-       std::string servername = Current->GetName();
-       if (IS_OPER(user))
+       const user_hash& users = ServerInstance->Users->GetUsers();
+       if (!users.empty())
        {
-               w += snprintf(myname + depth, 99 - depth, "%s (%s)", servername.c_str(), Current->GetID().c_str());
+               // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
+               percent = current->UserCount * 100.0 / users.size();
        }
-       else
+
+       std::string buffer = current->GetName();
+       if (user->IsOper())
        {
-               w += snprintf(myname + depth, 99 - depth, "%s", servername.c_str());
+               buffer += " (" + current->GetID();
+
+               const std::string& cur_vers = current->GetRawVersion();
+               if (!cur_vers.empty())
+                       buffer += " " + cur_vers;
+
+               buffer += ")";
        }
-       memset(myname + w, ' ', 100 - w);
-       if (w > maxnamew)
-               maxnamew = w;
-       snprintf(mystat, 49, "%5d [%5.2f%%]%s", Current->GetUserCount(), percent, operdata.c_str());
 
-       line++;
+       // Pad with spaces until its at max len, max_len must always be >= my names length
+       buffer.append(max_len - current->GetName().length(), ' ');
 
-       if (IS_OPER(user) || !Utils->FlatLinks)
-               depth = depth + 2;
-       for (unsigned int q = 0; q < Current->ChildCount(); q++)
+       buffer += InspIRCd::Format("%5d [%5.2f%%]", current->UserCount, percent);
+
+       if (user->IsOper())
        {
-               TreeServer* child = Current->GetChild(q);
-               if (!IS_OPER(user)) {
-                       if (child->Hidden)
-                               continue;
-                       if ((Utils->HideULines) && (ServerInstance->ULine(child->GetName())))
-                               continue;
-               }
-               ShowMap(child, user, depth, line, names, maxnamew, stats);
+               time_t secs_up = ServerInstance->Time() - current->age;
+               buffer += " [Up: " + ModuleSpanningTree::TimeToStr(secs_up) + (current->rtt == 0 ? "]" : " Lag: " + ConvToStr(current->rtt) + "ms]");
        }
-}
 
+       std::vector<std::string> map;
+       map.push_back(buffer);
 
-// Ok, prepare to be confused.
-// After much mulling over how to approach this, it struck me that
-// the 'usual' way of doing a /MAP isnt the best way. Instead of
-// keeping track of a ton of ascii characters, and line by line
-// under recursion working out where to place them using multiplications
-// and divisons, we instead render the map onto a backplane of characters
-// (a character matrix), then draw the branches as a series of "L" shapes
-// from the nodes. This is not only friendlier on CPU it uses less stack.
-bool ModuleSpanningTree::HandleMap(const std::vector<std::string>& parameters, User* user)
-{
-       if (parameters.size() > 0)
+       const TreeServer::ChildServers& servers = current->GetChildren();
+       for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i)
        {
-               /* Remote MAP, the server is within the 1st parameter */
-               TreeServer* s = Utils->FindServerMask(parameters[0]);
-               bool ret = false;
-               if (!s)
+               TreeServer* child = *i;
+
+               if (IsHidden(user, child))
+                       continue;
+
+               bool last = true;
+               for (TreeServer::ChildServers::const_iterator j = i + 1; last && j != servers.end(); ++j)
+                       if (!IsHidden(user, *j))
+                               last = false;
+
+               unsigned int next_len;
+
+               if (user->IsOper() || !Utils->FlatLinks)
                {
-                       user->WriteNumeric(ERR_NOSUCHSERVER, "%s %s :No such server", user->nick.c_str(), parameters[0].c_str());
-                       ret = true;
+                       // This child is indented by us, so remove the depth from the max length to align the users properly
+                       next_len = max_len - 2;
                }
-               else if (s && s != Utils->TreeRoot)
+               else
                {
-                       parameterlist params;
-                       params.push_back(parameters[0]);
-
-                       params[0] = s->GetName();
-                       Utils->DoOneToOne(user->uuid, "MAP", params, s->GetName());
-                       ret = true;
+                       // This user can not see depth, so max_len remains constant
+                       next_len = max_len;
                }
 
-               // Don't return if s == Utils->TreeRoot (us)
-               if (ret)
-                       return true;
-       }
+               // Build the map for this child
+               std::vector<std::string> child_map = GetMap(user, child, next_len, depth + 1);
 
-       // These arrays represent a virtual screen which we will
-       // "scratch" draw to, as the console device of an irc
-       // client does not provide for a proper terminal.
-       int totusers = ServerInstance->Users->clientlist->size();
-       int totservers = this->CountServs();
-       int maxnamew = 0;
-       int line = 0;
-       char* names = new char[totservers * 100];
-       char* stats = new char[totservers * 50];
-
-       // The only recursive bit is called here.
-       ShowMap(Utils->TreeRoot,user,0,line,names,maxnamew,stats);
-
-       // Process each line one by one.
-       for (int l = 1; l < line; l++)
-       {
-               char* myname = names + 100 * l;
-               // scan across the line looking for the start of the
-               // servername (the recursive part of the algorithm has placed
-               // the servers at indented positions depending on what they
-               // are related to)
-               int first_nonspace = 0;
-
-               while (myname[first_nonspace] == ' ')
+               for (std::vector<std::string>::const_iterator j = child_map.begin(); j != child_map.end(); ++j)
                {
-                       first_nonspace++;
+                       const char* prefix;
+
+                       if (user->IsOper() || !Utils->FlatLinks)
+                       {
+                               // If this server is not the root child
+                               if (j != child_map.begin())
+                               {
+                                       // If this child is not my last child, then add |
+                                       // to be able to "link" the next server in my list to me, and to indent this childs servers
+                                       if (!last)
+                                               prefix = "| ";
+                                       // Otherwise this is my last child, so just use a space as theres nothing else linked to me below this
+                                       else
+                                               prefix = "  ";
+                               }
+                               // If we get here, this server must be the root child
+                               else
+                               {
+                                       // If this is the last child, it gets a `-
+                                       if (last)
+                                               prefix = "`-";
+                                       // Otherwise this isn't the last child, so it gets |-
+                                       else
+                                               prefix = "|-";
+                               }
+                       }
+                       else
+                               // User can't see depth, so use no prefix
+                               prefix = "";
+
+                       // Add line to the map
+                       map.push_back(prefix + *j);
                }
+       }
 
-               first_nonspace--;
-
-               // Draw the `- (corner) section: this may be overwritten by
-               // another L shape passing along the same vertical pane, becoming
-               // a |- (branch) section instead.
-
-               myname[first_nonspace] = '-';
-               myname[first_nonspace-1] = '`';
-               int l2 = l - 1;
+       return map;
+}
 
-               // Draw upwards until we hit the parent server, causing possibly
-               // other corners (`-) to become branches (|-)
-               while ((names[l2 * 100 + first_nonspace-1] == ' ') || (names[l2 * 100 + first_nonspace-1] == '`'))
+CmdResult CommandMap::Handle(User* user, const Params& parameters)
+{
+       if (parameters.size() > 0)
+       {
+               // Remote MAP, the target server is the 1st parameter
+               TreeServer* s = Utils->FindServerMask(parameters[0]);
+               if (!s)
                {
-                       names[l2 * 100 + first_nonspace-1] = '|';
-                       l2--;
+                       user->WriteNumeric(ERR_NOSUCHSERVER, parameters[0], "No such server");
+                       return CMD_FAILURE;
                }
+
+               if (!s->IsRoot())
+                       return CMD_SUCCESS;
        }
 
-       float avg_users = totusers * 1.0 / line;
+       // Max depth and max server name length
+       unsigned int max_depth = 0;
+       unsigned int max_len = 0;
+       GetDepthAndLen(Utils->TreeRoot, 0, max_depth, max_len);
 
-       ServerInstance->Logs->Log("map",DEBUG,"local");
-       for (int t = 0; t < line; t++)
+       unsigned int max;
+       if (user->IsOper() || !Utils->FlatLinks)
        {
-               // terminate the string at maxnamew characters
-               names[100 * t + maxnamew] = '\0';
-               user->SendText(":%s %03d %s :%s %s", ServerInstance->Config->ServerName.c_str(),
-                       RPL_MAP, user->nick.c_str(), names + 100 * t, stats + 50 * t);
+               // Each level of the map is indented by 2 characters, making the max possible line (max_depth * 2) + max_len
+               max = (max_depth * 2) + max_len;
        }
-       user->SendText(":%s %03d %s :%d server%s and %d user%s, average %.2f users per server",
-               ServerInstance->Config->ServerName.c_str(), RPL_MAPUSERS, user->nick.c_str(),
-               line, (line > 1 ? "s" : ""), 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());
+       else
+       {
+               // This user can't see any depth
+               max = max_len;
+       }
+
+       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->WriteRemoteNumeric(RPL_MAP, *i);
 
-       delete[] names;
-       delete[] stats;
+       size_t totusers = ServerInstance->Users->GetUsers().size();
+       float avg_users = (float) totusers / Utils->serverlist.size();
 
-       return true;
+       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;
 }
 
+RouteDescriptor CommandMap::GetRouting(User* user, const Params& parameters)
+{
+       if (!parameters.empty())
+               return ROUTE_UNICAST(parameters[0]);
+       return ROUTE_LOCALONLY;
+}
index 7d01c8149bf62fbf5b2e0d0d5e633a6df40b68c4..eb224660d58a6d00335d383ba095fd672c74bf70 100644 (file)
  */
 
 
-/* $ModDesc: Provides a spanning tree server link protocol */
-
 #include "inspircd.h"
 #include "socket.h"
-#include "xline.h"
 
 #include "main.h"
 #include "utils.h"
 #include "treeserver.h"
 #include "treesocket.h"
 
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-ModResult ModuleSpanningTree::HandleSquit(const std::vector<std::string>& parameters, User* user)
+ModResult ModuleSpanningTree::HandleSquit(const CommandBase::Params& parameters, User* user)
 {
        TreeServer* s = Utils->FindServerMask(parameters[0]);
        if (s)
        {
-               if (s == Utils->TreeRoot)
+               if (s->IsRoot())
                {
-                       user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick.c_str(),parameters[0].c_str());
+                       user->WriteNotice("*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (" + parameters[0] + " matches local server name)");
                        return MOD_RES_DENY;
                }
 
-               TreeSocket* sock = s->GetSocket();
-
-               if (sock)
+               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());
-                       ServerInstance->SE->DelFd(sock);
-                       sock->Close();
+                       s->SQuit("Server quit by " + user->GetFullRealHost());
                }
                else
                {
-                       user->WriteServ("NOTICE %s :*** SQUIT may not be used to remove remote servers. Please use RSQUIT instead.",user->nick.c_str());
+                       user->WriteNotice("*** SQUIT may not be used to remove remote servers. Please use RSQUIT instead.");
                }
        }
        else
        {
-                user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick.c_str(),parameters[0].c_str());
+                user->WriteNotice("*** SQUIT: The server \002" + parameters[0] + "\002 does not exist on the network.");
        }
        return MOD_RES_DENY;
 }
-
index 688661b8039c4c6a13182a7b01fd7edcf9e24d1f..9b73837cb3735a81fce6f61048de9a92d4734925 100644 (file)
  */
 
 
-/* $ModDesc: Provides a spanning tree server link protocol */
-
 #include "inspircd.h"
-#include "socket.h"
 
 #include "main.h"
 #include "utils.h"
-#include "treeserver.h"
 #include "link.h"
-#include "treesocket.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(std::string(ServerInstance->Config->ServerName)+" 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(std::string(ServerInstance->Config->ServerName)+" 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 (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())
+                               stats.AddRow(248, 'U', name);
                }
                return MOD_RES_DENY;
        }
        return MOD_RES_PASSTHRU;
 }
-
index ad8c6a6ef1388afa7151d3657e0be96088e3d5a6..6a64a9403efc6e568b3bf4edc03098b4beb661f9 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
-/* $ModDesc: Provides a spanning tree server link protocol */
-
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
 
 #include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commandbuilder.h"
 
-ModResult ModuleSpanningTree::HandleRemoteWhois(const std::vector<std::string>& parameters, User* user)
+ModResult ModuleSpanningTree::HandleRemoteWhois(const CommandBase::Params& 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))
-               {
-                       parameterlist params;
-                       params.push_back(remote->uuid);
-                       Utils->DoOneToOne(user->uuid,"IDLE",params,remote->server);
-                       return MOD_RES_DENY;
-               }
-               else if (!remote)
-               {
-                       user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[1].c_str());
-                       user->WriteNumeric(318, "%s %s :End of /WHOIS list.",user->nick.c_str(), 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;
 }
-
index aec680b231c1cdb75971919a1e6583cdd2990816..844feb35b45e6b024f5b5772faf0ceae69d08e1b 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
 
-#include "main.h"
 #include "utils.h"
 #include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
+#include "utils.h"
 
-bool TreeSocket::LocalPing(const std::string &prefix, parameterlist &params)
+CmdResult CommandPing::Handle(User* user, Params& params)
 {
-       if (params.size() < 1)
-               return true;
-       if (params.size() == 1)
-       {
-               std::string stufftobounce = params[0];
-               this->WriteLine(":"+ServerInstance->Config->GetSID()+" PONG "+stufftobounce);
-               return true;
-       }
-       else
+       if (params[0] == ServerInstance->Config->GetSID())
        {
-               std::string forwardto = params[1];
-               if (forwardto == ServerInstance->Config->ServerName || forwardto == ServerInstance->Config->GetSID())
-               {
-                       // this is a ping for us, send back PONG to the requesting server
-                       params[1] = params[0];
-                       params[0] = forwardto;
-                       Utils->DoOneToOne(ServerInstance->Config->GetSID(),"PONG",params,params[1]);
-               }
-               else
-               {
-                       // not for us, pass it on :)
-                       Utils->DoOneToOne(prefix,"PING",params,forwardto);
-               }
-               return true;
+               // PING for us, reply with a PONG
+               CmdBuilder reply("PONG");
+               reply.push_back(user->uuid);
+               if (params.size() >= 2)
+                       // If there is a second parameter, append it
+                       reply.push_back(params[1]);
+
+               reply.Unicast(user);
        }
+       return CMD_SUCCESS;
 }
-
-
diff --git a/src/modules/m_spanningtree/pingtimer.cpp b/src/modules/m_spanningtree/pingtimer.cpp
new file mode 100644 (file)
index 0000000..1c96259
--- /dev/null
@@ -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 (file)
index 0000000..7535586
--- /dev/null
@@ -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();
+};
index 5966d05d9f953ed90b7667fc67bfcdabf2da92eb..718d5f0bb8890af5582b907ce23e40ff9024be97 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
 
-#include "main.h"
 #include "utils.h"
 #include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
+#include "utils.h"
 
-bool TreeSocket::LocalPong(const std::string &prefix, parameterlist &params)
+CmdResult CommandPong::HandleServer(TreeServer* server, CommandBase::Params& params)
 {
-       if (params.size() < 1)
-               return true;
-
-       if (params.size() == 1)
+       if (server->IsBursting())
        {
-               TreeServer* ServerSource = Utils->FindServer(prefix);
-               if (ServerSource)
-               {
-                       ServerSource->SetPingFlag();
-                       long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
-                       ServerSource->rtt = ts - ServerSource->LastPingMsec;
-               }
+               ServerInstance->SNO->WriteGlobalSno('l', "Server \002%s\002 has not finished burst, forcing end of burst (send ENDBURST!)", server->GetName().c_str());
+               server->FinishBurst();
        }
-       else
-       {
-               std::string forwardto = params[1];
-               if (forwardto == ServerInstance->Config->GetSID() || forwardto == ServerInstance->Config->ServerName)
-               {
-                       /*
-                        * this is a PONG for us
-                        * if the prefix is a user, check theyre local, and if they are,
-                        * dump the PONG reply back to their fd. If its a server, do nowt.
-                        * Services might want to send these s->s, but we dont need to yet.
-                        */
-                       User* u = ServerInstance->FindNick(prefix);
-                       if (u)
-                       {
-                               u->WriteServ("PONG %s %s",params[0].c_str(),params[1].c_str());
-                       }
 
-                       TreeServer *ServerSource = Utils->FindServer(params[0]);
-
-                       if (ServerSource)
-                       {
-                               long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
-                               ServerSource->rtt = ts - ServerSource->LastPingMsec;
-                               ServerSource->SetPingFlag();
-                       }
-               }
-               else
-               {
-                       // not for us, pass it on :)
-                       Utils->DoOneToOne(prefix,"PONG",params,forwardto);
-               }
+       if (params[0] == ServerInstance->Config->GetSID())
+       {
+               // PONG for us
+               server->OnPong();
        }
-
-       return true;
+       return CMD_SUCCESS;
 }
-
index 3f5d427e169a7ca90b68e82ae0648217f191c825..d3eab825fec48ec09ed1c7124f152eb240e99a12 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
-/* $ModDesc: Provides a spanning tree server link protocol */
-
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
 
 #include "main.h"
 #include "utils.h"
 #include "treeserver.h"
-#include "treesocket.h"
+#include "commandbuilder.h"
 
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-void ModuleSpanningTree::OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line)
+void ModuleSpanningTree::OnPostCommand(Command* command, const CommandBase::Params& parameters, LocalUser* user, CmdResult result, bool loop)
 {
        if (result == CMD_SUCCESS)
                Utils->RouteCommand(NULL, command, parameters, user);
 }
 
-void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string &command, const parameterlist& parameters, User *user)
+void SpanningTreeUtilities::RouteCommand(TreeServer* origin, CommandBase* thiscmd, const CommandBase::Params& parameters, User* user)
 {
-       if (!ServerInstance->Parser->IsValidCommand(command, parameters.size(), user))
-               return;
-
-       /* We know it's non-null because IsValidCommand returned true */
-       Command* thiscmd = ServerInstance->Parser->GetHandler(command);
-
+       const std::string& command = thiscmd->name;
        RouteDescriptor routing = thiscmd->GetRouting(user, parameters);
-
-       std::string sent_cmd = command;
-       parameterlist params;
-
        if (routing.type == ROUTE_TYPE_LOCALONLY)
-       {
-               /* Broadcast when it's a core command with the default route descriptor and the source is a
-                * remote user or a remote server
-                */
+               return;
 
-               Version ver = thiscmd->creator->GetVersion();
-               if ((!(ver.Flags & VF_CORE)) || (IS_LOCAL(user)) || (IS_SERVER(user) == ServerInstance->FakeClient))
-                       return;
+       const bool encap = ((routing.type == ROUTE_TYPE_OPT_BCAST) || (routing.type == ROUTE_TYPE_OPT_UCAST));
+       CmdBuilder params(user, encap ? "ENCAP" : command.c_str());
+       params.push_tags(parameters.GetTags());
+       TreeServer* sdest = NULL;
 
-               routing = ROUTE_BROADCAST;
-       }
-       else if (routing.type == ROUTE_TYPE_OPT_BCAST)
+       if (routing.type == ROUTE_TYPE_OPT_BCAST)
        {
-               params.push_back("*");
+               params.push('*');
                params.push_back(command);
-               sent_cmd = "ENCAP";
        }
-       else if (routing.type == ROUTE_TYPE_OPT_UCAST)
+       else if (routing.type == ROUTE_TYPE_UNICAST || routing.type == ROUTE_TYPE_OPT_UCAST)
        {
-               TreeServer* sdest = FindServer(routing.serverdest);
+               sdest = static_cast<TreeServer*>(routing.server);
                if (!sdest)
                {
-                       ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Trying to route ENCAP to nonexistent server %s",
-                               routing.serverdest.c_str());
-                       return;
+                       // 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 nonexistent server %s", (encap ? "ENCAP " : ""), command.c_str(), routing.serverdest.c_str());
+                               return;
+                       }
+               }
+
+               if (encap)
+               {
+                       params.push_back(sdest->GetID());
+                       params.push_back(command);
                }
-               params.push_back(sdest->GetID());
-               params.push_back(command);
-               sent_cmd = "ENCAP";
        }
        else
        {
@@ -88,14 +75,13 @@ void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string &
 
                if (!(ver.Flags & (VF_COMMON | VF_CORE)) && srcmodule != Creator)
                {
-                       ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Routed command %s from non-VF_COMMON module %s",
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Routed command %s from non-VF_COMMON module %s",
                                command.c_str(), srcmodule->ModuleSourceFile.c_str());
                        return;
                }
        }
 
-       std::string output_text;
-       ServerInstance->Parser->TranslateUIDs(thiscmd->translation, parameters, output_text, true, thiscmd);
+       std::string output_text = CommandParser::TranslateUIDs(thiscmd->translation, parameters, true, thiscmd);
 
        params.push_back(output_text);
 
@@ -106,59 +92,43 @@ void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string &
                if (ServerInstance->Modes->FindPrefix(dest[0]))
                {
                        pfx = dest[0];
-                       dest = dest.substr(1);
+                       dest.erase(dest.begin());
                }
                if (dest[0] == '#')
                {
                        Channel* c = ServerInstance->FindChan(dest);
                        if (!c)
                                return;
-                       TreeServerList list;
                        // TODO OnBuildExemptList hook was here
-                       GetListOfServersForChannel(c,list,pfx, CUList());
-                       std::string data = ":" + user->uuid + " " + sent_cmd;
-                       for (unsigned int x = 0; x < params.size(); x++)
-                               data += " " + params[x];
-                       for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
-                       {
-                               TreeSocket* Sock = i->second->GetSocket();
-                               if (origin && origin->GetSocket() == Sock)
-                                       continue;
-                               if (Sock)
-                                       Sock->WriteLine(data);
-                       }
+                       CUList exempts;
+                       std::string message;
+                       if (parameters.size() >= 2)
+                               message.assign(parameters[1]);
+                       SendChannelMessage(user->uuid, c, message, pfx, parameters.GetTags(), exempts, command.c_str(), origin ? origin->GetSocket() : NULL);
                }
                else if (dest[0] == '$')
                {
-                       if (origin)
-                               DoOneToAllButSender(user->uuid, sent_cmd, params, origin->GetName());
-                       else
-                               DoOneToMany(user->uuid, sent_cmd, params);
+                       params.Forward(origin);
                }
                else
                {
                        // user target?
                        User* d = ServerInstance->FindNick(dest);
-                       if (!d)
+                       if (!d || IS_LOCAL(d))
                                return;
-                       TreeServer* tsd = BestRouteTo(d->server);
+                       TreeServer* tsd = TreeServer::Get(d)->GetRoute();
                        if (tsd == origin)
                                // huh? no routing stuff around in a circle, please.
                                return;
-                       DoOneToOne(user->uuid, sent_cmd, params, d->server);
+                       params.Unicast(d);
                }
        }
        else if (routing.type == ROUTE_TYPE_BROADCAST || routing.type == ROUTE_TYPE_OPT_BCAST)
        {
-               if (origin)
-                       DoOneToAllButSender(user->uuid, sent_cmd, params, origin->GetName());
-               else
-                       DoOneToMany(user->uuid, sent_cmd, params);
+               params.Forward(origin);
        }
        else if (routing.type == ROUTE_TYPE_UNICAST || routing.type == ROUTE_TYPE_OPT_UCAST)
        {
-               if (origin && routing.serverdest == origin->GetName())
-                       return;
-               DoOneToOne(user->uuid, sent_cmd, params, routing.serverdest);
+               params.Unicast(sdest->ServerUser);
        }
 }
index b331571ca3d2de721e15eea2bff9de8cea3230ec..5db8aafe32a3f4a9f18a7e90469886ece8422fb5 100644 (file)
  */
 
 
-/* $ModDesc: Provides a spanning tree server link protocol */
-
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
 
 #include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
 
-ModResult ModuleSpanningTree::OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line)
+ModResult ModuleSpanningTree::OnPreCommand(std::string &command, CommandBase::Params& parameters, LocalUser *user, bool validated)
 {
        /* If the command doesnt appear to be valid, we dont want to mess with it. */
        if (!validated)
@@ -45,10 +36,6 @@ ModResult ModuleSpanningTree::OnPreCommand(std::string &command, std::vector<std
        {
                return this->HandleSquit(parameters,user);
        }
-       else if (command == "MAP")
-       {
-               return this->HandleMap(parameters,user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
-       }
        else if (command == "LINKS")
        {
                this->HandleLinks(parameters,user);
@@ -64,9 +51,7 @@ ModResult ModuleSpanningTree::OnPreCommand(std::string &command, std::vector<std
        }
        else if ((command == "VERSION") && (parameters.size() > 0))
        {
-               this->HandleVersion(parameters,user);
-               return MOD_RES_DENY;
+               return this->HandleVersion(parameters,user);
        }
        return MOD_RES_PASSTHRU;
 }
-
index ca4147fea254807ad7006001bf98a3e357c76bb9..56b9370ad9692da44a6777fe3995e7cbe622a7af 100644 (file)
 
 
 #include "inspircd.h"
-#include "main.h"
 #include "utils.h"
 #include "treeserver.h"
-#include "treesocket.h"
 #include "protocolinterface.h"
+#include "commands.h"
 
 /*
  * For documentation on this class, see include/protocol.h.
  */
 
-void SpanningTreeProtocolInterface::GetServerList(ProtoServerList &sl)
+void SpanningTreeProtocolInterface::GetServerList(ServerList& sl)
 {
-       sl.clear();
        for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
        {
-               ProtoServer ps;
+               ServerInfo ps;
                ps.servername = i->second->GetName();
                TreeServer* s = i->second->GetParent();
                ps.parentname = s ? s->GetName() : "";
-               ps.usercount = i->second->GetUserCount();
-               ps.opercount = i->second->GetOperCount();
-               ps.gecos = i->second->GetDesc();
+               ps.usercount = i->second->UserCount;
+               ps.opercount = i->second->OperCount;
+               ps.description = i->second->GetDesc();
                ps.latencyms = i->second->rtt;
                sl.push_back(ps);
        }
 }
 
-bool SpanningTreeProtocolInterface::SendEncapsulatedData(const parameterlist &encap)
+bool SpanningTreeProtocolInterface::SendEncapsulatedData(const std::string& targetmask, const std::string& cmd, const CommandBase::Params& params, User* source)
 {
-       if (encap[0].find_first_of("*?") != std::string::npos)
+       if (!source)
+               source = ServerInstance->FakeClient;
+
+       CmdBuilder encap(source, "ENCAP");
+
+       // Are there any wildcards in the target string?
+       if (targetmask.find_first_of("*?") != std::string::npos)
        {
-               Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ENCAP", encap);
-               return true;
+               // Yes, send the target string as-is; servers will decide whether or not it matches them
+               encap.push(targetmask).push(cmd).insert(params).Broadcast();
        }
-       return Utils->DoOneToOne(ServerInstance->Config->GetSID(), "ENCAP", encap, encap[0]);
-}
-
-void SpanningTreeProtocolInterface::SendMetaData(Extensible* target, const std::string &key, const std::string &data)
-{
-       parameterlist params;
-
-       User* u = dynamic_cast<User*>(target);
-       Channel* c = dynamic_cast<Channel*>(target);
-       if (u)
-               params.push_back(u->uuid);
-       else if (c)
-               params.push_back(c->name);
        else
-               params.push_back("*");
+       {
+               // No wildcards which means the target string has to be the name of a known server
+               TreeServer* server = Utils->FindServer(targetmask);
+               if (!server)
+                       return false;
 
-       params.push_back(key);
-       params.push_back(":" + data);
+               // Use the SID of the target in the message instead of the server name
+               encap.push(server->GetID()).push(cmd).insert(params).Unicast(server->ServerUser);
+       }
 
-       Utils->DoOneToMany(ServerInstance->Config->GetSID(),"METADATA",params);
+       return true;
 }
 
-void SpanningTreeProtocolInterface::SendTopic(Channel* channel, std::string &topic)
+void SpanningTreeProtocolInterface::BroadcastEncap(const std::string& cmd, const CommandBase::Params& params, User* source, User* omit)
 {
-       parameterlist params;
-
-       params.push_back(channel->name);
-       params.push_back(ConvToStr(ServerInstance->Time()));
-       params.push_back(ServerInstance->Config->ServerName);
-       params.push_back(":" + topic);
+       if (!source)
+               source = ServerInstance->FakeClient;
 
-       Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FTOPIC", params);
+       // If omit is non-NULL we pass the route belonging to the user to Forward(),
+       // otherwise we pass NULL, which is equivalent to Broadcast()
+       TreeServer* server = (omit ? TreeServer::Get(omit)->GetRoute() : NULL);
+       CmdBuilder(source, "ENCAP * ").push_raw(cmd).insert(params).Forward(server);
 }
 
-void SpanningTreeProtocolInterface::SendMode(const std::string &target, const parameterlist &modedata, const std::vector<TranslateType> &translate)
+void SpanningTreeProtocolInterface::SendMetaData(User* u, const std::string& key, const std::string& data)
 {
-       if (modedata.empty())
-               return;
-
-       std::string outdata;
-       ServerInstance->Parser->TranslateUIDs(translate, modedata, outdata);
-
-       std::string uidtarget;
-       ServerInstance->Parser->TranslateUIDs(TR_NICK, target, uidtarget);
-
-       parameterlist outlist;
-       outlist.push_back(uidtarget);
-       outlist.push_back(outdata);
-
-       User* a = ServerInstance->FindNick(uidtarget);
-       if (a)
-       {
-               Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODE",outlist);
-               return;
-       }
-       else
-       {
-               Channel* c = ServerInstance->FindChan(target);
-               if (c)
-               {
-                       outlist.insert(outlist.begin() + 1, ConvToStr(c->age));
-                       Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FMODE",outlist);
-               }
-       }
+       CommandMetadata::Builder(u, key, data).Broadcast();
 }
 
-void SpanningTreeProtocolInterface::SendSNONotice(const std::string &snomask, const std::string &text)
+void SpanningTreeProtocolInterface::SendMetaData(Channel* c, const std::string& key, const std::string& data)
 {
-       parameterlist p;
-       p.push_back(snomask);
-       p.push_back(":" + text);
-       Utils->DoOneToMany(ServerInstance->Config->GetSID(), "SNONOTICE", p);
+       CommandMetadata::Builder(c, key, data).Broadcast();
 }
 
-void SpanningTreeProtocolInterface::PushToClient(User* target, const std::string &rawline)
+void SpanningTreeProtocolInterface::SendMetaData(const std::string& key, const std::string& data)
 {
-       parameterlist p;
-       p.push_back(target->uuid);
-       p.push_back(":" + rawline);
-       Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PUSH", p, target->server);
-}
-
-void SpanningTreeProtocolInterface::SendChannel(Channel* target, char status, const std::string &text)
-{
-       TreeServerList list;
-       CUList exempt_list;
-       Utils->GetListOfServersForChannel(target,list,status,exempt_list);
-       for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
-       {
-               TreeSocket* Sock = i->second->GetSocket();
-               if (Sock)
-                       Sock->WriteLine(text);
-       }
+       CommandMetadata::Builder(key, data).Broadcast();
 }
 
-
-void SpanningTreeProtocolInterface::SendChannelPrivmsg(Channel* target, char status, const std::string &text)
+void SpanningTreeProtocolInterface::Server::SendMetaData(const std::string& key, const std::string& data)
 {
-       std::string cname = target->name;
-       if (status)
-               cname.insert(0, 1, status);
-
-       SendChannel(target, status, ":" + ServerInstance->Config->GetSID()+" PRIVMSG "+cname+" :"+text);
+       sock->WriteLine(CommandMetadata::Builder(key, data));
 }
 
-void SpanningTreeProtocolInterface::SendChannelNotice(Channel* target, char status, const std::string &text)
+void SpanningTreeProtocolInterface::SendSNONotice(char snomask, const std::string &text)
 {
-       std::string cname = target->name;
-       if (status)
-               cname.insert(0, 1, status);
-
-       SendChannel(target, status, ":" + ServerInstance->Config->GetSID()+" NOTICE "+cname+" :"+text);
+       CmdBuilder("SNONOTICE").push(snomask).push_last(text).Broadcast();
 }
 
-void SpanningTreeProtocolInterface::SendUserPrivmsg(User* target, const std::string &text)
+void SpanningTreeProtocolInterface::SendMessage(Channel* target, char status, const std::string& text, MessageType msgtype)
 {
-       parameterlist p;
-       p.push_back(target->uuid);
-       p.push_back(":" + text);
-       Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PRIVMSG", p, target->server);
+       const char* cmd = (msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
+       CUList exempt_list;
+       ClientProtocol::TagMap tags;
+       Utils->SendChannelMessage(ServerInstance->Config->GetSID(), target, text, status, tags, exempt_list, cmd);
 }
 
-void SpanningTreeProtocolInterface::SendUserNotice(User* target, const std::string &text)
+void SpanningTreeProtocolInterface::SendMessage(User* target, const std::string& text, MessageType msgtype)
 {
-       parameterlist p;
+       CmdBuilder p(msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
        p.push_back(target->uuid);
-       p.push_back(":" + text);
-       Utils->DoOneToOne(ServerInstance->Config->GetSID(), "NOTICE", p, target->server);
+       p.push_last(text);
+       p.Unicast(target);
 }
index 297366893d3f86c191d547dafa0a39533586da32..969ed68bf3e6a28fdfdda2f4dc4204968fc3cab5 100644 (file)
  */
 
 
-#ifndef M_SPANNINGTREE_PROTOCOLINTERFACE_H
-#define M_SPANNINGTREE_PROTOCOLINTERFACE_H
-
-class SpanningTreeUtilities;
-class ModuleSpanningTree;
+#pragma once
 
 class SpanningTreeProtocolInterface : public ProtocolInterface
 {
-       SpanningTreeUtilities* Utils;
-       void SendChannel(Channel* target, char status, const std::string &text);
  public:
-       SpanningTreeProtocolInterface(SpanningTreeUtilities* util) : Utils(util) { }
-       virtual ~SpanningTreeProtocolInterface() { }
-
-       virtual bool SendEncapsulatedData(const parameterlist &encap);
-       virtual void SendMetaData(Extensible* target, const std::string &key, const std::string &data);
-       virtual void SendTopic(Channel* channel, std::string &topic);
-       virtual void SendMode(const std::string &target, const parameterlist &modedata, const std::vector<TranslateType> &types);
-       virtual void SendSNONotice(const std::string &snomask, const std::string &text);
-       virtual void PushToClient(User* target, const std::string &rawline);
-       virtual void SendChannelPrivmsg(Channel* target, char status, const std::string &text);
-       virtual void SendChannelNotice(Channel* target, char status, const std::string &text);
-       virtual void SendUserPrivmsg(User* target, const std::string &text);
-       virtual void SendUserNotice(User* target, const std::string &text);
-       virtual void GetServerList(ProtoServerList &sl);
-};
+       class Server : public ProtocolInterface::Server
+       {
+               TreeSocket* const sock;
 
-#endif
+        public:
+               Server(TreeSocket* s) : sock(s) { }
+               void SendMetaData(const std::string& key, const std::string& data) CXX11_OVERRIDE;
+       };
 
+       bool SendEncapsulatedData(const std::string& targetmask, const std::string& cmd, const CommandBase::Params& params, User* source) CXX11_OVERRIDE;
+       void BroadcastEncap(const std::string& cmd, const CommandBase::Params& params, User* source, User* omit) CXX11_OVERRIDE;
+       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 SendSNONotice(char snomask, const std::string& text) CXX11_OVERRIDE;
+       void SendMessage(Channel* target, char status, const std::string& text, MessageType msgtype) CXX11_OVERRIDE;
+       void SendMessage(User* target, const std::string& text, MessageType msgtype) CXX11_OVERRIDE;
+       void GetServerList(ServerList& sl) CXX11_OVERRIDE;
+};
diff --git a/src/modules/m_spanningtree/push.cpp b/src/modules/m_spanningtree/push.cpp
deleted file mode 100644 (file)
index b791376..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-bool TreeSocket::Push(const std::string &prefix, parameterlist &params)
-{
-       if (params.size() < 2)
-               return true;
-       User* u = ServerInstance->FindNick(params[0]);
-       if (!u)
-               return true;
-       if (IS_LOCAL(u))
-       {
-               u->Write(params[1]);
-       }
-       else
-       {
-               // continue the raw onwards
-               params[1] = ":" + params[1];
-               Utils->DoOneToOne(prefix,"PUSH",params,u->server);
-       }
-       return true;
-}
-
index d4254cac67f951a3cb20e1551a5f206a2c38437d..7779355e29d56a72dac76b373b657aa09910ec2b 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
 
-#include "resolvers.h"
 #include "main.h"
 #include "utils.h"
-#include "treeserver.h"
-#include "link.h"
-#include "treesocket.h"
 #include "commands.h"
 
-CommandRConnect::CommandRConnect (Module* Creator, SpanningTreeUtilities* Util)
-       : Command(Creator, "RCONNECT", 2), Utils(Util)
+CommandRConnect::CommandRConnect (Module* Creator)
+       : Command(Creator, "RCONNECT", 2)
 {
        flags_needed = 'o';
        syntax = "<remote-server-mask> <target-server-mask>";
 }
 
-CmdResult CommandRConnect::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandRConnect::Handle(User* user, const Params& parameters)
 {
-       if (IS_LOCAL(user))
+       /* First see if the server which is being asked to connect to another server in fact exists */
+       if (!Utils->FindServerMask(parameters[0]))
        {
-               if (!Utils->FindServerMask(parameters[0]))
-               {
-                       user->WriteServ("NOTICE %s :*** RCONNECT: Server \002%s\002 isn't connected to the network!", user->nick.c_str(), parameters[0].c_str());
-                       return CMD_FAILURE;
-               }
-               user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick.c_str(),parameters[0].c_str(),parameters[1].c_str());
+               user->WriteRemoteNotice(InspIRCd::Format("*** RCONNECT: Server \002%s\002 isn't connected to the network!", parameters[0].c_str()));
+               return CMD_FAILURE;
        }
 
        /* Is this aimed at our server? */
@@ -54,14 +45,29 @@ CmdResult CommandRConnect::Handle (const std::vector<std::string>& parameters, U
        {
                /* Yes, initiate the given connect */
                ServerInstance->SNO->WriteToSnoMask('l',"Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick.c_str(),parameters[0].c_str(),parameters[1].c_str());
-               std::vector<std::string> para;
+               CommandBase::Params para;
                para.push_back(parameters[1]);
                ((ModuleSpanningTree*)(Module*)creator)->HandleConnect(para, user);
        }
+       else
+       {
+               /* It's not aimed at our server, but if the request originates from our user
+                * acknowledge that we sent the request.
+                *
+                * It's possible that we're asking a server for something that makes no sense
+                * (e.g. connect to itself or to an already connected server), but we don't check
+                * for those conditions here, as ModuleSpanningTree::HandleConnect() (which will run
+                * on the target) does all the checking and error reporting.
+                */
+               if (IS_LOCAL(user))
+               {
+                       user->WriteNotice("*** RCONNECT: Sending remote connect to \002 " + parameters[0] + "\002 to connect server \002" + parameters[1] + "\002.");
+               }
+       }
        return CMD_SUCCESS;
 }
 
-RouteDescriptor CommandRConnect::GetRouting(User* user, const std::vector<std::string>& parameters)
+RouteDescriptor CommandRConnect::GetRouting(User* user, const Params& parameters)
 {
        return ROUTE_UNICAST(parameters[0]);
 }
diff --git a/src/modules/m_spanningtree/remoteuser.cpp b/src/modules/m_spanningtree/remoteuser.cpp
new file mode 100644 (file)
index 0000000..717a6fd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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 "main.h"
+#include "remoteuser.h"
+
+SpanningTree::RemoteUser::RemoteUser(const std::string& uid, Server* srv)
+       : ::RemoteUser(uid, srv)
+{
+}
+
+void SpanningTree::RemoteUser::WriteRemoteNumeric(const Numeric::Numeric& numeric)
+{
+       CommandNum::Builder(this, numeric).Unicast(this);
+}
diff --git a/src/modules/m_spanningtree/remoteuser.h b/src/modules/m_spanningtree/remoteuser.h
new file mode 100644 (file)
index 0000000..416f2f7
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 SpanningTree
+{
+       class RemoteUser;
+}
+
+class SpanningTree::RemoteUser : public ::RemoteUser
+{
+ public:
+       RemoteUser(const std::string& uid, Server* srv);
+       void WriteRemoteNumeric(const Numeric::Numeric& numeric) CXX11_OVERRIDE;
+};
index d7c4c52274c02a2dfe51c16eabee2db5aa51dfff..b20b100dd2f6bd63f29d36613ef3769a3e61936d 100644 (file)
@@ -19,9 +19,8 @@
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
 
+#include "cachetimer.h"
 #include "resolvers.h"
 #include "main.h"
 #include "utils.h"
 #include "link.h"
 #include "treesocket.h"
 
-/* $ModDep: m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
-
 /** This class is used to resolve server hostnames during /connect and autoconnect.
  * As of 1.1, the resolver system is seperated out from BufferedSocket, so we must do this
  * resolver step first ourselves if we need it. This is totally nonblocking, and will
  * callback to OnLookupComplete or OnError when completed. Once it has completed we
  * will have an IP address which we can then use to continue our connection.
  */
-ServernameResolver::ServernameResolver(SpanningTreeUtilities* Util, const std::string &hostname, Link* x, bool &cached, QueryType qt, Autoconnect* myac)
-       : Resolver(hostname, qt, cached, Util->Creator), Utils(Util), query(qt), host(hostname), MyLink(x), myautoconnect(myac)
+ServernameResolver::ServernameResolver(DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt, Autoconnect* myac)
+       : DNS::Request(mgr, Utils->Creator, hostname, qt)
+       , query(qt), host(hostname), MyLink(x), myautoconnect(myac)
 {
 }
 
-void ServernameResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+void ServernameResolver::OnLookupComplete(const DNS::Query *r)
 {
+       const DNS::ResourceRecord* const ans_record = r->FindAnswerOfType(this->question.type);
+       if (!ans_record)
+       {
+               OnError(r);
+               return;
+       }
+
+       irc::sockets::sockaddrs sa;
+       if (!irc::sockets::aptosa(ans_record->rdata, MyLink->Port, sa))
+       {
+               // We had a result but it wasn't a valid IPv4/IPv6.
+               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(Utils, MyLink, myautoconnect, result);
+               TreeSocket* newsocket = new TreeSocket(MyLink, myautoconnect, sa);
                if (newsocket->GetFd() > -1)
                {
                        /* We're all OK */
@@ -66,47 +79,83 @@ void ServernameResolver::OnLookupComplete(const std::string &result, unsigned in
        }
 }
 
-void ServernameResolver::OnError(ResolverError e, const std::string &errormessage)
+void ServernameResolver::OnError(const DNS::Query *r)
 {
-       /* Ooops! */
-       if (query == DNS_QUERY_AAAA)
+       if (r->error == DNS::ERROR_UNLOADED)
        {
-               bool cached = false;
-               ServernameResolver* snr = new ServernameResolver(Utils, host, MyLink, cached, DNS_QUERY_A, myautoconnect);
-               ServerInstance->AddResolver(snr, cached);
+               // We're being unloaded, skip the snotice and ConnectServer() below to prevent autoconnect creating new sockets
                return;
        }
-       ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s", MyLink->Name.c_str(), errormessage.c_str() );
+
+       if (query == DNS::QUERY_AAAA)
+       {
+               ServernameResolver* snr = new ServernameResolver(this->manager, host, MyLink, DNS::QUERY_A, myautoconnect);
+               try
+               {
+                       this->manager->Process(snr);
+                       return;
+               }
+               catch (DNS::Exception &)
+               {
+                       delete snr;
+               }
+       }
+
+       ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s", MyLink->Name.c_str(), this->manager->GetErrorStr(r->error).c_str());
        Utils->Creator->ConnectServer(myautoconnect, false);
 }
 
-SecurityIPResolver::SecurityIPResolver(Module* me, SpanningTreeUtilities* U, const std::string &hostname, Link* x, bool &cached, QueryType qt)
-               : Resolver(hostname, qt, cached, me), MyLink(x), Utils(U), mine(me), host(hostname), query(qt)
+SecurityIPResolver::SecurityIPResolver(Module* me, DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt)
+       : DNS::Request(mgr, me, hostname, qt)
+       , MyLink(x), mine(me), host(hostname), query(qt)
 {
 }
 
-void SecurityIPResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+void SecurityIPResolver::OnLookupComplete(const DNS::Query *r)
 {
        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(result);
+                       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;
                }
        }
 }
 
-void SecurityIPResolver::OnError(ResolverError e, const std::string &errormessage)
+void SecurityIPResolver::OnError(const DNS::Query *r)
 {
-       if (query == DNS_QUERY_AAAA)
+       // This can be called because of us being unloaded but we don't have to do anything differently
+       if (query == DNS::QUERY_AAAA)
        {
-               bool cached = false;
-               SecurityIPResolver* res = new SecurityIPResolver(mine, Utils, host, MyLink, cached, DNS_QUERY_A);
-               ServerInstance->AddResolver(res, cached);
-               return;
+               SecurityIPResolver* res = new SecurityIPResolver(mine, this->manager, host, MyLink, DNS::QUERY_A);
+               try
+               {
+                       this->manager->Process(res);
+                       return;
+               }
+               catch (DNS::Exception &)
+               {
+                       delete res;
+               }
        }
-       ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Could not resolve IP associated with Link '%s': %s",
-               MyLink->Name.c_str(),errormessage.c_str());
+       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Could not resolve IP associated with Link '%s': %s",
+               MyLink->Name.c_str(), this->manager->GetErrorStr(r->error).c_str());
+}
+
+CacheRefreshTimer::CacheRefreshTimer()
+       : Timer(3600, true)
+{
+}
+
+bool CacheRefreshTimer::Tick(time_t TIME)
+{
+       Utils->RefreshIPCache();
+       return true;
 }
index 65b9e72495db121e2a5359e8c51c1110404a612c..782ac86efeebfa47172d860044aafe4ef7499066 100644 (file)
  */
 
 
-#ifndef M_SPANNINGTREE_RESOLVERS_H
-#define M_SPANNINGTREE_RESOLVERS_H
+#pragma once
 
-#include "socket.h"
 #include "inspircd.h"
-#include "xline.h"
+#include "modules/dns.h"
 
 #include "utils.h"
 #include "link.h"
 
 /** Handle resolving of server IPs for the cache
  */
-class SecurityIPResolver : public Resolver
+class SecurityIPResolver : public DNS::Request
 {
  private:
        reference<Link> MyLink;
-       SpanningTreeUtilities* Utils;
        Module* mine;
        std::string host;
-       QueryType query;
+       DNS::QueryType query;
  public:
-       SecurityIPResolver(Module* me, SpanningTreeUtilities* U, const std::string &hostname, Link* x, bool &cached, QueryType qt);
-       void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
-       void OnError(ResolverError e, const std::string &errormessage);
+       SecurityIPResolver(Module* me, DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt);
+       void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE;
+       void OnError(const DNS::Query *q) CXX11_OVERRIDE;
 };
 
 /** This class is used to resolve server hostnames during /connect and autoconnect.
@@ -50,18 +47,15 @@ class SecurityIPResolver : public Resolver
  * callback to OnLookupComplete or OnError when completed. Once it has completed we
  * will have an IP address which we can then use to continue our connection.
  */
-class ServernameResolver : public Resolver
+class ServernameResolver : public DNS::Request
 {
  private:
-       SpanningTreeUtilities* Utils;
-       QueryType query;
+       DNS::QueryType query;
        std::string host;
        reference<Link> MyLink;
        reference<Autoconnect> myautoconnect;
  public:
-       ServernameResolver(SpanningTreeUtilities* Util, const std::string &hostname, Link* x, bool &cached, QueryType qt, Autoconnect* myac);
-       void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
-       void OnError(ResolverError e, const std::string &errormessage);
+       ServernameResolver(DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt, Autoconnect* myac);
+       void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE;
+       void OnError(const DNS::Query *q) CXX11_OVERRIDE;
 };
-
-#endif
index 027ae02ab78dc45dab25cf4496c44a49148b75d9..7edb9501aa18ae7b4fac26986df3f96a1e511895 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
 
 #include "main.h"
 #include "utils.h"
 #include "treeserver.h"
-#include "treesocket.h"
 #include "commands.h"
 
-CommandRSQuit::CommandRSQuit (Module* Creator, SpanningTreeUtilities* Util)
-       : Command(Creator, "RSQUIT", 1), Utils(Util)
+CommandRSQuit::CommandRSQuit(Module* Creator)
+       : Command(Creator, "RSQUIT", 1)
 {
        flags_needed = 'o';
-       syntax = "<target-server-mask> [reason]";
+       syntax = "<target-server-mask> [:<reason>]";
 }
 
-CmdResult CommandRSQuit::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandRSQuit::Handle(User* user, const Params& parameters)
 {
        TreeServer *server_target; // Server to squit
-       TreeServer *server_linked; // Server target is linked to
 
        server_target = Utils->FindServerMask(parameters[0]);
        if (!server_target)
        {
-               user->WriteServ("NOTICE %s :*** RSQUIT: Server \002%s\002 isn't connected to the network!", user->nick.c_str(), 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 == Utils->TreeRoot)
+       if (server_target->IsRoot())
        {
-               NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+parameters[0]+" matches local server name)");
+               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;
        }
 
-       server_linked = server_target->GetParent();
-
-       if (server_linked == Utils->TreeRoot)
+       if (server_target->IsLocal())
        {
                // We have been asked to remove server_target.
-               TreeSocket* sock = server_target->GetSocket();
-               if (sock)
-               {
-                       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();
-               }
+               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);
+               server_target->SQuit("Server quit by " + user->GetFullRealHost() + " (" + reason + ")");
        }
 
        return CMD_SUCCESS;
 }
 
-RouteDescriptor CommandRSQuit::GetRouting(User* user, const std::vector<std::string>& parameters)
+RouteDescriptor CommandRSQuit::GetRouting(User* user, const Params& parameters)
 {
        return ROUTE_UNICAST(parameters[0]);
 }
-
-// XXX use protocol interface instead of rolling our own :)
-void CommandRSQuit::NoticeUser(User* user, const std::string &msg)
-{
-       if (IS_LOCAL(user))
-       {
-               user->WriteServ("NOTICE %s :%s",user->nick.c_str(),msg.c_str());
-       }
-       else
-       {
-               parameterlist params;
-               params.push_back(user->nick);
-               params.push_back("NOTICE "+ConvToStr(user->nick)+" :"+msg);
-               Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PUSH", params, user->server);
-       }
-}
-
index 92999b422bb852d663132367f89d91dc1dd96b0e..be3a0a687cf23c091b58c78aecc55d8ab57d306f 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
 
-#include "main.h"
 #include "utils.h"
-#include "treeserver.h"
 #include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
 
 /**
  * SAVE command - force nick change to UID on timestamp match
  */
-bool TreeSocket::ForceNick(const std::string &prefix, parameterlist &params)
+CmdResult CommandSave::Handle(User* user, Params& params)
 {
-       if (params.size() < 2)
-               return true;
-
-       User* u = ServerInstance->FindNick(params[0]);
-       time_t ts = atol(params[1].c_str());
+       User* u = ServerInstance->FindUUID(params[0]);
+       if (!u)
+               return CMD_FAILURE;
 
-       if ((u) && (!IS_SERVER(u)) && (u->age == ts))
-       {
-               Utils->DoOneToAllButSender(prefix,"SAVE",params,prefix);
+       time_t ts = ConvToNum<time_t>(params[1]);
 
-               if (!u->ForceNickChange(u->uuid.c_str()))
-               {
-                       ServerInstance->Users->QuitUser(u, "Nickname collision");
-               }
-       }
+       if (u->age == ts)
+               u->ChangeNick(u->uuid, SavedTimestamp);
 
-       return true;
+       return CMD_SUCCESS;
 }
-
index d3033799e1a21d8d91990b51377bcf32cbd64871..0af91a0ed35a020f0310bba22c5b5a384555f47f 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
+#include "modules/ssl.h"
 
 #include "main.h"
 #include "utils.h"
 #include "link.h"
 #include "treeserver.h"
 #include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h m_spanningtree/link.h */
+#include "commands.h"
 
 /*
  * Some server somewhere in the network introducing another server.
  *     -- w
  */
-bool TreeSocket::RemoteServer(const std::string &prefix, parameterlist &params)
+CmdResult CommandServer::HandleServer(TreeServer* ParentOfThis, Params& params)
 {
-       if (params.size() < 5)
-       {
-               SendError("Protocol error - Not enough parameters for SERVER command");
-               return false;
-       }
+       const std::string& servername = params[0];
+       const std::string& sid = params[1];
+       const std::string& description = params.back();
+       TreeSocket* socket = ParentOfThis->GetSocket();
 
-       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];
-       TreeServer* ParentOfThis = Utils->FindServer(prefix);
-
-       if (!ParentOfThis)
-       {
-               this->SendError("Protocol error - Introduced remote server from unknown server "+prefix);
-               return false;
-       }
-       if (!ServerInstance->IsSID(sid))
+       if (!InspIRCd::IsSID(sid))
        {
-               this->SendError("Invalid format server ID: "+sid+"!");
-               return false;
+               socket->SendError("Invalid format server ID: "+sid+"!");
+               return CMD_FAILURE;
        }
        TreeServer* CheckDupe = Utils->FindServer(servername);
        if (CheckDupe)
        {
-               this->SendError("Server "+servername+" already exists!");
-               ServerInstance->SNO->WriteToSnoMask('L', "Server \2"+CheckDupe->GetName()+"\2 being introduced from \2" + ParentOfThis->GetName() + "\2 denied, already exists. Closing link with " + ParentOfThis->GetName());
-               return false;
+               socket->SendError("Server "+servername+" already exists!");
+               ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+CheckDupe->GetName()+"\002 being introduced from \002" + ParentOfThis->GetName() + "\002 denied, already exists. Closing link with " + ParentOfThis->GetName());
+               return CMD_FAILURE;
        }
        CheckDupe = Utils->FindServer(sid);
        if (CheckDupe)
        {
-               this->SendError("Server ID "+sid+" already exists! 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"+servername+"\2 being introduced from \2" + ParentOfThis->GetName() + "\2 denied, server ID already exists on the network. Closing link with " + ParentOfThis->GetName());
-               return false;
+               socket->SendError("Server ID "+sid+" already exists! 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 \002"+servername+"\002 being introduced from \002" + ParentOfThis->GetName() + "\002 denied, server ID already exists on the network. Closing link with " + ParentOfThis->GetName());
+               return CMD_FAILURE;
        }
 
 
        Link* lnk = Utils->FindLink(servername);
 
-       TreeServer *Node = new TreeServer(Utils, servername, description, sid, ParentOfThis,NULL, lnk ? lnk->Hidden : false);
+       TreeServer* Node = new TreeServer(servername, description, sid, ParentOfThis, ParentOfThis->GetSocket(), lnk ? lnk->Hidden : false);
+
+       HandleExtra(Node, params);
 
-       ParentOfThis->AddChild(Node);
-       params[4] = ":" + params[4];
-       Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix);
        ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+ParentOfThis->GetName()+"\002 introduced server \002"+servername+"\002 ("+description+")");
-       return true;
+       return CMD_SUCCESS;
 }
 
+void CommandServer::HandleExtra(TreeServer* newserver, Params& params)
+{
+       for (CommandBase::Params::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(ConvToNum<uint64_t>(val));
+       }
+}
+
+Link* TreeSocket::AuthRemote(const CommandBase::Params& 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];
-       int hops = atoi(params[2].c_str());
+       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 (hops)
-       {
-               this->SendError("Server too far away for authentication");
-               ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
-               return false;
-       }
-
        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 (!InspIRCd::Match(sname, x->Name))
                        continue;
 
                if (!ComparePass(*x, password))
                {
-                       ServerInstance->SNO->WriteToSnoMask('l',"Invalid password on link: %s", x->Name.c_str());
+                       ServerInstance->SNO->WriteToSnoMask('l', "Invalid password on link: %s", x->Name.c_str());
                        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. user mode +s +Ll)");
+       ServerInstance->SNO->WriteToSnoMask('l', "Server connection from \002"+sname+"\002 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(CommandBase::Params& 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
@@ -158,49 +161,34 @@ 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(Utils, sname, description, sid, Utils->TreeRoot, this, x->Hidden);
-               Utils->TreeRoot->AddChild(MyRoot);
-               this->DoBurst(MyRoot);
-
-               params[4] = ":" + params[4];
-
-               /* IMPORTANT: Take password/hmac hash OUT of here before we broadcast the introduction! */
-               params[1] = "*";
-               Utils->DoOneToAllButSender(ServerInstance->Config->GetSID(),"SERVER",params,sname);
+               FinishAuth(params[0], params[3], params.back(), x->Hidden);
 
                return true;
        }
 
-       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 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)
        {
                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);
+               ServerInstance->SNO->WriteToSnoMask('l', "Server connection from \002"+sname+"\002 denied, already exists on server "+pname);
                return false;
        }
 
-       /* Check for fully initialized instances of the server by id */
-       ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Looking for dupe SID %s", sid.c_str());
+       // 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);
 
        if (CheckDupe)
        {
                this->SendError("Server ID "+CheckDupe->GetID()+" already exists on server "+CheckDupe->GetName()+"! 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 connection from \2"+sname+"\2 denied, server ID '"+CheckDupe->GetID()+
+               ServerInstance->SNO->WriteToSnoMask('l', "Server connection from \002"+sname+"\002 denied, server ID '"+CheckDupe->GetID()+
                                "' already exists on server "+CheckDupe->GetName());
                return false;
        }
@@ -212,60 +200,16 @@ bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid
  * Someone else is attempting to connect to us if this is called. Validate their credentials etc.
  *             -- w
  */
-bool TreeSocket::Inbound_Server(parameterlist &params)
+bool TreeSocket::Inbound_Server(CommandBase::Params& 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];
-       int hops = atoi(params[2].c_str());
-
-       this->SendCapabilities(2);
-
-       if (hops)
-       {
-               this->SendError("Server too far away for authentication");
-               ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
-               return false;
-       }
-
-       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.
@@ -276,8 +220,15 @@ bool TreeSocket::Inbound_Server(parameterlist &params)
                return true;
        }
 
-       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 false;
 }
 
+CommandServer::Builder::Builder(TreeServer* server)
+       : CmdBuilder(server->GetParent()->GetID(), "SERVER")
+{
+       push(server->GetName());
+       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
new file mode 100644 (file)
index 0000000..2f5c7ea
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 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 "main.h"
+#include "servercommand.h"
+
+ServerCommand::ServerCommand(Module* Creator, const std::string& Name, unsigned int MinParams, unsigned int MaxParams)
+       : CommandBase(Creator, Name, MinParams, MaxParams)
+{
+}
+
+void ServerCommand::RegisterService()
+{
+       ModuleSpanningTree* st = static_cast<ModuleSpanningTree*>(static_cast<Module*>(creator));
+       st->CmdManager.AddCommand(this);
+}
+
+RouteDescriptor ServerCommand::GetRouting(User* user, const Params& parameters)
+{
+       // Broadcast server-to-server commands unless overridden
+       return ROUTE_BROADCAST;
+}
+
+time_t ServerCommand::ExtractTS(const std::string& tsstr)
+{
+       time_t TS = ConvToNum<time_t>(tsstr);
+       if (!TS)
+               throw ProtocolException("Invalid TS");
+       return TS;
+}
+
+ServerCommand* ServerCommandManager::GetHandler(const std::string& command) const
+{
+       ServerCommandMap::const_iterator it = commands.find(command);
+       if (it != commands.end())
+               return it->second;
+       return NULL;
+}
+
+bool ServerCommandManager::AddCommand(ServerCommand* cmd)
+{
+       return commands.insert(std::make_pair(cmd->name, cmd)).second;
+}
diff --git a/src/modules/m_spanningtree/servercommand.h b/src/modules/m_spanningtree/servercommand.h
new file mode 100644 (file)
index 0000000..6ea5a92
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2013 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 "utils.h"
+#include "treeserver.h"
+
+class ProtocolException : public ModuleException
+{
+ public:
+       ProtocolException(const std::string& msg)
+               : ModuleException("Protocol violation: " + msg)
+       {
+       }
+};
+
+/** Base class for server-to-server commands that may have a (remote) user source or server source.
+ */
+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, Params& parameters) = 0;
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
+
+       /**
+        * Extract the TS from a string.
+        * @param tsstr The string containing the TS.
+        * @return The raw timestamp value.
+        * This function throws a ProtocolException if it considers the TS invalid. Note that the detection of
+        * invalid timestamps is not designed to be bulletproof, only some cases - like "0" - trigger an exception.
+        */
+       static time_t ExtractTS(const std::string& tsstr);
+};
+
+/** Base class for server-to-server command handlers which are only valid if their source is a user.
+ * When a server sends a command of this type and the source is a server (sid), the link is aborted.
+ */
+template <class T>
+class UserOnlyServerCommand : public ServerCommand
+{
+ public:
+       UserOnlyServerCommand(Module* Creator, const std::string& Name, unsigned int MinPara = 0, unsigned int MaxPara = 0)
+               : ServerCommand(Creator, Name, MinPara, MaxPara) { }
+
+       CmdResult Handle(User* user, Params& parameters) CXX11_OVERRIDE
+       {
+               RemoteUser* remoteuser = IS_REMOTE(user);
+               if (!remoteuser)
+                       throw ProtocolException("Invalid source");
+               return static_cast<T*>(this)->HandleRemote(remoteuser, parameters);
+       }
+};
+
+/** Base class for server-to-server command handlers which are only valid if their source is a server.
+ * When a server sends a command of this type and the source is a user (uuid), the link is aborted.
+ */
+template <class T>
+class ServerOnlyServerCommand : public ServerCommand
+{
+ public:
+       ServerOnlyServerCommand(Module* Creator, const std::string& Name, unsigned int MinPara = 0, unsigned int MaxPara = 0)
+               : ServerCommand(Creator, Name, MinPara, MaxPara) { }
+
+       CmdResult Handle(User* user, CommandBase::Params& parameters) CXX11_OVERRIDE
+       {
+               if (!IS_SERVER(user))
+                       throw ProtocolException("Invalid source");
+               TreeServer* server = TreeServer::Get(user);
+               return static_cast<T*>(this)->HandleServer(server, parameters);
+       }
+};
+
+class ServerCommandManager
+{
+       typedef TR1NS::unordered_map<std::string, ServerCommand*> ServerCommandMap;
+       ServerCommandMap commands;
+
+ public:
+       ServerCommand* GetHandler(const std::string& command) const;
+       bool AddCommand(ServerCommand* cmd);
+};
diff --git a/src/modules/m_spanningtree/sinfo.cpp b/src/modules/m_spanningtree/sinfo.cpp
new file mode 100644 (file)
index 0000000..a5dae78
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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, CommandBase::Params& 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 == "rawversion")
+       {
+               server->SetRawVersion(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);
+}
index 416502369c7856312afe3d34fc43283a15b21c8f..92187ddf76297a85681cfd23146be4a26b0a0d10 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
 
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
 #include "commands.h"
 
-CmdResult CommandSVSJoin::Handle(const std::vector<std::string>& parameters, User *user)
+CmdResult CommandSVSJoin::Handle(User* user, Params& parameters)
 {
        // Check for valid channel name
-       if (!ServerInstance->IsChannel(parameters[1].c_str(), ServerInstance->Config->Limits.ChanMax))
+       if (!ServerInstance->IsChannel(parameters[1]))
                return CMD_FAILURE;
 
        // Check target exists
@@ -40,15 +34,25 @@ CmdResult CommandSVSJoin::Handle(const std::vector<std::string>& parameters, Use
                return CMD_FAILURE;
 
        /* only join if it's local, otherwise just pass it on! */
-       if (IS_LOCAL(u))
-               Channel::JoinUser(u, parameters[1].c_str(), false, "", false, ServerInstance->Time());
+       LocalUser* localuser = IS_LOCAL(u);
+       if (localuser)
+       {
+               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)
+RouteDescriptor CommandSVSJoin::GetRouting(User* user, const Params& parameters)
 {
-       User* u = ServerInstance->FindUUID(parameters[0]);
-       if (u)
-               return ROUTE_OPT_UCAST(u->server);
-       return ROUTE_LOCALONLY;
+       return ROUTE_OPT_UCAST(parameters[0]);
 }
index 59973202ddc06cc52a73a214648eeeaae45986dc..a734dc8ed8e84bb1945b7e5b7ca54bc40c02b6fb 100644 (file)
 #include "inspircd.h"
 
 #include "main.h"
-#include "utils.h"
 #include "commands.h"
 
-CmdResult CommandSVSNick::Handle(const std::vector<std::string>& parameters, User *user)
+CmdResult CommandSVSNick::Handle(User* user, Params& parameters)
 {
        User* u = ServerInstance->FindNick(parameters[0]);
 
        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 = ConvToNum<time_t>(parameters[3]);
+                       if (u->age != ExpectedTS)
+                               return CMD_FAILURE; // Ignore SVSNICK
+               }
+
                std::string nick = parameters[1];
                if (isdigit(nick[0]))
                        nick = u->uuid;
 
-               // Don't update the TS if the nick is exactly the same
-               if (u->nick == nick)
-                       return CMD_FAILURE;
-
-               time_t NickTS = ConvToInt(parameters[2]);
+               time_t NickTS = ConvToNum<time_t>(parameters[2]);
                if (NickTS <= 0)
                        return CMD_FAILURE;
 
-               ModuleSpanningTree* st = (ModuleSpanningTree*)(Module*)creator;
-               st->KeepNickTS = true;
-               u->age = NickTS;
-
-               if (!u->ForceNickChange(nick.c_str()))
+               if (!u->ChangeNick(nick, NickTS))
                {
-                       /* buh. UID them */
-                       if (!u->ForceNickChange(u->uuid.c_str()))
-                       {
-                               ServerInstance->Users->QuitUser(u, "Nickname collision");
-                       }
+                       // Changing to 'nick' failed (it may already be in use), change to the uuid
+                       u->ChangeNick(u->uuid);
                }
-
-               st->KeepNickTS = false;
        }
 
        return CMD_SUCCESS;
 }
 
-RouteDescriptor CommandSVSNick::GetRouting(User* user, const std::vector<std::string>& parameters)
+RouteDescriptor CommandSVSNick::GetRouting(User* user, const Params& parameters)
 {
-       User* u = ServerInstance->FindNick(parameters[0]);
-       if (u)
-               return ROUTE_OPT_UCAST(u->server);
-       return ROUTE_LOCALONLY;
+       return ROUTE_OPT_UCAST(parameters[0]);
 }
index 3bdf13b25ca1e03ee8cc0859e5f8047404f08314..505a033e301efa137bf86be083128b643d41c7d0 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
 
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
 #include "commands.h"
 
-CmdResult CommandSVSPart::Handle(const std::vector<std::string>& parameters, User *user)
+CmdResult CommandSVSPart::Handle(User* user, Params& parameters)
 {
        User* u = ServerInstance->FindUUID(parameters[0]);
        if (!u)
@@ -46,10 +40,7 @@ CmdResult CommandSVSPart::Handle(const std::vector<std::string>& parameters, Use
        return CMD_SUCCESS;
 }
 
-RouteDescriptor CommandSVSPart::GetRouting(User* user, const std::vector<std::string>& parameters)
+RouteDescriptor CommandSVSPart::GetRouting(User* user, const Params& 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 (file)
index 0000000..66e1bb3
--- /dev/null
@@ -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 (file)
index 0000000..a2bc6df
--- /dev/null
@@ -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);
+}
index 493b05ebf78f6dda096e3c8b3ab04f86200dc16e..bbe66ff833906301e1b54efb8ef217236e4e9489 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
 #include "xline.h"
 #include "main.h"
-#include "../spanningtree.h"
+#include "modules/server.h"
 
 #include "utils.h"
 #include "treeserver.h"
 
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h */
-
 /** We use this constructor only to create the 'root' item, Utils->TreeRoot, which
  * represents our own server. Therefore, it has no route, no parent, and
  * no socket associated with it. Its version string is our own local version.
  */
-TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id)
-       : ServerName(Name.c_str()), ServerDesc(Desc), Utils(Util), ServerUser(ServerInstance->FakeClient)
+TreeServer::TreeServer()
+       : Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc)
+       , Parent(NULL), Route(NULL)
+       , VersionString(ServerInstance->GetVersionString())
+       , fullversion(ServerInstance->GetVersionString(true))
+       , rawversion(INSPIRCD_VERSION)
+       , 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)
 {
-       age = ServerInstance->Time();
-       bursting = false;
-       Parent = NULL;
-       VersionString.clear();
-       ServerUserCount = ServerOperCount = 0;
-       VersionString = ServerInstance->GetVersionString();
-       Route = NULL;
-       Socket = NULL; /* Fix by brain */
-       StartBurst = rtt = 0;
-       Warned = Hidden = false;
        AddHashEntry();
-       SetID(id);
 }
 
 /** 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(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id, TreeServer* Above, TreeSocket* Sock, bool Hide)
-       : Parent(Above), ServerName(Name.c_str()), ServerDesc(Desc), Socket(Sock), Utils(Util), ServerUser(new FakeUser(id, Name)), Hidden(Hide)
+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), 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)
 {
-       age = ServerInstance->Time();
-       bursting = true;
-       VersionString.clear();
-       ServerUserCount = ServerOperCount = 0;
-       SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
-       SetPingFlag();
-       Warned = false;
-       rtt = 0;
-
-       long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
-       this->StartBurst = ts;
-       ServerInstance->Logs->Log("m_spanningtree",DEBUG, "Started bursting at time %lu", ts);
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "New server %s behind_bursting %u", GetName().c_str(), behind_bursting);
+       CheckULine();
+
+       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)
@@ -124,246 +115,180 @@ TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::strin
         */
 
        this->AddHashEntry();
+       Parent->Children.push_back(this);
 
-       SetID(id);
+       FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), ServerEventListener, 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();
-       for(unsigned int q=0; q < ChildCount(); q++)
+       // 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 = GetChild(q);
+               TreeServer* child = *i;
                child->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)",
-               ServerName.c_str(), (bursttime > 10000 ? bursttime / 1000 : bursttime), (bursttime > 10000 ? "secs" : "msecs"));
-       AddServerEvent(Utils->Creator, ServerName.c_str());
-}
+       ServerInstance->SNO->WriteToSnoMask(Parent == Utils->TreeRoot ? 'l' : 'L', "Received end of netburst from \002%s\002 (burst time: %lu %s)",
+               GetName().c_str(), (bursttime > 10000 ? bursttime / 1000 : bursttime), (bursttime > 10000 ? "secs" : "msecs"));
 
-void TreeServer::SetID(const std::string &id)
-{
-       ServerInstance->Logs->Log("m_spanningtree",DEBUG, "Setting SID to " + id);
-       sid = id;
-       Utils->sidlist[sid] = this;
+       StartBurst = 0;
+       FinishBurstInternal();
 }
 
-int TreeServer::QuitUsers(const std::string &reason)
+void TreeServer::SQuitChild(TreeServer* server, const std::string& reason)
 {
-       const char* reason_s = reason.c_str();
-       std::vector<User*> time_to_die;
-       for (user_hash::iterator n = ServerInstance->Users->clientlist->begin(); n != ServerInstance->Users->clientlist->end(); n++)
+       stdalgo::erase(Children, server);
+
+       if (IsRoot())
        {
-               if (n->second->server == ServerName)
-               {
-                       time_to_die.push_back(n->second);
-               }
+               // 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();
        }
-       for (std::vector<User*>::iterator n = time_to_die.begin(); n != time_to_die.end(); n++)
+       else
        {
-               User* a = (User*)*n;
-               if (!IS_LOCAL(a))
-               {
-                       if (this->Utils->quiet_bursts)
-                               a->quietquit = true;
-
-                       if (ServerInstance->Config->HideSplits)
-                               ServerInstance->Users->QuitUser(a, "*.net *.split", reason_s);
-                       else
-                               ServerInstance->Users->QuitUser(a, reason_s);
-               }
+               ServerInstance->SNO->WriteToSnoMask('L', "Server \002" + server->GetName() + "\002 split from server \002" + GetName() + "\002 with reason: " + reason);
        }
-       return time_to_die.size();
-}
-
-/** This method is used to add the structure to the
- * hash_map for linear searches. It is only called
- * by the constructors.
- */
-void TreeServer::AddHashEntry()
-{
-       server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str());
-       if (iter == Utils->serverlist.end())
-               Utils->serverlist[this->ServerName.c_str()] = this;
-}
-
-/** This method removes the reference to this object
- * from the hash_map which is used for linear searches.
- * It is only called by the default destructor.
- */
-void TreeServer::DelHashEntry()
-{
-       server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str());
-       if (iter != Utils->serverlist.end())
-               Utils->serverlist.erase(iter);
-}
-
-/** These accessors etc should be pretty self-
- * explanitory.
- */
-TreeServer* TreeServer::GetRoute()
-{
-       return Route;
-}
-
-std::string TreeServer::GetName()
-{
-       return ServerName.c_str();
-}
-
-const std::string& TreeServer::GetDesc()
-{
-       return ServerDesc;
-}
-
-const std::string& TreeServer::GetVersion()
-{
-       return VersionString;
-}
-
-void TreeServer::SetNextPingTime(time_t t)
-{
-       this->NextPing = t;
-       LastPingWasGood = false;
-}
 
-time_t TreeServer::NextPingTime()
-{
-       return NextPing;
-}
+       unsigned int num_lost_servers = 0;
+       server->SQuitInternal(num_lost_servers);
 
-bool TreeServer::AnsweredLastPing()
-{
-       return LastPingWasGood;
-}
+       const std::string quitreason = GetName() + " " + server->GetName();
+       unsigned int num_lost_users = QuitUsers(quitreason);
 
-void TreeServer::SetPingFlag()
-{
-       LastPingWasGood = true;
-}
+       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" : "");
 
-unsigned int TreeServer::GetUserCount()
-{
-       return ServerUserCount;
-}
+       // No-op if the socket is already closed (i.e. it called us)
+       if (server->IsLocal())
+               server->GetSocket()->Close();
 
-void TreeServer::SetUserCount(int diff)
-{
-       ServerUserCount += diff;
+       // Add the server to the cull list, the servers behind it are handled by cull() and the destructor
+       ServerInstance->GlobalCulls.AddItem(server);
 }
 
-void TreeServer::SetOperCount(int diff)
+void TreeServer::SQuitInternal(unsigned int& num_lost_servers)
 {
-       ServerOperCount += diff;
-}
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s lost in split", GetName().c_str());
 
-unsigned int TreeServer::GetOperCount()
-{
-       return ServerOperCount;
-}
+       for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
+       {
+               TreeServer* server = *i;
+               server->SQuitInternal(num_lost_servers);
+       }
 
-TreeSocket* TreeServer::GetSocket()
-{
-       return Socket;
-}
+       // Mark server as dead
+       isdead = true;
+       num_lost_servers++;
+       RemoveHash();
 
-TreeServer* TreeServer::GetParent()
-{
-       return Parent;
+       if (!Utils->Creator->dying)
+               FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), ServerEventListener, OnServerSplit, (this));
 }
 
-void TreeServer::SetVersion(const std::string &Version)
+unsigned int TreeServer::QuitUsers(const std::string& reason)
 {
-       VersionString = Version;
-}
+       std::string publicreason = Utils->HideSplits ? "*.net *.split" : reason;
 
-unsigned int TreeServer::ChildCount()
-{
-       return Children.size();
-}
-
-TreeServer* TreeServer::GetChild(unsigned int n)
-{
-       if (n < Children.size())
-       {
-               /* Make sure they  cant request
-                * an out-of-range object. After
-                * all we know what these programmer
-                * types are like *grin*.
-                */
-               return Children[n];
-       }
-       else
+       const user_hash& users = ServerInstance->Users->GetUsers();
+       unsigned int original_size = users.size();
+       for (user_hash::const_iterator i = users.begin(); i != users.end(); )
        {
-               return NULL;
+               User* user = i->second;
+               // Increment the iterator now because QuitUser() removes the user from the container
+               ++i;
+               TreeServer* server = TreeServer::Get(user);
+               if (server->IsDead())
+                       ServerInstance->Users->QuitUser(user, publicreason, &reason);
        }
+       return original_size - users.size();
 }
 
-void TreeServer::AddChild(TreeServer* Child)
+void TreeServer::CheckULine()
 {
-       Children.push_back(Child);
-}
+       uline = silentuline = false;
 
-bool TreeServer::DelChild(TreeServer* Child)
-{
-       std::vector<TreeServer*>::iterator it = std::find(Children.begin(), Children.end(), Child);
-       if (it != Children.end())
+       ConfigTagList tags = ServerInstance->Config->ConfTags("uline");
+       for (ConfigIter i = tags.first; i != tags.second; ++i)
        {
-               Children.erase(it);
-               return true;
+               ConfigTag* tag = i->second;
+               std::string server = tag->getString("server");
+               if (!strcasecmp(server.c_str(), GetName().c_str()))
+               {
+                       if (this->IsRoot())
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Servers should not uline themselves (at " + tag->getTagLocation() + ")");
+                               return;
+                       }
+
+                       uline = true;
+                       silentuline = tag->getBool("silent");
+                       break;
+               }
        }
-       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.
+/** This method is used to add the server to the
+ * maps for linear searches. It is only called
+ * by the constructors.
  */
-bool TreeServer::Tidy()
+void TreeServer::AddHashEntry()
 {
-       while (1)
-       {
-               std::vector<TreeServer*>::iterator a = Children.begin();
-               if (a == Children.end())
-                       return true;
-               TreeServer* s = *a;
-               s->Tidy();
-               s->cull();
-               Children.erase(a);
-               delete s;
-       }
+       Utils->serverlist[GetName()] = this;
+       Utils->sidlist[sid] = this;
 }
 
 CullResult TreeServer::cull()
 {
-       if (ServerUser != ServerInstance->FakeClient)
+       // Recursively cull all servers that are under us in the tree
+       for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
+       {
+               TreeServer* server = *i;
+               server->cull();
+       }
+
+       if (!IsRoot())
                ServerUser->cull();
        return classbase::cull();
 }
 
 TreeServer::~TreeServer()
 {
-       /* We'd better tidy up after ourselves, eh? */
-       this->DelHashEntry();
-       if (ServerUser != ServerInstance->FakeClient)
+       // 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;
+}
 
-       server_hash::iterator iter = Utils->sidlist.find(GetID());
-       if (iter != Utils->sidlist.end())
-               Utils->sidlist.erase(iter);
+void TreeServer::RemoveHash()
+{
+       Utils->sidlist.erase(sid);
+       Utils->serverlist.erase(GetName());
 }
index 60b6d1defc9d7e1dc8e10c45fe1d2a35a61f4466..ffe0373d23ff098437eb87d051e61edf39d0b1ac 100644 (file)
  */
 
 
-#ifndef M_SPANNINGTREE_TREESERVER_H
-#define M_SPANNINGTREE_TREESERVER_H
+#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
  * TreeServer items, deleting and inserting them as they
  * are created and destroyed.
  */
-class TreeServer : public classbase
+class TreeServer : public Server
 {
        TreeServer* Parent;                     /* Parent entry */
        TreeServer* Route;                      /* Route entry */
        std::vector<TreeServer*> Children;      /* List of child objects */
-       irc::string ServerName;                 /* Server's name */
-       std::string ServerDesc;                 /* Server's description */
        std::string VersionString;              /* Version string or empty string */
-       unsigned int ServerUserCount;           /* How many users are on this server? [note: doesn't care about +i] */
-       unsigned int ServerOperCount;           /* How many opers are on this server? */
-       TreeSocket* Socket;                     /* For directly connected servers this points at the socket object */
-       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 */
-       SpanningTreeUtilities* Utils;           /* Utility class */
+
+       /** Full version string including patch version and other info
+        */
+       std::string fullversion;
+       std::string rawversion;
+
+       TreeSocket* Socket;                     /* Socket used to communicate with this server */
        std::string sid;                        /* Server ID */
 
-       /** Set server ID
-        * @param id Server ID
-        * @throws CoreException on duplicate 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 SetID(const std::string &id);
+       void RemoveHash();
 
  public:
+       typedef std::vector<TreeServer*> ChildServers;
        FakeUser* const ServerUser;             /* User representing this server */
-       time_t age;
+       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? */
 
        /** We use this constructor only to create the 'root' item, Utils->TreeRoot, which
         * represents our own server. Therefore, it has no route, no parent, and
         * no socket associated with it. Its version string is our own local version.
         */
-       TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id);
+       TreeServer();
 
        /** 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.
         */
-       TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id, TreeServer* Above, TreeSocket* Sock, bool Hide);
+       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);
-
-       /** This method is used to add the structure to the
-        * hash_map for linear searches. It is only called
-        * by the constructors.
+       /** 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 AddHashEntry();
+       void SQuitChild(TreeServer* server, const std::string& reason);
 
-       /** This method removes the reference to this object
-        * from the hash_map which is used for linear searches.
-        * It is only called by the default destructor.
+       /** 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 DelHashEntry();
+       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; }
 
-       /** Get server name
+       /** Returns true if this server is the tree root (i.e.: us)
         */
-       std::string GetName();
+       bool IsRoot() const { return (this->Parent == NULL); }
 
-       /** Get server description (GECOS)
+       /** Returns true if this server is locally connected
         */
-       const std::string& GetDesc();
+       bool IsLocal() const { return (this->Route == this); }
 
-       /** Get server version string
+       /** Returns true if the server is awaiting destruction
+        * @return True if the server is waiting to be culled and deleted, false otherwise
         */
-       const std::string& GetVersion();
+       bool IsDead() const { return isdead; }
 
-       /** Set time we are next due to ping this server
+       /** Get server version string
         */
-       void SetNextPingTime(time_t t);
+       const std::string& GetVersion() const { return VersionString; }
 
-       /** Get the time we are next due to ping this server
+       /** Get the full version string of this server
+        * @return The full version string of this server, including patch version and other info
         */
-       time_t NextPingTime();
+       const std::string& GetFullVersion() const { return fullversion; }
 
-       /** Last ping time in milliseconds, used to calculate round trip time
+       /** Get the raw version string of this server
         */
-       unsigned long LastPingMsec;
+       const std::string& GetRawVersion() const { return rawversion; }
 
        /** Round trip time of last ping
         */
        unsigned long rtt;
 
-       /** When we recieved BURST from this server, used to calculate total burst time at ENDBURST.
+       /** When we received 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
+       /** Get the TreeSocket pointer for local servers.
+        * For remote servers, this returns NULL.
         */
-       bool AnsweredLastPing();
+       TreeSocket* GetSocket() const { return Socket; }
 
-       /** Set the server as responding to its last ping
+       /** Get the parent server.
+        * For the root node, this returns NULL.
         */
-       void SetPingFlag();
+       TreeServer* GetParent() const { return Parent; }
 
-       /** Get the number of users on this server.
+       /** Set the server version string
         */
-       unsigned int GetUserCount();
+       void SetVersion(const std::string& verstr) { VersionString = verstr; }
 
-       /** Increment or decrement the user count by diff.
+       /** Set the full version string
+        * @param verstr The version string to set
         */
-       void SetUserCount(int diff);
+       void SetFullVersion(const std::string& verstr) { fullversion = verstr; }
 
-       /** Gets the numbers of opers on this server.
+       /** Set the raw version string
         */
-       unsigned int GetOperCount();
+       void SetRawVersion(const std::string& verstr) { rawversion = verstr; }
 
-       /** Increment or decrement the oper count by diff.
+       /** 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
         */
-       void SetOperCount(int diff);
+       void SetDesc(const std::string& descstr) { description = descstr; }
 
-       /** Get the TreeSocket pointer for local servers.
-        * For remote servers, this returns NULL.
+       /** Return all child servers
         */
-       TreeSocket* GetSocket();
+       const ChildServers& GetChildren() const { return Children; }
 
-       /** Get the parent server.
-        * For the root node, this returns NULL.
+       /** Get server ID
         */
-       TreeServer* GetParent();
+       const std::string& GetID() const { return sid; }
 
-       /** Set the server version string
+       /** Marks a server as having finished bursting and performs appropriate actions.
         */
-       void SetVersion(const std::string &Version);
+       void FinishBurst();
+       /** Recursive call for child servers */
+       void FinishBurstInternal();
 
-       /** Return number of child servers
+       /** (Re)check the uline state of this server
         */
-       unsigned int ChildCount();
+       void CheckULine();
 
-       /** Return a child server indexed 0..n
+       /** Get the bursting state of this server
+        * @return True if this server is bursting, false if it isn't
         */
-       TreeServer* GetChild(unsigned int n);
+       bool IsBursting() const { return (StartBurst != 0); }
 
-       /** Add a child server
+       /** 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
         */
-       void AddChild(TreeServer* Child);
+       bool IsBehindBursting() const { return (behind_bursting != 0); }
 
-       /** Delete a child server, return false if it didn't exist.
+       /** Set the bursting state of the server
+        * @param startms Time the server started bursting, if 0 or omitted, use current time
         */
-       bool DelChild(TreeServer* Child);
+       void BeginBurst(uint64_t startms = 0);
 
-       /** 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.
+       /** Register a PONG from the server
         */
-       bool Tidy();
+       void OnPong() { pingtimer.OnPong(); }
 
-       /** Get server ID
-        */
-       const std::string& GetID();
+       CullResult cull() CXX11_OVERRIDE;
 
-       /** Marks a server as having finished bursting and performs appropriate actions.
+       /** Destructor, deletes ServerUser unless IsRoot()
         */
-       void FinishBurst();
-       /** Recursive call for child servers */
-       void FinishBurstInternal();
+       ~TreeServer();
 
-       CullResult cull();
-       /** Destructor
+       /** Returns the TreeServer the given user is connected to
+        * @param user The user whose server to return
+        * @return The TreeServer this user is connected to.
         */
-       ~TreeServer();
+       static TreeServer* Get(User* user)
+       {
+               return static_cast<TreeServer*>(user->server);
+       }
 };
-
-#endif
index efcce5f7a3e46463eb71eb061d0c525f06da7a1d..547c87195f06105fa2552d14d634f2ee54dc6fae 100644 (file)
  */
 
 
-#ifndef M_SPANNINGTREE_TREESOCKET_H
-#define M_SPANNINGTREE_TREESOCKET_H
+#pragma once
 
-#include "socket.h"
 #include "inspircd.h"
-#include "xline.h"
 
 #include "utils.h"
 
@@ -76,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
@@ -92,39 +89,92 @@ struct CapabData
  */
 class TreeSocket : public BufferedSocket
 {
-       SpanningTreeUtilities* Utils;           /* Utility class */
+       struct BurstState;
+
        std::string linkID;                     /* Description for this link */
        ServerState LinkState;                  /* Link state */
        CapabData* capab;                       /* Link setup data (held until burst is sent) */
        TreeServer* MyRoot;                     /* The server we are talking to */
-       int proto_version;                      /* Remote protocol version */
-       bool ConnectionFailureShown; /* Set to true if a connection failure message was shown */
+       unsigned int proto_version;                     /* Remote protocol version */
 
-       static const unsigned int FMODE_MAX_LENGTH = 350;
+       /** 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
         */
        bool CheckDuplicate(const std::string& servername, const std::string& sid);
 
+       /** Send all ListModeBase modes set on the channel
+        */
+       void SendListModes(Channel* chan);
+
+       /** Send all known information about a channel */
+       void SyncChannel(Channel* chan, BurstState& bs);
+
+       /** 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 CommandBase::Params& 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:
-       time_t age;
+       const time_t age;
 
        /** 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.
         */
-       TreeSocket(SpanningTreeUtilities* Util, Link* link, Autoconnect* myac, const std::string& ipaddr);
+       TreeSocket(Link* link, Autoconnect* myac, const irc::sockets::sockaddrs& sa);
 
        /** 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.
         */
-       TreeSocket(SpanningTreeUtilities* Util, int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
+       TreeSocket(int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
 
        /** Get link state
         */
-       ServerState GetLinkState();
+       ServerState GetLinkState() const { return LinkState; }
 
        /** Get challenge set in our CAPAB for challenge/response
         */
@@ -150,7 +200,7 @@ class TreeSocket : public BufferedSocket
         */
        void CleanNegotiationInfo();
 
-       CullResult cull();
+       CullResult cull() CXX11_OVERRIDE;
        /** Destructor
         */
        ~TreeSocket();
@@ -166,11 +216,11 @@ class TreeSocket : public BufferedSocket
         * to server docs on the inspircd.org site, the other side
         * will then send back its own server string.
         */
-       virtual void OnConnected();
+       void OnConnected() CXX11_OVERRIDE;
 
        /** Handle socket error event
         */
-       virtual void OnError(BufferedSocketError e);
+       void OnError(BufferedSocketError e) CXX11_OVERRIDE;
 
        /** Sends an error to the remote server, and displays it locally to show
         * that it was sent.
@@ -180,48 +230,28 @@ class TreeSocket : public BufferedSocket
        /** Recursively send the server tree with distances as hops.
         * This is used during network burst to inform the other server
         * (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 hopcounts given
-        * by this function are relative, this doesn't matter so long as
-        * they are all >1, as all the remote servers re-calculate them
-        * to be relative too, with themselves as hop 0.
         */
-       void SendServers(TreeServer* Current, TreeServer* s, int hops);
+       void SendServers(TreeServer* Current, TreeServer* s);
 
        /** Returns module list as a string, filtered by filter
         * @param filter a module version bitmask, such as VF_COMMON or VF_OPTCOMMON
         */
        std::string MyModules(int filter);
 
+       /** Returns mode list as a string, filtered by type.
+        * @param type The type of modes to return.
+        */
+       std::string BuildModeList(ModeType type);
+
        /** Send my capabilities to the remote side
         */
        void SendCapabilities(int phase);
 
-       /** Add modules to VF_COMMON list for backwards compatability */
-       void CompatAddModules(std::vector<std::string>& modlist);
-
        /* Isolate and return the elements that are different between two lists */
        void ListDifference(const std::string &one, const std::string &two, char sep,
                std::string& mleft, std::string& mright);
 
-       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);
-
-       /* Used on nick collision ... XXX ugly function HACK */
-       int DoCollision(User *u, time_t remotets, const std::string &remoteident, const std::string &remoteip, const std::string &remoteuid);
+       bool Capab(const CommandBase::Params& params);
 
        /** Send one or more FJOINs for a channel of users.
         * If the length of a single line is more than 480-NICKMAX
@@ -229,14 +259,11 @@ class TreeSocket : public BufferedSocket
         */
        void SendFJoins(Channel* c);
 
-       /** Send G, Q, Z and E lines */
+       /** Send G-, Q-, Z- and E-lines */
        void SendXLines();
 
-       /** Send channel modes and topics */
-       void SendChannelModes();
-
-       /** send all users and their oper state/modes */
-       void SendUsers();
+       /** Send all known information about a channel */
+       void SyncChannel(Channel* chan);
 
        /** This function is called when we want to send a netburst to a local
         * server. There is a set order we must do this, because for example
@@ -248,90 +275,45 @@ class TreeSocket : public BufferedSocket
        /** This function is called when we receive data from a remote
         * server.
         */
-       void OnDataReady();
+       void OnDataReady() CXX11_OVERRIDE;
 
        /** Send one or more complete lines down the socket
         */
-       void WriteLine(std::string line);
+       void WriteLine(const std::string& line);
 
        /** Handle ERROR command */
-       void Error(parameterlist &params);
-
-       /** Remote AWAY */
-       bool Away(const std::string &prefix, parameterlist &params);
-
-       /** SAVE to resolve nick collisions without killing */
-       bool ForceNick(const std::string &prefix, parameterlist &params);
-
-       /** ENCAP command
-        */
-       void Encap(User* who, parameterlist &params);
-
-       /** OPERQUIT command
-        */
-       bool OperQuit(const std::string &prefix, parameterlist &params);
-
-       /** PONG
-        */
-       bool LocalPong(const std::string &prefix, parameterlist &params);
-
-       /** VERSION
-        */
-       bool ServerVersion(const std::string &prefix, parameterlist &params);
-
-       /** ADDLINE
-        */
-       bool AddLine(const std::string &prefix, parameterlist &params);
-
-       /** DELLINE
-        */
-       bool DelLine(const std::string &prefix, parameterlist &params);
-
-       /** WHOIS
-        */
-       bool Whois(const std::string &prefix, parameterlist &params);
-
-       /** PUSH
-        */
-       bool Push(const std::string &prefix, parameterlist &params);
-
-       /** PING
-        */
-       bool LocalPing(const std::string &prefix, parameterlist &params);
-
-       /** <- (remote) <- SERVER
-        */
-       bool RemoteServer(const std::string &prefix, parameterlist &params);
+       void Error(CommandBase::Params& params);
 
        /** (local) -> SERVER
         */
-       bool Outbound_Reply_Server(parameterlist &params);
+       bool Outbound_Reply_Server(CommandBase::Params& params);
 
        /** (local) <- SERVER
         */
-       bool Inbound_Server(parameterlist &params);
+       bool Inbound_Server(CommandBase::Params& params);
 
        /** Handle IRC line split
         */
-       void Split(const std::string &line, std::string& prefix, std::string& command, parameterlist &params);
+       void Split(const std::string& line, std::string& tags, std::string& prefix, std::string& command, CommandBase::Params& params);
 
        /** Process complete line from buffer
         */
        void ProcessLine(std::string &line);
 
-       void ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params);
+       /** Process message tags received from a remote server. */
+       void ProcessTag(User* source, const std::string& tag, ClientProtocol::TagMap& tags);
+
+       /** Process a message for a fully connected server. */
+       void ProcessConnectedLine(std::string& tags, std::string& prefix, std::string& command, CommandBase::Params& params);
 
        /** Handle socket timeout from connect()
         */
-       virtual void OnTimeout();
+       void OnTimeout() CXX11_OVERRIDE;
        /** Handle server quit on close
         */
-       virtual void Close();
+       void Close() CXX11_OVERRIDE;
 
-       /** Returns true if this server was introduced to the rest of the network
+       /** Fixes messages coming from old servers so the new command handlers understand them
         */
-       bool Introduced();
+       bool PreProcessOldProtocolMessage(User*& who, std::string& cmd, CommandBase::Params& params);
 };
-
-#endif
-
index c9729cc0f3a6837c96d3d290b1e44c0ad8e9887b..190c5dc15891ac22c2d708532fff6c437ec398be 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
+#include "iohook.h"
 
 #include "main.h"
-#include "../spanningtree.h"
+#include "modules/server.h"
 #include "utils.h"
 #include "treeserver.h"
 #include "link.h"
 #include "treesocket.h"
-#include "resolvers.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(SpanningTreeUtilities* Util, Link* link, Autoconnect* myac, const std::string& ipaddr)
-       : Utils(Util)
+TreeSocket::TreeSocket(Link* link, Autoconnect* myac, const irc::sockets::sockaddrs& dest)
+       : linkID(link->Name), LinkState(CONNECTING), MyRoot(NULL), proto_version(0)
+       , burstsent(false), age(ServerInstance->Time())
 {
-       age = ServerInstance->Time();
-       linkID = assign(link->Name);
        capab = new CapabData;
        capab->link = link;
        capab->ac = myac;
        capab->capab_phase = 0;
-       MyRoot = NULL;
-       proto_version = 0;
-       ConnectionFailureShown = false;
-       LinkState = CONNECTING;
-       if (!link->Hook.empty())
+
+       irc::sockets::sockaddrs bind;
+       memset(&bind, 0, sizeof(bind));
+       if (!link->Bind.empty() && (dest.family() == AF_INET || dest.family() == AF_INET6))
        {
-               ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, link->Hook);
-               if (!prov)
+               if (!irc::sockets::aptosa(link->Bind, 0, bind))
+               {
+                       state = I_ERROR;
+                       SetError("Bind address '" + link->Bind + "' is not a valid IPv4 or IPv6 address");
+                       TreeSocket::OnError(I_ERR_BIND);
+                       return;
+               }
+               else if (bind.family() != dest.family())
                {
-                       SetError("Could not find hook '" + link->Hook + "' for connection to " + linkID);
+                       state = I_ERROR;
+                       SetError("Bind address '" + bind.addr() + "' is not the same address family as destination address '" + dest.addr() + "'");
+                       TreeSocket::OnError(I_ERR_BIND);
                        return;
                }
-               AddIOHook(prov->creator);
        }
-       DoConnect(ipaddr, link->Port, link->Timeout, link->Bind);
-       Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, link->Timeout);
+
+       DoConnect(dest, bind, link->Timeout);
+       Utils->timeoutlist[this] = std::pair<std::string, unsigned int>(linkID, link->Timeout);
        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(SpanningTreeUtilities* Util, int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
-       : BufferedSocket(newfd), Utils(Util)
+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)
+       , burstsent(false), age(ServerInstance->Time())
 {
        capab = new CapabData;
        capab->capab_phase = 0;
-       MyRoot = NULL;
-       age = ServerInstance->Time();
-       LinkState = WAIT_AUTH_1;
-       proto_version = 0;
-       ConnectionFailureShown = false;
-       linkID = "inbound from " + client->addr();
 
-       FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
-       if (GetIOHook())
-               GetIOHook()->OnStreamSocketAccept(this, client, server);
-       SendCapabilities(1);
+       for (ListenSocket::IOHookProvList::iterator i = via->iohookprovs.begin(); i != via->iohookprovs.end(); ++i)
+       {
+               ListenSocket::IOHookProvRef& iohookprovref = *i;
+               if (!iohookprovref)
+                       continue;
 
-       Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, 30);
-}
+               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;
+               }
+       }
 
-ServerState TreeSocket::GetLinkState()
-{
-       return this->LinkState;
+       SendCapabilities(1);
+
+       Utils->timeoutlist[this] = std::pair<std::string, unsigned int>(linkID, 30);
 }
 
 void TreeSocket::CleanNegotiationInfo()
@@ -114,21 +119,31 @@ CullResult TreeSocket::cull()
 
 TreeSocket::~TreeSocket()
 {
-       if (capab)
-               delete capab;
+       delete capab;
 }
 
 /** 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()
 {
        if (this->LinkState == CONNECTING)
        {
-               ServerInstance->SNO->WriteGlobalSno('l', "Connection to \2%s\2[%s] started.", linkID.c_str(),
+               if (!capab->link->Hook.empty())
+               {
+                       ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, capab->link->Hook);
+                       if (!prov)
+                       {
+                               SetError("Could not find hook '" + capab->link->Hook + "' for connection to " + linkID);
+                               return;
+                       }
+                       static_cast<IOHookProvider*>(prov)->OnConnect(this);
+               }
+
+               ServerInstance->SNO->WriteGlobalSno('l', "Connection to \002%s\002[%s] started.", linkID.c_str(),
                        (capab->link->HiddenFromStats ? "<hidden>" : capab->link->IPAddr.c_str()));
                this->SendCapabilities(1);
        }
@@ -139,6 +154,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)
@@ -149,79 +165,31 @@ 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)
+CmdResult CommandSQuit::HandleServer(TreeServer* server, CommandBase::Params& params)
 {
-       std::string servername = Current->GetName();
-       ServerInstance->Logs->Log("m_spanningtree",DEBUG,"SquitServer for %s from %s",
-               servername.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 ;)
-        */
-       for (unsigned int q = 0; q < Current->ChildCount(); q++)
+       TreeServer* quitting = Utils->FindServer(params[0]);
+       if (!quitting)
        {
-               TreeServer* recursive_server = Current->GetChild(q);
-               this->SquitServer(from,recursive_server, num_lost_servers, num_lost_users);
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Squit from unknown server");
+               return CMD_FAILURE;
        }
-       /* 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) && (Current != Utils->TreeRoot))
+       CmdResult ret = CMD_SUCCESS;
+       if (quitting == server)
        {
-               DelServerEvent(Utils->Creator, Current->GetName());
+               ret = CMD_FAILURE;
+               server = server->GetParent();
+       }
+       else if (quitting->GetParent() != server)
+               throw ProtocolException("Attempted to SQUIT a non-directly connected server or the parent");
 
-               if (!Current->GetSocket() || Current->GetSocket()->Introduced())
-               {
-                       parameterlist params;
-                       params.push_back(Current->GetID());
-                       params.push_back(":"+reason);
-                       Utils->DoOneToAllButSender(Current->GetParent()->GetID(),"SQUIT",params,Current->GetID());
-               }
+       server->SQuitChild(quitting, params[1]);
 
-               if (Current->GetParent() == Utils->TreeRoot)
-               {
-                       ServerInstance->SNO->WriteGlobalSno('l', "Server \002"+Current->GetName()+"\002 split: "+reason);
-                       LocalSquit = true;
-               }
-               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();
-               SquitServer(from, Current, num_lost_servers, num_lost_users);
-               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();
-               }
-       }
-       else
-               ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Squit from unknown server");
+       // XXX: Return CMD_FAILURE when servers SQUIT themselves (i.e. :00S SQUIT 00S :Shutting down)
+       // 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 ret;
 }
 
 /** This function is called when we receive data from a remote
@@ -235,13 +203,24 @@ 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");
                        break;
                }
-               ProcessLine(line);
+
+               try
+               {
+                       ProcessLine(line);
+               }
+               catch (CoreException& ex)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error while processing: " + line);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason());
+                       SendError(ex.GetReason() + " - check the log file for details");
+               }
+
                if (!getError().empty())
                        break;
        }
@@ -249,8 +228,3 @@ void TreeSocket::OnDataReady()
                SendError("RecvQ overrun (line too long)");
        Utils->Creator->loopCall = false;
 }
-
-bool TreeSocket::Introduced()
-{
-       return (capab == NULL);
-}
index acb822fbfa169419b2021fca91b9c4d2c984b49e..05d85aa67fa287b605daff0b6d9e34e1e011925e 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
 
 #include "main.h"
 #include "utils.h"
 #include "treeserver.h"
-#include "link.h"
 #include "treesocket.h"
 #include "resolvers.h"
+#include "commands.h"
 
 /* Handle ERROR command */
-void TreeSocket::Error(parameterlist &params)
+void TreeSocket::Error(CommandBase::Params& params)
 {
        std::string msg = params.size() ? params[0] : "";
        SetError("received ERROR " + msg);
 }
 
-void TreeSocket::Split(const std::string& line, std::string& prefix, std::string& command, parameterlist& params)
+void TreeSocket::Split(const std::string& line, std::string& tags, std::string& prefix, std::string& command, CommandBase::Params& params)
 {
+       std::string token;
        irc::tokenstream tokens(line);
 
-       if (!tokens.GetToken(prefix))
+       if (!tokens.GetMiddle(token))
                return;
-       
-       if (prefix[0] == ':')
-       {
-               prefix = prefix.substr(1);
 
-               if (prefix.empty())
+       if (token[0] == '@')
+       {
+               if (token.length() <= 1)
                {
-                       this->SendError("BUG (?) Empty prefix received: " + line);
+                       this->SendError("BUG: Received a message with empty tags: " + line);
                        return;
                }
-               if (!tokens.GetToken(command))
+
+               tags.assign(token, 1, std::string::npos);
+               if (!tokens.GetMiddle(token))
                {
-                       this->SendError("BUG (?) Empty command received: " + line);
+                       this->SendError("BUG: Received a message with no command: " + line);
                        return;
                }
        }
-       else
-       {
-               command = prefix;
-               prefix.clear();
-       }
-       if (command.empty())
-               this->SendError("BUG (?) Empty command received: " + line);
 
-       std::string param;
-       while (tokens.GetToken(param))
+       if (token[0] == ':')
        {
-               params.push_back(param);
+               if (token.length() <= 1)
+               {
+                       this->SendError("BUG: Received a message with an empty prefix: " + line);
+                       return;
+               }
+
+               prefix.assign(token, 1, std::string::npos);
+               if (!tokens.GetMiddle(token))
+               {
+                       this->SendError("BUG: Received a message with no command: " + line);
+                       return;
+               }
        }
+
+       command.assign(token);
+       while (tokens.GetTrailing(token))
+               params.push_back(token);
 }
 
 void TreeSocket::ProcessLine(std::string &line)
 {
+       std::string tags;
        std::string prefix;
        std::string command;
-       parameterlist params;
+       CommandBase::Params params;
 
-       ServerInstance->Logs->Log("m_spanningtree", RAWIO, "S[%d] I %s", this->GetFd(), line.c_str());
+       ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] I %s", this->GetFd(), line.c_str());
 
-       Split(line, prefix, command, params);
+       Split(line, tags, prefix, command, params);
 
        if (command.empty())
                return;
@@ -151,17 +157,17 @@ void TreeSocket::ProcessLine(std::string &line)
                        {
                                if (params.size())
                                {
-                                       time_t them = atoi(params[0].c_str());
+                                       time_t them = ConvToNum<time_t>(params[0]);
                                        time_t delta = them - ServerInstance->Time();
                                        if ((delta < -600) || (delta > 600))
                                        {
-                                               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));
+                                               ServerInstance->SNO->WriteGlobalSno('l', "\002ERROR\002: Your clocks are off by %ld seconds (this is more than five minutes). Link aborted, \002PLEASE SYNC YOUR CLOCKS!\002", 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 %ld seconds. Please consider synching your clocks.", labs((long)delta));
+                                               ServerInstance->SNO->WriteGlobalSno('l', "\002WARNING\002: Your clocks are off by %ld seconds. Please consider syncing your clocks.", labs((long)delta));
                                        }
                                }
 
@@ -171,25 +177,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(Utils, capab->name, capab->description, capab->sid, Utils->TreeRoot, this, capab->hidden);
-                               Utils->TreeRoot->AddChild(MyRoot);
-
-                               MyRoot->bursting = true;
-                               this->DoBurst(MyRoot);
-
-                               parameterlist sparams;
-                               sparams.push_back(MyRoot->GetName());
-                               sparams.push_back("*");
-                               sparams.push_back("0");
-                               sparams.push_back(MyRoot->GetID());
-                               sparams.push_back(":" + MyRoot->GetDesc());
-                               Utils->DoOneToAllButSender(ServerInstance->Config->GetSID(), "SERVER", sparams, MyRoot->GetName());
-                               Utils->DoOneToAllButSender(MyRoot->GetID(), "BURST", params, MyRoot->GetName());
+                               FinishAuth(capab->name, capab->sid, capab->description, capab->hidden);
                        }
                        else if (command == "ERROR")
                        {
@@ -228,59 +216,98 @@ void TreeSocket::ProcessLine(std::string &line)
                         *  Credentials have been exchanged, we've gotten their 'BURST' (or sent ours).
                         *  Anything from here on should be accepted a little more reasonably.
                         */
-                       this->ProcessConnectedLine(prefix, command, params);
+                       this->ProcessConnectedLine(tags, prefix, command, params);
                break;
                case DYING:
                break;
        }
 }
 
-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);
-       std::string direction;
+       // 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() == UUID_LENGTH-1) && (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("m_spanningtree", 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;
        }
 
-       // Make sure prefix is still good
-       direction = who->server;
-       prefix = who->uuid;
+       // Unknown prefix
+       return NULL;
+}
+
+void TreeSocket::ProcessTag(User* source, const std::string& tag, ClientProtocol::TagMap& tags)
+{
+       std::string tagkey;
+       std::string tagval;
+       const std::string::size_type p = tag.find('=');
+       if (p != std::string::npos)
+       {
+               // Tag has a value
+               tagkey.assign(tag, 0, p);
+               tagval.assign(tag, p + 1, std::string::npos);
+       }
+       else
+       {
+               tagkey.assign(tag);
+       }
+
+       const Events::ModuleEventProvider::SubscriberList& list = Utils->Creator->tagevprov.GetSubscribers();
+       for (Events::ModuleEventProvider::SubscriberList::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               ClientProtocol::MessageTagProvider* const tagprov = static_cast<ClientProtocol::MessageTagProvider*>(*i);
+               const ModResult res = tagprov->OnProcessTag(source, tagkey, tagval);
+               if (res == MOD_RES_ALLOW)
+                       tags.insert(std::make_pair(tagkey, ClientProtocol::MessageTagData(tagprov, tagval)));
+               else if (res == MOD_RES_DENY)
+                       break;
+       }
+}
+
+void TreeSocket::ProcessConnectedLine(std::string& taglist, std::string& prefix, std::string& command, CommandBase::Params& 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;
+       }
 
        /*
         * Check for fake direction here, and drop any instances that are found.
@@ -298,214 +325,76 @@ 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 = Utils->BestRouteTo(direction);
-       if ((!route_back_again) || (route_back_again->GetSocket() != this))
+       TreeServer* const server = TreeServer::Get(who);
+       if (server->GetSocket() != this)
        {
-               if (route_back_again)
-                       ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Protocol violation: Fake direction '%s' from connection '%s'",
-                               prefix.c_str(),linkID.c_str());
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Protocol violation: Fake direction '%s' from connection '%s'", prefix.c_str(), linkID.c_str());
                return;
        }
 
-       /*
-        * First up, check for any malformed commands (e.g. MODE without a timestamp)
-        * and rewrite commands where necessary (SVSMODE -> MODE for services). -- w
-        */
-       if (command == "SVSMODE") // This isn't in an "else if" so we still force FMODE for changes on channels.
-               command = "MODE";
-
-       // TODO move all this into Commands
-       if (command == "MAP")
-       {
-               Utils->Creator->HandleMap(params, who);
-       }
-       else if (command == "SERVER")
-       {
-               this->RemoteServer(prefix,params);
-       }
-       else if (command == "ERROR")
-       {
-               this->Error(params);
-       }
-       else if (command == "AWAY")
-       {
-               this->Away(prefix,params);
-       }
-       else if (command == "PING")
-       {
-               this->LocalPing(prefix,params);
-       }
-       else if (command == "PONG")
-       {
-               TreeServer *s = Utils->FindServer(prefix);
-               if (s && s->bursting)
-               {
-                       ServerInstance->SNO->WriteGlobalSno('l',"Server \002%s\002 has not finished burst, forcing end of burst (send ENDBURST!)", prefix.c_str());
-                       s->FinishBurst();
-               }
-               this->LocalPong(prefix,params);
-       }
-       else if (command == "VERSION")
-       {
-               this->ServerVersion(prefix,params);
-       }
-       else if (command == "ADDLINE")
-       {
-               this->AddLine(prefix,params);
-       }
-       else if (command == "DELLINE")
-       {
-               this->DelLine(prefix,params);
-       }
-       else if (command == "SAVE")
+       // Translate commands coming from servers using an older protocol
+       if (proto_version < ProtocolVersion)
        {
-               this->ForceNick(prefix,params);
-       }
-       else if (command == "OPERQUIT")
-       {
-               this->OperQuit(prefix,params);
-       }
-       else if (command == "IDLE")
-       {
-               this->Whois(prefix,params);
-       }
-       else if (command == "PUSH")
-       {
-               this->Push(prefix,params);
-       }
-       else if (command == "SQUIT")
-       {
-               if (params.size() == 2)
-               {
-                       this->Squit(Utils->FindServer(params[0]),params[1]);
-               }
-       }
-       else if (command == "SNONOTICE")
-       {
-               if (params.size() >= 2)
-               {
-                       ServerInstance->SNO->WriteToSnoMask(params[0][0], "From " + who->nick + ": "+ params[1]);
-                       params[1] = ":" + params[1];
-                       Utils->DoOneToAllButSender(prefix, command, params, prefix);
-               }
-       }
-       else if (command == "BURST")
-       {
-               // Set prefix server as bursting
-               TreeServer* ServerSource = Utils->FindServer(prefix);
-               if (!ServerSource)
-               {
-                       ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got BURST from a non-server(?): %s", prefix.c_str());
+               if (!PreProcessOldProtocolMessage(who, command, params))
                        return;
-               }
-
-               ServerSource->bursting = true;
-               Utils->DoOneToAllButSender(prefix, command, params, prefix);
        }
-       else if (command == "ENDBURST")
-       {
-               TreeServer* ServerSource = Utils->FindServer(prefix);
-               if (!ServerSource)
-               {
-                       ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got ENDBURST from a non-server(?): %s", prefix.c_str());
-                       return;
-               }
 
-               ServerSource->FinishBurst();
-               Utils->DoOneToAllButSender(prefix, command, params, prefix);
-       }
-       else if (command == "ENCAP")
-       {
-               this->Encap(who, params);
-       }
-       else if (command == "NICK")
+       ServerCommand* scmd = Utils->Creator->CmdManager.GetHandler(command);
+       CommandBase* cmdbase = scmd;
+       Command* cmd = NULL;
+       if (!scmd)
        {
-               if (params.size() != 2)
-               {
-                       SendError("Protocol violation: Wrong number of parameters for NICK message");
-                       return;
-               }
-
-               if (IS_SERVER(who))
-               {
-                       SendError("Protocol violation: Server changing nick");
-                       return;
-               }
-
-               if ((isdigit(params[0][0])) && (params[0] != who->uuid))
-               {
-                       SendError("Protocol violation: User changing nick to an invalid UID - " + params[0]);
-                       return;
-               }
-
-               /* Update timestamp on user when they change nicks */
-               who->age = atoi(params[1].c_str());
-
-               /*
-                * On nick messages, check that the nick doesnt already exist here.
-                * If it does, perform collision logic.
-                */
-               bool callfnc = true;
-               User* x = ServerInstance->FindNickOnly(params[0]);
-               if ((x) && (x != who) && (x->registered == REG_ALL))
+               // Not a special server-to-server command
+               cmd = ServerInstance->Parser.GetHandler(command);
+               if (!cmd)
                {
-                       int collideret = 0;
-                       /* x is local, who is remote */
-                       collideret = this->DoCollision(x, who->age, who->ident, who->GetIPString(), who->uuid);
-                       if (collideret != 1)
+                       if (command == "ERROR")
+                       {
+                               this->Error(params);
+                               return;
+                       }
+                       else if (command == "BURST")
                        {
-                               // Remote client lost, or both lost, rewrite this nick change as a change to uuid before
-                               // forwarding and don't call ForceNickChange() because DoCollision() has done it already
-                               params[0] = who->uuid;
-                               callfnc = false;
+                               // This is sent even when there is no need for it, drop it here for now
+                               return;
                        }
+
+                       throw ProtocolException("Unknown command: " + command);
                }
-               if (callfnc)
-                       who->ForceNickChange(params[0].c_str());
-               Utils->RouteCommand(route_back_again, command, params, who);
+               cmdbase = cmd;
        }
-       else
-       {
-               Command* cmd = ServerInstance->Parser->GetHandler(command);
-               
-               if (!cmd)
-               {
-                       irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
-                       ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Unrecognised S2S command :%s %s %s",
-                               who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
-                       SendError("Unrecognised command '" + command + "' -- possibly loaded mismatched modules");
-                       return;
-               }
 
-               if (params.size() < cmd->min_params)
-               {
-                       irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
-                       ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Insufficient parameters for S2S command :%s %s %s",
-                               who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
-                       SendError("Insufficient parameters for command '" + command + "'");
+       if (params.size() < cmdbase->min_params)
+               throw ProtocolException("Insufficient parameters");
+
+       if ((!params.empty()) && (params.back().empty()) && (!cmdbase->allow_empty_last_param))
+       {
+               // the last param is empty and the command handler doesn't allow that, check if there will be enough params if we drop the last
+               if (params.size()-1 < cmdbase->min_params)
                        return;
-               }
+               params.pop_back();
+       }
 
-               if ((!params.empty()) && (params.back().empty()) && (!cmd->allow_empty_last_param))
-               {
-                       // the last param is empty and the command handler doesn't allow that, check if there will be enough params if we drop the last
-                       if (params.size()-1 < cmd->min_params)
-                               return;
-                       params.pop_back();
-               }
+       CmdResult res;
+       ClientProtocol::TagMap tags;
+       std::string tag;
+       irc::sepstream tagstream(taglist, ';');
+       while (tagstream.GetToken(tag))
+               ProcessTag(who, tag, tags);
 
-               CmdResult res = cmd->Handle(params, who);
+       CommandBase::Params newparams(params, tags);
 
+       if (scmd)
+               res = scmd->Handle(who, newparams);
+       else
+       {
+               res = cmd->Handle(who, newparams);
                if (res == CMD_INVALID)
-               {
-                       irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
-                       ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Error handling S2S command :%s %s %s",
-                               who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
-                       SendError("Error handling '" + command + "' -- possibly loaded mismatched modules");
-               }
-               else if (res == CMD_SUCCESS)
-                       Utils->RouteCommand(route_back_again, command, params, who);
+                       throw ProtocolException("Error in command handler");
        }
+
+       if (res == CMD_SUCCESS)
+               Utils->RouteCommand(server->GetRoute(), cmdbase, newparams, who);
 }
 
 void TreeSocket::OnTimeout()
@@ -515,8 +404,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");
 
@@ -524,18 +415,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 '\002%s\002' failed.", linkID.c_str());
 
-               time_t server_uptime = ServerInstance->Time() - this->age;
-               if (server_uptime)
-               {
-                       std::string timestr = Utils->Creator->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 '\002%s\002' 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);
+}
index 6620dd13ac8e2aeee81b06c344da5d73dc1e8143..0729065fcf5b39ba91d18b9bad423f185e911d03 100644 (file)
 #include "commands.h"
 
 #include "utils.h"
-#include "link.h"
-#include "treesocket.h"
 #include "treeserver.h"
-#include "resolvers.h"
+#include "remoteuser.h"
 
-CmdResult CommandUID::Handle(const parameterlist &params, User* serversrc)
+CmdResult CommandUID::HandleServer(TreeServer* remoteserver, CommandBase::Params& params)
 {
-       SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
-       /** 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
+        * UID uuid age nick host dhost ident ip.string signon +modes (modepara) :real
         */
-       time_t age_t = ConvToInt(params[1]);
-       time_t signon = ConvToInt(params[7]);
+       time_t age_t = ServerCommand::ExtractTS(params[1]);
+       time_t signon = ServerCommand::ExtractTS(params[7]);
        std::string empty;
-       std::string modestr(params[8]);
-
-       TreeServer* remoteserver = Utils->FindServer(serversrc->server);
-
-       if (!remoteserver)
-               return CMD_INVALID;
-       /* Is this a valid UID, and not misrouted? */
-       if (params[0].length() != 9 || params[0].substr(0,3) != serversrc->uuid)
-               return CMD_INVALID;
-       /* Check parameters for validity before introducing the client, discovered by dmb */
-       if (!age_t)
-               return CMD_INVALID;
-       if (!signon)
-               return CMD_INVALID;
-       if (modestr[0] != '+')
-               return CMD_INVALID;
-       TreeSocket* sock = remoteserver->GetRoute()->GetSocket();
+       const std::string& modestr = params[8];
 
-       /* check for collision */
-       User* const collideswith = ServerInstance->FindNickOnly(params[2]);
+       // 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");
+       // Sanity check on mode string: must begin with '+'
+       if (modestr[0] != '+')
+               throw ProtocolException("Invalid mode string");
 
+       // See if there is a nick collision
+       User* collideswith = ServerInstance->FindNickOnly(params[2]);
        if ((collideswith) && (collideswith->registered != REG_ALL))
        {
                // 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
-               collideswith->WriteFrom(collideswith, "NICK %s", collideswith->uuid.c_str());
-               collideswith->WriteNumeric(433, "%s %s :Nickname overruled.", collideswith->nick.c_str(), collideswith->nick.c_str());
-
-               // Clear the bit before calling User::ChangeNick() to make it NOT run the OnUserPostNick() hook
-               collideswith->registered &= ~REG_NICK;
-               collideswith->ChangeNick(collideswith->uuid, true);
+               LocalUser* const localuser = static_cast<LocalUser*>(collideswith);
+               localuser->OverruleNick();
        }
        else if (collideswith)
        {
-               /*
-                * Nick collision.
-                */
-               int collide = sock->DoCollision(collideswith, age_t, params[5], params[6], params[0]);
-               ServerInstance->Logs->Log("m_spanningtree",DEBUG,"*** Collision on %s, collide=%d", params[2].c_str(), collide);
-
-               if (collide != 1)
+               // 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
-                        *
-                        * This alters the line that will be sent to other servers, which
-                        * commands normally shouldn't do; hence the required const_cast.
-                        */
-                       const_cast<parameterlist&>(params)[2] = params[0];
+                       // 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.
         */
-       User* _new = NULL;
-       try
-       {
-               _new = new RemoteUser(params[0], remoteserver->GetName());
-       }
-       catch (...)
-       {
-               ServerInstance->Logs->Log("m_spanningtree", DEFAULT, "Duplicate UUID %s in client introduction", params[0].c_str());
-               return CMD_INVALID;
-       }
-       (*(ServerInstance->Users->clientlist))[params[2]] = _new;
+       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->ChangeRealHost(params[3], false);
+       _new->ChangeDisplayedHost(params[4]);
        _new->ident = params[5];
-       _new->fullname = params[params.size() - 1];
+       _new->ChangeRealName(params.back());
        _new->registered = REG_ALL;
        _new->signon = signon;
        _new->age = age_t;
 
-       /* we need to remove the + from the modestring, so we can do our stuff */
-       std::string::size_type pos_after_plus = modestr.find_first_not_of('+');
-       if (pos_after_plus != std::string::npos)
-       modestr = modestr.substr(pos_after_plus);
-
        unsigned int paramptr = 9;
-       for (std::string::iterator v = modestr.begin(); v != modestr.end(); v++)
+
+       for (std::string::const_iterator v = modestr.begin(); v != modestr.end(); ++v)
        {
-               /* For each mode thats set, increase counter */
+               // Accept more '+' chars, for now
+               if (*v == '+')
+                       continue;
+
+               /* For each mode thats set, find the mode handler and set it on the new user */
                ModeHandler* mh = ServerInstance->Modes->FindMode(*v, MODETYPE_USER);
+               if (!mh)
+                       throw ProtocolException("Unrecognised mode '" + std::string(1, *v) + "'");
 
-               if (mh)
+               if (mh->NeedsParam(true))
                {
-                       if (mh->GetNumParams(true))
-                       {
-                               if (paramptr >= params.size() - 1)
-                                       return CMD_INVALID;
-                               std::string mp = params[paramptr++];
-                               /* IMPORTANT NOTE:
-                                * All modes are assumed to succeed here as they are being set by a remote server.
-                                * Modes CANNOT FAIL here. If they DO fail, then the failure is ignored. This is important
-                                * to note as all but one modules currently cannot ever fail in this situation, except for
-                                * m_servprotect which specifically works this way to prevent the mode being set ANYWHERE
-                                * but here, at client introduction. You may safely assume this behaviour is standard and
-                                * will not change in future versions if you want to make use of this protective behaviour
-                                * yourself.
-                                */
-                               mh->OnModeChange(_new, _new, NULL, mp, true);
-                       }
-                       else
-                               mh->OnModeChange(_new, _new, NULL, empty, true);
-                       _new->SetMode(*v, true);
+                       if (paramptr >= params.size() - 1)
+                               throw ProtocolException("Out of parameters while processing modes");
+                       std::string mp = params[paramptr++];
+                       /* IMPORTANT NOTE:
+                        * All modes are assumed to succeed here as they are being set by a remote server.
+                        * Modes CANNOT FAIL here. If they DO fail, then the failure is ignored. This is important
+                        * to note as all but one modules currently cannot ever fail in this situation, except for
+                        * m_servprotect which specifically works this way to prevent the mode being set ANYWHERE
+                        * but here, at client introduction. You may safely assume this behaviour is standard and
+                        * will not change in future versions if you want to make use of this protective behaviour
+                        * yourself.
+                        */
+                       mh->OnModeChange(_new, _new, NULL, mp, true);
                }
+               else
+                       mh->OnModeChange(_new, _new, NULL, empty, true);
+               _new->SetMode(mh, true);
        }
 
-       /* now we've done with modes processing, put the + back for remote servers */
-       if (modestr[0] != '+')
-               modestr = "+" + modestr;
-
-       _new->SetClientIP(params[6].c_str());
+       _new->SetClientIP(params[6]);
 
-       ServerInstance->Users->AddGlobalClone(_new);
-       remoteserver->SetUserCount(1); // increment by 1
+       ServerInstance->Users->AddClone(_new);
+       remoteserver->UserCount++;
 
        bool dosend = true;
 
-       if ((Utils->quiet_bursts && remoteserver->bursting) || ServerInstance->SilentULine(_new->server))
+       if ((Utils->quiet_bursts && remoteserver->IsBehindBursting()) || _new->server->IsSilentULine())
                dosend = false;
 
        if (dosend)
-               ServerInstance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s (%s) [%s]", _new->server.c_str(), _new->GetFullRealHost().c_str(), _new->GetIPString(), _new->fullname.c_str());
+               ServerInstance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s (%s) [%s]", remoteserver->GetName().c_str(), _new->GetFullRealHost().c_str(), _new->GetIPString().c_str(), _new->GetRealName().c_str());
 
-       FOREACH_MOD(I_OnPostConnect,OnPostConnect(_new));
+       FOREACH_MOD(OnPostConnect, (_new));
 
        return CMD_SUCCESS;
 }
 
-CmdResult CommandFHost::Handle(const parameterlist &params, User* src)
+CmdResult CommandFHost::HandleRemote(RemoteUser* src, Params& params)
 {
-       if (IS_SERVER(src))
-               return CMD_FAILURE;
-       src->ChangeDisplayedHost(params[0].c_str());
+       src->ChangeDisplayedHost(params[0]);
        return CMD_SUCCESS;
 }
 
-CmdResult CommandFIdent::Handle(const parameterlist &params, User* src)
+CmdResult CommandFIdent::HandleRemote(RemoteUser* src, Params& params)
 {
-       if (IS_SERVER(src))
-               return CMD_FAILURE;
-       src->ChangeIdent(params[0].c_str());
+       src->ChangeIdent(params[0]);
        return CMD_SUCCESS;
 }
 
-CmdResult CommandFName::Handle(const parameterlist &params, User* src)
+CmdResult CommandFName::HandleRemote(RemoteUser* src, Params& params)
 {
-       if (IS_SERVER(src))
-               return CMD_FAILURE;
-       src->ChangeName(params[0].c_str());
+       src->ChangeRealName(params[0]);
        return CMD_SUCCESS;
 }
 
+CommandUID::Builder::Builder(User* user)
+       : CmdBuilder(TreeServer::Get(user)->GetID(), "UID")
+{
+       push(user->uuid);
+       push_int(user->age);
+       push(user->nick);
+       push(user->GetRealHost());
+       push(user->GetDisplayedHost());
+       push(user->ident);
+       push(user->GetIPString());
+       push_int(user->signon);
+       push(user->GetModeLetters(true));
+       push_last(user->GetRealName());
+}
index 367a3b921a88da46f5d7477c2c8ea17a004a60ac..f78b8d4c02498bebc3aef7d6b86b147de2b6503c 100644 (file)
 
 
 #include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
 
 #include "main.h"
 #include "utils.h"
 #include "treeserver.h"
-#include "link.h"
 #include "treesocket.h"
 #include "resolvers.h"
+#include "commandbuilder.h"
+#include "modules/server.h"
+
+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")
+       if (!stdalgo::string::equalsci(from->bind_tag->getString("type"), "servers"))
                return MOD_RES_PASSTHRU;
 
        std::string incomingip = client->addr();
@@ -45,7 +44,7 @@ ModResult ModuleSpanningTree::OnAcceptConnection(int newsock, ListenSocket* from
                if (*i == "*" || *i == incomingip || irc::sockets::cidr_mask(*i).match(*client))
                {
                        /* we don't need to do anything with the pointer, creating it stores it in the necessary places */
-                       new TreeSocket(Utils, newsock, from, client, server);
+                       new TreeSocket(newsock, from, client, server);
                        return MOD_RES_ALLOW;
                }
        }
@@ -53,18 +52,12 @@ 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 (ServerInstance->IsSID(ServerName))
+       if (InspIRCd::IsSID(ServerName))
                return this->FindServerID(ServerName);
 
-       server_hash::iterator iter = serverlist.find(ServerName.c_str());
+       server_hash::iterator iter = serverlist.find(ServerName);
        if (iter != serverlist.end())
        {
                return iter->second;
@@ -75,41 +68,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)
-{
-       if (ServerName.c_str() == TreeRoot->GetName() || ServerName == ServerInstance->Config->GetSID())
-               return NULL;
-       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)
-               {
-                       Found = FindServer(u->server);
-                       if (Found)
-                               return Found->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)
 {
@@ -130,27 +90,36 @@ TreeServer* SpanningTreeUtilities::FindServerID(const std::string &id)
                return NULL;
 }
 
-SpanningTreeUtilities::SpanningTreeUtilities(ModuleSpanningTree* C) : Creator(C)
+TreeServer* SpanningTreeUtilities::FindRouteTarget(const std::string& target)
 {
-       ServerInstance->Logs->Log("m_spanningtree",DEBUG,"***** Using SID for hash: %s *****", ServerInstance->Config->GetSID().c_str());
+       TreeServer* const server = FindServer(target);
+       if (server)
+               return server;
+
+       User* const user = ServerInstance->FindNick(target);
+       if (user)
+               return TreeServer::Get(user);
+
+       return NULL;
+}
 
-       this->TreeRoot = new TreeServer(this, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc, ServerInstance->Config->GetSID());
-       this->ReadConfiguration();
+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);
 }
 
 CullResult SpanningTreeUtilities::cull()
 {
-       while (TreeRoot->ChildCount())
+       const TreeServer::ChildServers& children = TreeRoot->GetChildren();
+       while (!children.empty())
        {
-               TreeServer* child_server = TreeRoot->GetChild(0);
-               if (child_server)
-               {
-                       TreeSocket* sock = child_server->GetSocket();
-                       sock->Close();
-               }
+               TreeSocket* sock = children.front()->GetSocket();
+               sock->Close();
        }
 
-       for(std::map<TreeSocket*, std::pair<std::string, int> >::iterator i = timeoutlist.begin(); i != timeoutlist.end(); ++i)
+       for(TimeoutList::iterator i = timeoutlist.begin(); i != timeoutlist.end(); ++i)
        {
                TreeSocket* s = i->first;
                s->Close();
@@ -165,26 +134,20 @@ SpanningTreeUtilities::~SpanningTreeUtilities()
        delete TreeRoot;
 }
 
-void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list)
-{
-       if (list.find(server) == list.end())
-               list[server] = server;
-}
-
-/* returns a list of DIRECT servernames for a specific channel */
-void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeServerList &list, char status, const CUList &exempt_list)
+// 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;
        if (status)
        {
-               ModeHandler* mh = ServerInstance->Modes->FindPrefix(status);
+               PrefixMode* mh = ServerInstance->Modes->FindPrefix(status);
                if (mh)
                        minrank = mh->GetPrefixRank();
        }
 
-       const UserMembList *ulist = c->GetUsers();
-
-       for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+       TreeServer::ChildServers children = TreeRoot->GetChildren();
+       const Channel::MemberMap& ulist = c->GetUsers();
+       for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
        {
                if (IS_LOCAL(i->first))
                        continue;
@@ -194,86 +157,48 @@ void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeServerLis
 
                if (exempt_list.find(i->first) == exempt_list.end())
                {
-                       TreeServer* best = this->BestRouteTo(i->first->server);
-                       if (best)
-                               AddThisServer(best,list);
+                       TreeServer* best = TreeServer::Get(i->first);
+                       list.insert(best->GetSocket());
+
+                       TreeServer::ChildServers::iterator citer = std::find(children.begin(), children.end(), best);
+                       if (citer != children.end())
+                               children.erase(citer);
                }
        }
-       return;
-}
 
-bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, const parameterlist &params, const std::string& omit)
-{
-       TreeServer* omitroute = this->BestRouteTo(omit);
-       std::string FullLine = ":" + prefix + " " + command;
-       unsigned int words = params.size();
-       for (unsigned int x = 0; x < words; x++)
+       // Check whether the servers which do not have users in the channel might need this message. This
+       // is used to keep the chanhistory module synchronised between servers.
+       for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
        {
-               FullLine = FullLine + " " + params[x];
+               ModResult result;
+               FIRST_MOD_RESULT_CUSTOM(Creator->GetEventProvider(), ServerEventListener, OnBroadcastMessage, result, (c, *i));
+               if (result == MOD_RES_ALLOW)
+                       list.insert((*i)->GetSocket());
        }
-       unsigned int items = this->TreeRoot->ChildCount();
-       for (unsigned int x = 0; x < items; x++)
-       {
-               TreeServer* Route = this->TreeRoot->GetChild(x);
-               // Send the line IF:
-               // The route has a socket (its a direct connection)
-               // The route isnt the one to be omitted
-               // The route isnt the path to the one to be omitted
-               if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
-               {
-                       TreeSocket* Sock = Route->GetSocket();
-                       if (Sock)
-                               Sock->WriteLine(FullLine);
-               }
-       }
-       return true;
 }
 
-bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist &params)
+void SpanningTreeUtilities::DoOneToAllButSender(const CmdBuilder& params, TreeServer* omitroute)
 {
-       std::string FullLine = ":" + prefix + " " + command;
-       unsigned int words = params.size();
-       for (unsigned int x = 0; x < words; x++)
-       {
-               FullLine = FullLine + " " + params[x];
-       }
-       unsigned int items = this->TreeRoot->ChildCount();
-       for (unsigned int x = 0; x < items; x++)
+       const std::string& FullLine = params.str();
+
+       const TreeServer::ChildServers& children = TreeRoot->GetChildren();
+       for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
        {
-               TreeServer* Route = this->TreeRoot->GetChild(x);
-               if (Route && Route->GetSocket())
+               TreeServer* Route = *i;
+               // Send the line if the route isn't the path to the one to be omitted
+               if (Route != omitroute)
                {
-                       TreeSocket* Sock = Route->GetSocket();
-                       if (Sock)
-                               Sock->WriteLine(FullLine);
+                       Route->GetSocket()->WriteLine(FullLine);
                }
        }
-       return true;
 }
 
-bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, const parameterlist &params, const std::string& target)
+void SpanningTreeUtilities::DoOneToOne(const CmdBuilder& params, Server* server)
 {
-       TreeServer* Route = this->BestRouteTo(target);
-       if (Route)
-       {
-               std::string FullLine = ":" + prefix + " " + command;
-               unsigned int words = params.size();
-               for (unsigned int x = 0; x < words; x++)
-               {
-                       FullLine = FullLine + " " + params[x];
-               }
-               if (Route && Route->GetSocket())
-               {
-                       TreeSocket* Sock = Route->GetSocket();
-                       if (Sock)
-                               Sock->WriteLine(FullLine);
-               }
-               return true;
-       }
-       else
-       {
-               return false;
-       }
+       TreeServer* ts = static_cast<TreeServer*>(server);
+       TreeSocket* sock = ts->GetSocket();
+       if (sock)
+               sock->WriteLine(params);
 }
 
 void SpanningTreeUtilities::RefreshIPCache()
@@ -284,28 +209,27 @@ void SpanningTreeUtilities::RefreshIPCache()
                Link* L = *i;
                if (!L->Port)
                {
-                       ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"m_spanningtree: Ignoring a link block without a port.");
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring a link block without a port.");
                        /* Invalid link block */
                        continue;
                }
 
-               if (L->AllowMask.length())
-                       ValidIPs.push_back(L->AllowMask);
+               ValidIPs.insert(ValidIPs.end(), L->AllowMasks.begin(), L->AllowMasks.end());
 
                irc::sockets::sockaddrs dummy;
                bool ipvalid = irc::sockets::aptosa(L->IPAddr, L->Port, dummy);
                if ((L->IPAddr == "*") || (ipvalid))
                        ValidIPs.push_back(L->IPAddr);
-               else
+               else if (this->Creator->DNS)
                {
+                       SecurityIPResolver* sr = new SecurityIPResolver(Creator, *this->Creator->DNS, L->IPAddr, L, DNS::QUERY_AAAA);
                        try
                        {
-                               bool cached = false;
-                               SecurityIPResolver* sr = new SecurityIPResolver(Creator, this, L->IPAddr, L, cached, DNS_QUERY_AAAA);
-                               ServerInstance->AddResolver(sr, cached);
+                               this->Creator->DNS->Process(sr);
                        }
-                       catch (...)
+                       catch (DNS::Exception &)
                        {
+                               delete sr;
                        }
                }
        }
@@ -317,17 +241,14 @@ void SpanningTreeUtilities::ReadConfiguration()
        ConfigTag* options = ServerInstance->Config->ConfValue("options");
        FlatLinks = security->getBool("flatlinks");
        HideULines = security->getBool("hideulines");
+       HideSplits = security->getBool("hidesplits");
        AnnounceTSChange = options->getBool("announcets");
        AllowOptCommon = options->getBool("allowmismatch");
-       ChallengeResponse = !security->getBool("disablehmac");
        quiet_bursts = ServerInstance->Config->ConfValue("performance")->getBool("quietbursts");
-       PingWarnTime = options->getInt("pingwarning");
-       PingFreq = options->getInt("serverpingfreq");
-
-       if (PingFreq == 0)
-               PingFreq = 60;
+       PingWarnTime = options->getDuration("pingwarning", 15);
+       PingFreq = options->getDuration("serverpingfreq", 60, 1);
 
-       if (PingWarnTime < 0 || PingWarnTime > PingFreq - 1)
+       if (PingWarnTime >= PingFreq)
                PingWarnTime = 0;
 
        AutoconnectBlocks.clear();
@@ -339,14 +260,18 @@ void SpanningTreeUtilities::ReadConfiguration()
                reference<Link> L = new Link(tag);
                std::string linkname = tag->getString("name");
                L->Name = linkname.c_str();
-               L->AllowMask = tag->getString("allowmask");
+
+               irc::spacesepstream sep = tag->getString("allowmask");
+               for (std::string s; sep.GetToken(s);)
+                       L->AllowMasks.push_back(s);
+
                L->IPAddr = tag->getString("ipaddr");
-               L->Port = tag->getInt("port");
+               L->Port = tag->getUInt("port", 0);
                L->SendPass = tag->getString("sendpass", tag->getString("password"));
                L->RecvPass = tag->getString("recvpass", tag->getString("password"));
                L->Fingerprint = tag->getString("fingerprint");
                L->HiddenFromStats = tag->getBool("statshidden");
-               L->Timeout = tag->getInt("timeout", 30);
+               L->Timeout = tag->getDuration("timeout", 30);
                L->Hook = tag->getString("ssl");
                L->Bind = tag->getString("bind");
                L->Hidden = tag->getBool("hidden");
@@ -355,31 +280,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() > 64)
-                       throw ModuleException("The link name '"+assign(L->Name)+"' is invalid as it is longer than 64 characters");
+               if (L->Name.length() > ServerInstance->Config->Limits.MaxHost)
+                       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("m_spanningtree",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("m_spanningtree",DEFAULT,"Configuration warning: Link block '" + assign(L->Name) + "' has no port defined, you will not be able to /connect it.");
+               if (!L->Port && L->IPAddr.find('/') == std::string::npos)
+                       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);
@@ -390,7 +315,7 @@ void SpanningTreeUtilities::ReadConfiguration()
        {
                ConfigTag* tag = i->second;
                reference<Autoconnect> A = new Autoconnect(tag);
-               A->Period = tag->getInt("period");
+               A->Period = tag->getDuration("period", 60, 1);
                A->NextConnectTime = ServerInstance->Time() + A->Period;
                A->position = -1;
                irc::spacesepstream ss(tag->getString("server"));
@@ -400,11 +325,6 @@ void SpanningTreeUtilities::ReadConfiguration()
                        A->servers.push_back(server);
                }
 
-               if (A->Period <= 0)
-               {
-                       throw ModuleException("Invalid configuration for autoconnect, period not a positive integer!");
-               }
-
                if (A->servers.empty())
                {
                        throw ModuleException("Invalid configuration for autoconnect, server cannot be empty!");
@@ -413,6 +333,9 @@ void SpanningTreeUtilities::ReadConfiguration()
                AutoconnectBlocks.push_back(A);
        }
 
+       for (server_hash::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
+               i->second->CheckULine();
+
        RefreshIPCache();
 }
 
@@ -421,7 +344,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;
                }
@@ -429,15 +352,23 @@ Link* SpanningTreeUtilities::FindLink(const std::string& name)
        return NULL;
 }
 
-void SpanningTreeUtilities::Rehash()
+void SpanningTreeUtilities::SendChannelMessage(const std::string& prefix, Channel* target, const std::string& text, char status, const ClientProtocol::TagMap& tags, const CUList& exempt_list, const char* message_type, TreeSocket* omit)
 {
-       server_hash temp;
-       for (server_hash::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
-               temp.insert(std::make_pair(i->first, i->second));
-       serverlist.swap(temp);
-       temp.clear();
-
-       for (server_hash::const_iterator i = sidlist.begin(); i != sidlist.end(); ++i)
-               temp.insert(std::make_pair(i->first, i->second));
-       sidlist.swap(temp);
+       CmdBuilder msg(prefix, message_type);
+       msg.push_tags(tags);
+       msg.push_raw(' ');
+       if (status != 0)
+               msg.push_raw(status);
+       msg.push_raw(target->name);
+       if (!text.empty())
+               msg.push_last(text);
+
+       TreeSocketSet list;
+       this->GetListOfServersForChannel(target, list, status, exempt_list);
+       for (TreeSocketSet::iterator i = list.begin(); i != list.end(); ++i)
+       {
+               TreeSocket* Sock = *i;
+               if (Sock != omit)
+                       Sock->WriteLine(msg);
+       }
 }
index 5559b3459b5defe2b1942a618f6077acacd7b7c9..c6f5822fe715cd7a97de1eb5ddbe5285014c2f96 100644 (file)
  */
 
 
-#ifndef M_SPANNINGTREE_UTILS_H
-#define M_SPANNINGTREE_UTILS_H
+#pragma once
 
 #include "inspircd.h"
+#include "cachetimer.h"
 
-/* Foward declarations */
 class TreeServer;
 class TreeSocket;
 class Link;
 class Autoconnect;
 class ModuleSpanningTree;
 class SpanningTreeUtilities;
+class CmdBuilder;
 
-/* This hash_map holds the hash equivalent of the server
- * tree, used for rapid linear lookups.
- */
-#ifdef HASHMAP_DEPRECATED
-       typedef nspace::hash_map<std::string, TreeServer*, nspace::insensitive, irc::StrHashComp> server_hash;
-#else
-       typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<std::string>, irc::StrHashComp> server_hash;
-#endif
+extern SpanningTreeUtilities* Utils;
 
-typedef std::map<TreeServer*,TreeServer*> TreeServerList;
+/** Associative container type, mapping server names/ids to TreeServers
+ */
+typedef TR1NS::unordered_map<std::string, TreeServer*, irc::insensitive, irc::StrHashComp> server_hash;
 
 /** Contains helper functions and variables for this module,
  * and keeps them out of the global namespace
  */
 class SpanningTreeUtilities : public classbase
 {
+       CacheRefreshTimer RefreshTimer;
+
  public:
-       typedef std::map<TreeSocket*, std::pair<std::string, int> > TimeoutList;
+       typedef std::set<TreeSocket*> TreeSocketSet;
+       typedef std::map<TreeSocket*, std::pair<std::string, unsigned int> > TimeoutList;
 
        /** Creator module
         */
@@ -59,6 +57,11 @@ class SpanningTreeUtilities : public classbase
        /** Flatten links and /MAP for non-opers
         */
        bool FlatLinks;
+
+       /** True if we're going to hide netsplits as *.net *.split for non-opers
+        */
+       bool HideSplits;
+
        /** Hide U-Lined servers in /MAP and /LINKS
         */
        bool HideULines;
@@ -77,7 +80,7 @@ class SpanningTreeUtilities : public classbase
        /* Number of seconds that a server can go without ping
         * before opers are warned of high latency.
         */
-       int PingWarnTime;
+       unsigned int PingWarnTime;
        /** This variable represents the root of the server tree
         */
        TreeServer *TreeRoot;
@@ -100,17 +103,9 @@ class SpanningTreeUtilities : public classbase
         */
        std::vector<reference<Autoconnect> > AutoconnectBlocks;
 
-       /** True (default) if we are to use challenge-response HMAC
-        * to authenticate passwords.
-        *
-        * NOTE: This defaults to on, but should be turned off if
-        * you are linking to an older version of inspircd.
-        */
-       bool ChallengeResponse;
-
        /** Ping frequency of server to server links
         */
-       int PingFreq;
+       unsigned int PingFreq;
 
        /** Initialise utility class
         */
@@ -118,39 +113,39 @@ class SpanningTreeUtilities : public classbase
 
        /** Prepare for class destruction
         */
-       CullResult cull();
+       CullResult cull() CXX11_OVERRIDE;
 
        /** Destroy class and free listeners etc
         */
        ~SpanningTreeUtilities();
 
-       void RouteCommand(TreeServer*, const std::string&, const parameterlist&, User*);
+       void RouteCommand(TreeServer* origin, CommandBase* cmd, const CommandBase::Params& parameters, User* user);
 
        /** Send a message from this server to one other local or remote
         */
-       bool DoOneToOne(const std::string &prefix, const std::string &command, const parameterlist &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
         */
-       bool DoOneToAllButSender(const std::string &prefix, const std::string &command, const parameterlist &params, const std::string& omit);
+       void DoOneToAllButSender(const CmdBuilder& params, TreeServer* omit);
 
        /** Send a message from this server to all others
         */
-       bool DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist &params);
+       void DoOneToMany(const CmdBuilder& params);
 
        /** Read the spanningtree module's tags from the config file
         */
        void ReadConfiguration();
 
-       /** Add a server to the server list for GetListOfServersForChannel
+       /** Handle nick collision
         */
-       void AddThisServer(TreeServer* server, TreeServerList &list);
+       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, TreeServerList &list, char status, const CUList &exempt_list);
+       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);
 
@@ -158,9 +153,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
         */
@@ -174,10 +170,12 @@ class SpanningTreeUtilities : public classbase
         */
        void RefreshIPCache();
 
-       /** Recreate serverlist and sidlist, this is needed because of m_nationalchars changing
-        * national_case_insensitive_map which is used by the hash function
+       /** Sends a PRIVMSG or a NOTICE to a channel obeying an exempt list and an optional prefix
         */
-       void Rehash();
+       void SendChannelMessage(const std::string& prefix, Channel* target, const std::string& text, char status, const ClientProtocol::TagMap& tags, const CUList& exempt_list, const char* message_type, TreeSocket* omit = NULL);
 };
 
-#endif
+inline void SpanningTreeUtilities::DoOneToMany(const CmdBuilder& params)
+{
+       DoOneToAllButSender(params, NULL);
+}
diff --git a/src/modules/m_spanningtree/version.cpp b/src/modules/m_spanningtree/version.cpp
deleted file mode 100644 (file)
index e08d13e..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *
- * 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 "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-bool TreeSocket::ServerVersion(const std::string &prefix, parameterlist &params)
-{
-       if (params.size() < 1)
-               return true;
-
-       TreeServer* ServerSource = Utils->FindServer(prefix);
-
-       if (ServerSource)
-       {
-               ServerSource->SetVersion(params[0]);
-       }
-       params[0] = ":" + params[0];
-       Utils->DoOneToAllButSender(prefix,"VERSION",params,prefix);
-       return true;
-}
-
index df97145be95d51744ae1588b127e0bd38a521e6e..54ff7e0888ddacb3c3fec7170d86eb7c0b8fd13c 100644 (file)
 
 
 #include "inspircd.h"
-#include "sql.h"
-#include "hash.h"
-
-/* $ModDesc: Allow/Deny connections based upon an arbitrary SQL table */
+#include "modules/sql.h"
+#include "modules/hash.h"
+#include "modules/ssl.h"
 
 enum AuthState {
        AUTH_STATE_NONE = 0,
@@ -29,24 +28,69 @@ enum AuthState {
        AUTH_STATE_FAIL = 2
 };
 
-class AuthQuery : public SQLQuery
+class AuthQuery : public SQL::Query
 {
  public:
        const std::string uid;
        LocalIntExt& pendingExt;
        bool verbose;
-       AuthQuery(Module* me, const std::string& u, LocalIntExt& e, bool v)
-               : SQLQuery(me), uid(u), pendingExt(e), verbose(v)
+       const std::string& kdf;
+       const std::string& pwcolumn;
+
+       AuthQuery(Module* me, const std::string& u, LocalIntExt& e, bool v, const std::string& kd, const std::string& pwcol)
+               : SQL::Query(me)
+               , uid(u)
+               , pendingExt(e)
+               , verbose(v)
+               , kdf(kd)
+               , pwcolumn(pwcol)
        {
        }
-       
-       void OnResult(SQLResult& res)
+
+       void OnResult(SQL::Result& res) CXX11_OVERRIDE
        {
-               User* user = ServerInstance->FindNick(uid);
+               LocalUser* user = static_cast<LocalUser*>(ServerInstance->FindUUID(uid));
                if (!user)
                        return;
+
                if (res.Rows())
                {
+                       if (!kdf.empty())
+                       {
+                               HashProvider* hashprov = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + kdf);
+                               if (!hashprov)
+                               {
+                                       if (verbose)
+                                               ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (a provider for %s was not loaded)", user->GetFullRealHost().c_str(), kdf.c_str());
+                                       pendingExt.set(user, AUTH_STATE_FAIL);
+                                       return;
+                               }
+
+                               size_t colindex = 0;
+                               if (!pwcolumn.empty() && !res.HasColumn(pwcolumn, colindex))
+                               {
+                                       if (verbose)
+                                               ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (the column specified (%s) was not returned)", user->GetFullRealHost().c_str(), pwcolumn.c_str());
+                                       pendingExt.set(user, AUTH_STATE_FAIL);
+                                       return;
+                               }
+
+                               SQL::Row row;
+                               while (res.GetRow(row))
+                               {
+                                       if (hashprov->Compare(user->password, row[colindex]))
+                                       {
+                                               pendingExt.set(user, AUTH_STATE_NONE);
+                                               return;
+                                       }
+                               }
+
+                               if (verbose)
+                                       ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (password from the SQL query did not match the user provided password)", user->GetFullRealHost().c_str());
+                               pendingExt.set(user, AUTH_STATE_FAIL);
+                               return;
+                       }
+
                        pendingExt.set(user, AUTH_STATE_NONE);
                }
                else
@@ -57,41 +101,40 @@ class AuthQuery : public SQLQuery
                }
        }
 
-       void OnError(SQLerror& error)
+       void OnError(SQL::Error& error) CXX11_OVERRIDE
        {
                User* user = ServerInstance->FindNick(uid);
                if (!user)
                        return;
                pendingExt.set(user, AUTH_STATE_FAIL);
                if (verbose)
-                       ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (SQL query failed: %s)", user->GetFullRealHost().c_str(), error.Str());
+                       ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (SQL query failed: %s)", user->GetFullRealHost().c_str(), error.ToString());
        }
 };
 
 class ModuleSQLAuth : public Module
 {
        LocalIntExt pendingExt;
-       dynamic_reference<SQLProvider> SQL;
+       dynamic_reference<SQL::Provider> SQL;
+       UserCertificateAPI sslapi;
 
        std::string freeformquery;
        std::string killreason;
        std::string allowpattern;
        bool verbose;
+       std::vector<std::string> hash_algos;
+       std::string kdf;
+       std::string pwcolumn;
 
  public:
-       ModuleSQLAuth() : pendingExt("sqlauth-wait", this), SQL(this, "SQL")
+       ModuleSQLAuth()
+               : pendingExt("sqlauth-wait", ExtensionItem::EXT_USER, this)
+               , SQL(this, "SQL")
+               , sslapi(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(pendingExt);
-               OnRehash(NULL);
-               Implementation eventlist[] = { I_OnCheckReady, I_OnRehash, I_OnUserRegister };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* conf = ServerInstance->Config->ConfValue("sqlauth");
                std::string dbid = conf->getString("dbid");
@@ -103,9 +146,17 @@ class ModuleSQLAuth : public Module
                killreason = conf->getString("killreason");
                allowpattern = conf->getString("allowpattern");
                verbose = conf->getBool("verbose");
+               kdf = conf->getString("kdf");
+               pwcolumn = conf->getString("column");
+
+               hash_algos.clear();
+               irc::commasepstream algos(conf->getString("hash", "md5,sha256"));
+               std::string algo;
+               while (algos.GetToken(algo))
+                       hash_algos.push_back(algo);
        }
 
-       ModResult OnUserRegister(LocalUser* user)
+       ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
        {
                // Note this is their initial (unresolved) connect block
                ConfigTag* tag = user->MyClass->config;
@@ -120,31 +171,31 @@ class ModuleSQLAuth : public Module
 
                if (!SQL)
                {
-                       ServerInstance->SNO->WriteGlobalSno('a', "Forbiding connection from %s (SQL database not present)", user->GetFullRealHost().c_str());
+                       ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (SQL database not present)", user->GetFullRealHost().c_str());
                        ServerInstance->Users->QuitUser(user, killreason);
                        return MOD_RES_PASSTHRU;
                }
 
                pendingExt.set(user, AUTH_STATE_BUSY);
 
-               ParamM userinfo;
-               SQL->PopulateUserInfo(user, userinfo);
+               SQL::ParamMap userinfo;
+               SQL::PopulateUserInfo(user, userinfo);
                userinfo["pass"] = user->password;
+               userinfo["certfp"] = sslapi ? sslapi->GetFingerprint(user) : "";
 
-               HashProvider* md5 = ServerInstance->Modules->FindDataService<HashProvider>("hash/md5");
-               if (md5)
-                       userinfo["md5pass"] = md5->hexsum(user->password);
-
-               HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256");
-               if (sha256)
-                       userinfo["sha256pass"] = sha256->hexsum(user->password);
+               for (std::vector<std::string>::const_iterator it = hash_algos.begin(); it != hash_algos.end(); ++it)
+               {
+                       HashProvider* hashprov = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + *it);
+                       if (hashprov && !hashprov->IsKDF())
+                               userinfo[*it + "pass"] = hashprov->Generate(user->password);
+               }
 
-               SQL->submit(new AuthQuery(this, user->uuid, pendingExt, verbose), freeformquery, userinfo);
+               SQL->Submit(new AuthQuery(this, user->uuid, pendingExt, verbose, kdf, pwcolumn), freeformquery, userinfo);
 
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnCheckReady(LocalUser* user)
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
                switch (pendingExt.get(user))
                {
@@ -159,9 +210,9 @@ class ModuleSQLAuth : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Allow/Deny connections based upon an arbitrary SQL table", VF_VENDOR);
+               return Version("Allow/deny connections based upon an arbitrary SQL table", VF_VENDOR);
        }
 };
 
index ae581cc4b61e3b36a98962291c41243545ff77f9..e4aaab474e41720146a29af12607a7f4e2130d0c 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2017 Dylan Frank <b00mx0r@aureus.pw>
  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
  *
  * This file is part of InspIRCd.  InspIRCd is free software: you can
 
 
 #include "inspircd.h"
-#include "sql.h"
-#include "hash.h"
+#include "modules/sql.h"
 
-/* $ModDesc: Allows storage of oper credentials in an SQL table */
-
-static bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
-{
-       std::stringstream hl(hostlist);
-       std::string xhost;
-       while (hl >> xhost)
-       {
-               if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
-               {
-                       return true;
-               }
-       }
-       return false;
-}
-
-class OpMeQuery : public SQLQuery
+class OperQuery : public SQL::Query
 {
  public:
+       // This variable will store all the OPER blocks from the DB
+       std::vector<std::string>& my_blocks;
+       /** We want to store the username and password if this is called during an /OPER, as we're responsible for /OPER post-DB fetch
+        *  Note: uid will be empty if this DB update was not called as a result of a user command (i.e. /REHASH)
+        */
        const std::string uid, username, password;
-       OpMeQuery(Module* me, const std::string& u, const std::string& un, const std::string& pw)
-               : SQLQuery(me), uid(u), username(un), password(pw)
+       OperQuery(Module* me, std::vector<std::string>& mb, const std::string& u, const std::string& un, const std::string& pw)
+               : SQL::Query(me)
+               , my_blocks(mb)
+               , uid(u)
+               , username(un)
+               , password(pw)
+       {
+       }
+       OperQuery(Module* me, std::vector<std::string>& mb)
+               : SQL::Query(me)
+               , my_blocks(mb)
        {
        }
 
-       void OnResult(SQLResult& res)
+       void OnResult(SQL::Result& res) CXX11_OVERRIDE
        {
-               ServerInstance->Logs->Log("m_sqloper",DEBUG, "SQLOPER: result for %s", uid.c_str());
-               User* user = ServerInstance->FindNick(uid);
-               if (!user)
-                       return;
+               ServerConfig::OperIndex& oper_blocks = ServerInstance->Config->oper_blocks;
 
-               // multiple rows may exist
-               SQLEntries row;
+               // Remove our previous blocks from oper_blocks for a clean update
+               for (std::vector<std::string>::const_iterator i = my_blocks.begin(); i != my_blocks.end(); ++i)
+               {
+                       oper_blocks.erase(*i);
+               }
+               my_blocks.clear();
+
+               SQL::Row row;
+               // Iterate through DB results to create oper blocks from sqloper rows
                while (res.GetRow(row))
                {
-#if 0
-                       parameterlist cols;
+                       std::vector<std::string> cols;
                        res.GetCols(cols);
 
-                       std::vector<KeyVal>* items;
-                       reference<ConfigTag> tag = ConfigTag::create("oper", "<m_sqloper>", 0, items);
-                       for(unsigned int i=0; i < cols.size(); i++)
+                       // Create the oper tag as if we were the conf file.
+                       ConfigItems* items;
+                       reference<ConfigTag> tag = ConfigTag::create("oper", MODNAME, 0, items);
+
+                       /** Iterate through each column in the SQLOpers table. An infinite number of fields can be specified.
+                        *  Column 'x' with cell value 'y' will be the same as x=y in an OPER block in opers.conf.
+                        */
+                       for (unsigned int i=0; i < cols.size(); ++i)
                        {
-                               if (!row[i].nul)
-                                       items->insert(std::make_pair(cols[i], row[i]));
+                               if (!row[i].IsNull())
+                                       (*items)[cols[i]] = row[i];
                        }
-#else
-                       if (OperUser(user, row[0], row[1]))
-                               return;
-#endif
+                       const std::string name = tag->getString("name");
+
+                       // Skip both duplicate sqloper blocks and sqloper blocks that attempt to override conf blocks.
+                       if (oper_blocks.find(name) != oper_blocks.end())
+                               continue;
+
+                       const std::string type = tag->getString("type");
+                       ServerConfig::OperIndex::iterator tblk = ServerInstance->Config->OperTypes.find(type);
+                       if (tblk == ServerInstance->Config->OperTypes.end())
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Sqloper block " + name + " has missing type " + type);
+                               ServerInstance->SNO->WriteGlobalSno('a', "m_sqloper: Oper block %s has missing type %s", name.c_str(), type.c_str());
+                               continue;
+                       }
+
+                       OperInfo* ifo = new OperInfo(type);
+
+                       ifo->type_block = tblk->second->type_block;
+                       ifo->oper_block = tag;
+                       ifo->class_blocks.assign(tblk->second->class_blocks.begin(), tblk->second->class_blocks.end());
+                       oper_blocks[name] = ifo;
+                       my_blocks.push_back(name);
+                       row.clear();
+               }
+
+               // If this was done as a result of /OPER and not a config read
+               if (!uid.empty())
+               {
+                       // Now that we've updated the DB, call any other /OPER hooks and then call /OPER
+                       OperExec();
                }
-               ServerInstance->Logs->Log("m_sqloper",DEBUG, "SQLOPER: no matches for %s (checked %d rows)", uid.c_str(), res.Rows());
-               // nobody succeeded... fall back to OPER
-               fallback();
        }
 
-       void OnError(SQLerror& error)
+       void OnError(SQL::Error& error) CXX11_OVERRIDE
        {
-               ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: query failed (%s)", error.Str());
-               fallback();
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "query failed (%s)", error.ToString());
+               ServerInstance->SNO->WriteGlobalSno('a', "m_sqloper: Failed to update blocks from database");
+               if (!uid.empty())
+               {
+                       // Fallback. We don't want to block a netadmin from /OPER
+                       OperExec();
+               }
        }
 
-       void fallback()
+       // Call /oper after placing all blocks from the SQL table into the config->oper_blocks list.
+       void OperExec()
        {
                User* user = ServerInstance->FindNick(uid);
-               if (!user)
+               LocalUser* localuser = IS_LOCAL(user);
+               // This should never be true
+               if (!localuser)
                        return;
 
-               Command* oper_command = ServerInstance->Parser->GetHandler("OPER");
+               Command* oper_command = ServerInstance->Parser.GetHandler("OPER");
 
                if (oper_command)
                {
-                       std::vector<std::string> params;
+                       CommandBase::Params params;
                        params.push_back(username);
                        params.push_back(password);
-                       oper_command->Handle(params, user);
-               }
-               else
-               {
-                       ServerInstance->Logs->Log("m_sqloper",SPARSE, "BUG: WHAT?! Why do we have no OPER command?!");
-               }
-       }
-
-       bool OperUser(User* user, const std::string &pattern, const std::string &type)
-       {
-               OperIndex::iterator iter = ServerInstance->Config->oper_blocks.find(" " + type);
-               if (iter == ServerInstance->Config->oper_blocks.end())
-               {
-                       ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: bad type '%s' in returned row for oper %s", type.c_str(), username.c_str());
-                       return false;
-               }
-               OperInfo* ifo = iter->second;
 
-               std::string hostname(user->ident);
+                       // Begin callback to other modules (i.e. sslinfo) now that we completed the DB fetch
+                       ModResult MOD_RESULT;
 
-               hostname.append("@").append(user->host);
+                       std::string origin = "OPER";
+                       FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (origin, params, localuser, true));
+                       if (MOD_RESULT == MOD_RES_DENY)
+                               return;
 
-               if (OneOfMatches(hostname.c_str(), user->GetIPString(), pattern))
+                       // Now handle /OPER.
+                       ClientProtocol::TagMap tags;
+                       oper_command->Handle(user, CommandBase::Params(params, tags));
+               }
+               else
                {
-                       /* Opertype and host match, looks like this is it. */
-
-                       user->Oper(ifo);
-                       return true;
+                       ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "BUG: WHAT?! Why do we have no OPER command?!");
                }
-
-               return false;
        }
 };
 
 class ModuleSQLOper : public Module
 {
+       // Whether OperQuery is running
+       bool active;
        std::string query;
-       std::string hashtype;
-       dynamic_reference<SQLProvider> SQL;
+       // Stores oper blocks from DB
+       std::vector<std::string> my_blocks;
+       dynamic_reference<SQL::Provider> SQL;
 
 public:
-       ModuleSQLOper() : SQL(this, "SQL") {}
-
-       void init()
+       ModuleSQLOper()
+               : active(false)
+               , SQL(this, "SQL")
        {
-               OnRehash(NULL);
-
-               Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
+               // Clear list of our blocks, as ConfigReader just wiped them anyway
+               my_blocks.clear();
+
                ConfigTag* tag = ServerInstance->Config->ConfValue("sqloper");
 
                std::string dbid = tag->getString("dbid");
@@ -158,42 +183,67 @@ public:
                else
                        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 * FROM ircd_opers WHERE active=1;");
+               // Update sqloper list from the database.
+               GetOperBlocks();
        }
 
-       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ~ModuleSQLOper()
        {
-               if (validated && command == "OPER" && parameters.size() >= 2)
+               // Remove all oper blocks that were from the DB
+               for (std::vector<std::string>::const_iterator i = my_blocks.begin(); i != my_blocks.end(); ++i)
+               {
+                       ServerInstance->Config->oper_blocks.erase(*i);
+               }
+       }
+
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
+       {
+               // If we are not in the middle of an existing /OPER and someone is trying to oper-up
+               if (validated && command == "OPER" && parameters.size() >= 2 && !active)
                {
                        if (SQL)
                        {
-                               LookupOper(user, parameters[0], parameters[1]);
-                               /* Query is in progress, it will re-invoke OPER if needed */
+                               GetOperBlocks(user->uuid, parameters[0], parameters[1]);
+                               /** We need to reload oper blocks from the DB before other
+                                *  hooks can run (i.e. sslinfo). We will re-call /OPER later.
+                                */
                                return MOD_RES_DENY;
                        }
-                       ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: database not present");
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "database not present");
+               }
+               else if (active)
+               {
+                       active = false;
                }
+               // There is either no DB or we successfully reloaded oper blocks
                return MOD_RES_PASSTHRU;
        }
 
-       void LookupOper(User* user, const std::string &username, const std::string &password)
+       // The one w/o params is for non-/OPER DB updates, such as a rehash.
+       void GetOperBlocks()
        {
-               HashProvider* hash = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + hashtype);
-
-               ParamM userinfo;
-               SQL->PopulateUserInfo(user, userinfo);
-               userinfo["username"] = username;
-               userinfo["password"] = hash ? hash->hexsum(password) : password;
+               SQL->Submit(new OperQuery(this, my_blocks), query);
+       }
+       void GetOperBlocks(const std::string u, const std::string& un, const std::string& pw)
+       {
+               active = true;
+               // Call to SQL query to fetch oper list from SQL table.
+               SQL->Submit(new OperQuery(this, my_blocks, u, un, pw), query);
+       }
 
-               SQL->submit(new OpMeQuery(this, user->uuid, username, password), query, userinfo);
+       void Prioritize() CXX11_OVERRIDE
+       {
+               /** Run before other /OPER hooks that expect populated blocks, i.e. sslinfo or a TOTP module.
+                *  We issue a DENY first, and will re-run OnPreCommand later to trigger the other hooks post-DB update.
+                */
+               ServerInstance->Modules.SetPriority(this, I_OnPreCommand, PRIORITY_FIRST);
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Allows storage of oper credentials in an SQL table", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleSQLOper)
index 083ac0f0448486a212bfc53ad61adfc57a3389ed..1b1ce9eaa090c059a89d95a8bcd862e473453771 100644 (file)
 
 
 #include "inspircd.h"
-#include "ssl.h"
+#include "modules/ssl.h"
+#include "modules/webirc.h"
+#include "modules/whois.h"
+#include "modules/who.h"
 
-/* $ModDesc: Provides SSL metadata, including /WHOIS information and /SSLINFO command */
+enum
+{
+       // From oftc-hybrid.
+       RPL_WHOISCERTFP = 276,
+
+       // From UnrealIRCd.
+       RPL_WHOISSECURE = 671
+};
 
-class SSLCertExt : public ExtensionItem {
+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));
        }
+
        void set(Extensible* item, ssl_cert* value)
        {
                value->refcount_inc();
@@ -37,12 +53,17 @@ class SSLCertExt : public ExtensionItem {
                        delete old;
        }
 
-       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const
+       void unset(Extensible* container)
+       {
+               free(container, unset_raw(container));
+       }
+
+       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
        {
                return static_cast<ssl_cert*>(item)->GetMetaLine();
        }
 
-       void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
        {
                ssl_cert* cert = new ssl_cert;
                set(container, cert);
@@ -67,7 +88,7 @@ class SSLCertExt : public ExtensionItem {
                }
        }
 
-       void free(void* item)
+       void free(Extensible* container, void* item) CXX11_OVERRIDE
        {
                ssl_cert* old = static_cast<ssl_cert*>(item);
                if (old && old->refcount_dec())
@@ -75,127 +96,165 @@ class SSLCertExt : public ExtensionItem {
        }
 };
 
-/** Handle /SSLINFO
- */
+class UserCertificateAPIImpl : public UserCertificateAPIBase
+{
+ public:
+       LocalIntExt nosslext;
+       SSLCertExt sslext;
+
+       UserCertificateAPIImpl(Module* mod)
+               : UserCertificateAPIBase(mod)
+               , nosslext("no_ssl_cert", ExtensionItem::EXT_USER, mod)
+               , sslext(mod)
+       {
+       }
+
+       ssl_cert* GetCertificate(User* user) CXX11_OVERRIDE
+       {
+               ssl_cert* cert = sslext.get(user);
+               if (cert)
+                       return cert;
+
+               LocalUser* luser = IS_LOCAL(user);
+               if (!luser || nosslext.get(luser))
+                       return NULL;
+
+               cert = SSLClientCert::GetCertificate(&luser->eh);
+               if (!cert)
+                       return NULL;
+
+               SetCertificate(user, cert);
+               return cert;
+       }
+
+       void SetCertificate(User* user, ssl_cert* cert) CXX11_OVERRIDE
+       {
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Setting SSL certificate for %s: %s",
+                       user->GetFullHost().c_str(), cert->GetMetaLine().c_str());
+               sslext.set(user, cert);
+       }
+};
+
 class CommandSSLInfo : public Command
 {
  public:
-       SSLCertExt CertExt;
+       UserCertificateAPIImpl sslapi;
 
-       CommandSSLInfo(Module* Creator) : Command(Creator, "SSLINFO", 1), CertExt(Creator)
+       CommandSSLInfo(Module* Creator)
+               : Command(Creator, "SSLINFO", 1)
+               , sslapi(Creator)
        {
                this->syntax = "<nick>";
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                User* target = ServerInstance->FindNickOnly(parameters[0]);
 
                if ((!target) || (target->registered != REG_ALL))
                {
-                       user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nickname", user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
                        return CMD_FAILURE;
                }
                bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
-               if (operonlyfp && !IS_OPER(user) && target != user)
+               if (operonlyfp && !user->IsOper() && target != user)
                {
-                       user->WriteServ("NOTICE %s :*** You cannot view SSL certificate information for other users", user->nick.c_str());
+                       user->WriteNotice("*** You cannot view SSL certificate information for other users");
                        return CMD_FAILURE;
                }
-               ssl_cert* cert = CertExt.get(target);
+               ssl_cert* cert = sslapi.GetCertificate(target);
                if (!cert)
                {
-                       user->WriteServ("NOTICE %s :*** No SSL certificate for this user", user->nick.c_str());
+                       user->WriteNotice("*** No SSL certificate for this user");
                }
                else if (cert->GetError().length())
                {
-                       user->WriteServ("NOTICE %s :*** No SSL certificate information for this user (%s).", user->nick.c_str(), cert->GetError().c_str());
+                       user->WriteNotice("*** No SSL certificate information for this user (" + cert->GetError() + ").");
                }
                else
                {
-                       user->WriteServ("NOTICE %s :*** Distinguished Name: %s", user->nick.c_str(), cert->GetDN().c_str());
-                       user->WriteServ("NOTICE %s :*** Issuer:             %s", user->nick.c_str(), cert->GetIssuer().c_str());
-                       user->WriteServ("NOTICE %s :*** Key Fingerprint:    %s", user->nick.c_str(), cert->GetFingerprint().c_str());
+                       user->WriteNotice("*** Distinguished Name: " + cert->GetDN());
+                       user->WriteNotice("*** Issuer:             " + cert->GetIssuer());
+                       user->WriteNotice("*** Key Fingerprint:    " + cert->GetFingerprint());
                }
                return CMD_SUCCESS;
        }
 };
 
-class ModuleSSLInfo : public Module
+class ModuleSSLInfo
+       : public Module
+       , public WebIRC::EventListener
+       , public Whois::EventListener
+       , public Who::EventListener
 {
+ private:
        CommandSSLInfo cmd;
 
- public:
-       ModuleSSLInfo() : cmd(this)
+       bool MatchFP(ssl_cert* const cert, const std::string& fp) const
        {
+               return irc::spacesepstream(fp).Contains(cert->GetFingerprint());
        }
 
-       void init()
+ public:
+       ModuleSSLInfo()
+               : WebIRC::EventListener(this)
+               , Whois::EventListener(this)
+               , Who::EventListener(this)
+               , cmd(this)
        {
-               ServerInstance->Modules->AddService(cmd);
-
-               ServerInstance->Modules->AddService(cmd.CertExt);
-
-               Implementation eventlist[] = { I_OnWhois, I_OnPreCommand, I_OnSetConnectClass, I_OnUserConnect, I_OnPostConnect };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("SSL Certificate Utilities", VF_VENDOR);
        }
 
-       void OnWhois(User* source, User* dest)
+       void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
        {
-               ssl_cert* cert = cmd.CertExt.get(dest);
+               ssl_cert* cert = cmd.sslapi.GetCertificate(whois.GetTarget());
                if (cert)
                {
-                       ServerInstance->SendWhoisLine(source, dest, 671, "%s %s :is using a secure connection", source->nick.c_str(), dest->nick.c_str());
+                       whois.SendLine(RPL_WHOISSECURE, "is using a secure connection");
                        bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
-                       if ((!operonlyfp || source == dest || IS_OPER(source)) && !cert->fingerprint.empty())
-                               ServerInstance->SendWhoisLine(source, dest, 276, "%s %s :has client certificate fingerprint %s",
-                                       source->nick.c_str(), dest->nick.c_str(), cert->fingerprint.c_str());
+                       if ((!operonlyfp || whois.IsSelfWhois() || whois.GetSource()->IsOper()) && !cert->fingerprint.empty())
+                               whois.SendLine(RPL_WHOISCERTFP, InspIRCd::Format("has client certificate fingerprint %s", cert->fingerprint.c_str()));
                }
        }
 
-       bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
+       ModResult OnWhoLine(const Who::Request& request, LocalUser* source, User* user, Membership* memb, Numeric::Numeric& numeric) CXX11_OVERRIDE
        {
-               std::stringstream hl(hostlist);
-               std::string xhost;
-               while (hl >> xhost)
-               {
-                       if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
-                       {
-                               return true;
-                       }
-               }
-               return false;
+               size_t flag_index;
+               if (!request.GetFieldIndex('f', flag_index))
+                       return MOD_RES_PASSTHRU;
+
+               ssl_cert* cert = cmd.sslapi.GetCertificate(user);
+               if (cert)
+                       numeric.GetParams()[flag_index].push_back('s');
+
+               return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
        {
                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;
-                               if (!ifo->oper_block)
-                                       return MOD_RES_PASSTHRU;
-
-                               ssl_cert* cert = cmd.CertExt.get(user);
+                               ssl_cert* cert = cmd.sslapi.GetCertificate(user);
 
                                if (ifo->oper_block->getBool("sslonly") && !cert)
                                {
-                                       user->WriteNumeric(491, "%s :This oper login requires an SSL connection.", user->nick.c_str());
+                                       user->WriteNumeric(ERR_NOOPERHOST, "This oper login requires an SSL connection.");
                                        user->CommandFloodPenalty += 10000;
                                        return MOD_RES_DENY;
                                }
 
                                std::string fingerprint;
-                               if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || cert->GetFingerprint() != fingerprint))
+                               if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || !MatchFP(cert, fingerprint)))
                                {
-                                       user->WriteNumeric(491, "%s :This oper login requires a matching SSL fingerprint.",user->nick.c_str());
+                                       user->WriteNumeric(ERR_NOOPERHOST, "This oper login requires a matching SSL certificate fingerprint.");
                                        user->CommandFloodPenalty += 10000;
                                        return MOD_RES_DENY;
                                }
@@ -206,43 +265,55 @@ class ModuleSSLInfo : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       void OnUserConnect(LocalUser* user)
+       void OnPostConnect(User* user) CXX11_OVERRIDE
        {
-               SocketCertificateRequest req(&user->eh, this);
-               if (!req.cert)
+               LocalUser* const localuser = IS_LOCAL(user);
+               if (!localuser)
                        return;
-               cmd.CertExt.set(user, req.cert);
-       }
 
-       void OnPostConnect(User* user)
-       {
-               ssl_cert *cert = cmd.CertExt.get(user);
-               if (!cert || cert->fingerprint.empty())
+               const SSLIOHook* const ssliohook = SSLIOHook::IsSSL(&localuser->eh);
+               if (!ssliohook || cmd.sslapi.nosslext.get(localuser))
+                       return;
+
+               ssl_cert* const cert = ssliohook->GetCertificate();
+
+               {
+                       std::string text = "*** You are connected to ";
+                       if (!ssliohook->GetServerName(text))
+                               text.append(ServerInstance->Config->ServerName);
+                       text.append(" 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;
-                       if (!ifo->oper_block)
-                               continue;
-
                        std::string fp = ifo->oper_block->getString("fingerprint");
-                       if (fp == cert->fingerprint && ifo->oper_block->getBool("autologin"))
+                       if (MatchFP(cert, fp) && ifo->oper_block->getBool("autologin"))
                                user->Oper(ifo);
                }
        }
 
-       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+       ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
        {
-               SocketCertificateRequest req(&user->eh, this);
+               ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
                bool ok = true;
                if (myclass->config->getString("requiressl") == "trusted")
                {
-                       ok = (req.cert && req.cert->IsCAVerified());
+                       ok = (cert && cert->IsCAVerified());
+                       ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class requires a trusted SSL cert. Client %s one.", (ok ? "has" : "does not have"));
                }
                else if (myclass->config->getBool("requiressl"))
                {
-                       ok = (req.cert != NULL);
+                       ok = (cert != NULL);
+                       ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class requires any SSL cert. Client %s one.", (ok ? "has" : "does not have"));
                }
 
                if (!ok)
@@ -250,15 +321,37 @@ class ModuleSSLInfo : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       void OnRequest(Request& request)
+       void OnWebIRCAuth(LocalUser* user, const WebIRC::FlagMap* flags) CXX11_OVERRIDE
        {
-               if (strcmp("GET_USER_CERT", request.id) == 0)
+               // We are only interested in connection flags. If none have been
+               // given then we have nothing to do.
+               if (!flags)
+                       return;
+
+               // We only care about the tls connection flag if the connection
+               // between the gateway and the server is secure.
+               if (!cmd.sslapi.GetCertificate(user))
+                       return;
+
+               WebIRC::FlagMap::const_iterator iter = flags->find("secure");
+               if (iter == flags->end())
                {
-                       UserCertificateRequest& req = static_cast<UserCertificateRequest&>(request);
-                       req.cert = cmd.CertExt.get(req.user);
+                       // If this is not set then the connection between the client and
+                       // the gateway is not secure.
+                       cmd.sslapi.nosslext.set(user, 1);
+                       cmd.sslapi.sslext.unset(user);
+                       return;
                }
+
+               // Create a fake ssl_cert for the user.
+               ssl_cert* cert = new ssl_cert;
+               cert->error = "WebIRC users can not specify valid certs yet";
+               cert->invalid = true;
+               cert->revoked = true;
+               cert->trusted = false;
+               cert->unknownsigner = true;
+               cmd.sslapi.SetCertificate(user, cert);
        }
 };
 
 MODULE_INIT(ModuleSSLInfo)
-
index c81c7420783747b7d0d560b475224a5a5a925a5c..67128e6bd59596076437feb3288f0e09034c52ae 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2013 Shawn Smith <shawn@inspircd.org>
  *   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>
 
 
 #include "inspircd.h"
-#include "ssl.h"
+#include "modules/ctctags.h"
+#include "modules/ssl.h"
 
-/* $ModDesc: Provides channel mode +z to allow for Secure/SSL only channels */
+enum
+{
+       // From UnrealIRCd.
+       ERR_SECUREONLYCHAN = 489,
+       ERR_ALLMUSTSSL = 490
+};
 
 /** Handle channel mode +z
  */
 class SSLMode : public ModeHandler
 {
+ private:
+       UserCertificateAPI& API;
+
  public:
-       SSLMode(Module* Creator) : ModeHandler(Creator, "sslonly", 'z', PARAM_NONE, MODETYPE_CHANNEL) { }
+       SSLMode(Module* Creator, UserCertificateAPI& api)
+               : ModeHandler(Creator, "sslonly", 'z', PARAM_NONE, MODETYPE_CHANNEL)
+               , API(api)
+       {
+       }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
        {
                if (adding)
                {
-                       if (!channel->IsModeSet('z'))
+                       if (!channel->IsModeSet(this))
                        {
                                if (IS_LOCAL(source))
                                {
-                                       const UserMembList* userlist = channel->GetUsers();
-                                       for(UserMembCIter i = userlist->begin(); i != userlist->end(); i++)
+                                       if (!API)
+                                       {
+                                               source->WriteNumeric(ERR_ALLMUSTSSL, channel->name, "Unable to determine whether all members of the channel are connected via SSL");
+                                               return MODEACTION_DENY;
+                                       }
+
+                                       const Channel::MemberMap& userlist = channel->GetUsers();
+                                       for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
                                        {
-                                               UserCertificateRequest req(i->first, creator);
-                                               req.Send();
-                                               if(!req.cert && !ServerInstance->ULine(i->first->server))
+                                               ssl_cert* cert = API->GetCertificate(i->first);
+                                               if (!cert && !i->first->server->IsULine())
                                                {
-                                                       source->WriteNumeric(ERR_ALLMUSTSSL, "%s %s :all members of the channel must be connected via SSL", source->nick.c_str(), channel->name.c_str());
+                                                       source->WriteNumeric(ERR_ALLMUSTSSL, channel->name, "all members of the channel must be connected via SSL");
                                                        return MODEACTION_DENY;
                                                }
                                        }
                                }
-                               channel->SetMode('z',true);
+                               channel->SetMode(this, true);
                                return MODEACTION_ALLOW;
                        }
                        else
@@ -63,9 +82,9 @@ class SSLMode : public ModeHandler
                }
                else
                {
-                       if (channel->IsModeSet('z'))
+                       if (channel->IsModeSet(this))
                        {
-                               channel->SetMode('z',false);
+                               channel->SetMode(this, false);
                                return MODEACTION_ALLOW;
                        }
 
@@ -74,39 +93,113 @@ class SSLMode : public ModeHandler
        }
 };
 
-class ModuleSSLModes : public Module
+/** Handle user mode +z
+*/
+class SSLModeUser : public ModeHandler
 {
+ private:
+       UserCertificateAPI& API;
+
+ public:
+       SSLModeUser(Module* Creator, UserCertificateAPI& api)
+               : ModeHandler(Creator, "sslqueries", 'z', PARAM_NONE, MODETYPE_USER)
+               , API(api)
+       {
+               if (!ServerInstance->Config->ConfValue("sslmodes")->getBool("enableumode"))
+                       DisableAutoRegister();
+       }
+
+       ModeAction OnModeChange(User* user, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE
+       {
+               if (adding)
+               {
+                       if (!dest->IsModeSet(this))
+                       {
+                               if (!API || !API->GetCertificate(user))
+                                       return MODEACTION_DENY;
+
+                               dest->SetMode(this, true);
+                               return MODEACTION_ALLOW;
+                       }
+               }
+               else
+               {
+                       if (dest->IsModeSet(this))
+                       {
+                               dest->SetMode(this, false);
+                               return MODEACTION_ALLOW;
+                       }
+               }
+
+               return MODEACTION_DENY;
+       }
+};
 
+class ModuleSSLModes
+       : public Module
+       , public CTCTags::EventListener
+{
+ private:
+       UserCertificateAPI api;
        SSLMode sslm;
+       SSLModeUser sslquery;
 
  public:
        ModuleSSLModes()
-               : sslm(this)
+               : CTCTags::EventListener(this)
+               , api(this)
+               , sslm(this, api)
+               , sslquery(this, api)
        {
        }
 
-       void init()
+       ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(sslm);
-               Implementation eventlist[] = { I_OnUserPreJoin, I_OnCheckBan, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               if(chan && chan->IsModeSet(sslm))
+               {
+                       if (!api)
+                       {
+                               user->WriteNumeric(ERR_SECUREONLYCHAN, cname, "Cannot join channel; unable to determine if you are an SSL user (+z is set)");
+                               return MOD_RES_DENY;
+                       }
+
+                       if (!api->GetCertificate(user))
+                       {
+                               user->WriteNumeric(ERR_SECUREONLYCHAN, cname, "Cannot join channel; SSL users only (+z is set)");
+                               return MOD_RES_DENY;
+                       }
+               }
+
+               return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+       ModResult HandleMessage(User* user, const MessageTarget& msgtarget)
        {
-               if(chan && chan->IsModeSet('z'))
+               if (msgtarget.type != MessageTarget::TYPE_USER)
+                       return MOD_RES_PASSTHRU;
+
+               User* target = msgtarget.Get<User>();
+
+               /* If one or more of the parties involved is a ulined service, we wont stop it. */
+               if (user->server->IsULine() || target->server->IsULine())
+                       return MOD_RES_PASSTHRU;
+
+               /* If the target is +z */
+               if (target->IsModeSet(sslquery))
                {
-                       UserCertificateRequest req(user, this);
-                       req.Send();
-                       if (req.cert)
+                       if (!api || !api->GetCertificate(user))
                        {
-                               // Let them in
-                               return MOD_RES_PASSTHRU;
+                               /* The sending user is not on an SSL connection */
+                               user->WriteNumeric(ERR_CANTSENDTOUSER, target->nick, "You are not permitted to send private messages to this user (+z is set)");
+                               return MOD_RES_DENY;
                        }
-                       else
+               }
+               /* If the user is +z */
+               else if (user->IsModeSet(sslquery))
+               {
+                       if (!api || !api->GetCertificate(target))
                        {
-                               // Deny
-                               user->WriteServ( "489 %s %s :Cannot join channel; SSL users only (+z)", user->nick.c_str(), cname);
+                               user->WriteNumeric(ERR_CANTSENDTOUSER, target->nick, "You must remove user mode 'z' before you are able to send private messages to a non-SSL user.");
                                return MOD_RES_DENY;
                        }
                }
@@ -114,33 +207,36 @@ class ModuleSSLModes : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
+       {
+               return HandleMessage(user, target);
+       }
+
+       ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
+       {
+               return HandleMessage(user, target);
+       }
+
+       ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
        {
                if ((mask.length() > 2) && (mask[0] == 'z') && (mask[1] == ':'))
                {
-                       UserCertificateRequest req(user, this);
-                       req.Send();
-                       if (req.cert && InspIRCd::Match(req.cert->GetFingerprint(), mask.substr(2)))
+                       const std::string fp = api ? api->GetFingerprint(user) : "";
+                       if (!fp.empty() && InspIRCd::Match(fp, mask.substr(2)))
                                return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
        }
 
-       ~ModuleSSLModes()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
+               tokens["EXTBAN"].push_back('z');
        }
 
-       void On005Numeric(std::string &output)
+       Version GetVersion() CXX11_OVERRIDE
        {
-               ServerInstance->AddExtBanChar('z');
-       }
-
-       Version GetVersion()
-       {
-               return Version("Provides channel mode +z to allow for Secure/SSL only channels", VF_VENDOR);
+               return Version("Provides user and channel mode +z to allow for SSL-only channels, queries and notices", VF_VENDOR);
        }
 };
 
-
 MODULE_INIT(ModuleSSLModes)
-
diff --git a/src/modules/m_starttls.cpp b/src/modules/m_starttls.cpp
new file mode 100644 (file)
index 0000000..881ef49
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2014 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
+ * 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/ssl.h"
+#include "modules/cap.h"
+
+// From IRCv3 tls-3.1
+enum
+{
+       RPL_STARTTLS = 670,
+       ERR_STARTTLS = 691
+};
+
+class CommandStartTLS : public SplitCommand
+{
+       dynamic_reference_nocheck<IOHookProvider>& ssl;
+
+ public:
+       CommandStartTLS(Module* mod, dynamic_reference_nocheck<IOHookProvider>& s)
+               : SplitCommand(mod, "STARTTLS")
+               , ssl(s)
+       {
+               works_before_reg = true;
+       }
+
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               if (!ssl)
+               {
+                       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");
+                       return CMD_FAILURE;
+               }
+
+               if (user->eh.GetIOHook())
+               {
+                       user->WriteNumeric(ERR_STARTTLS, "STARTTLS failure");
+                       return CMD_FAILURE;
+               }
+
+               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,
+                * we assume the write will not block here; this is usually safe, as
+                * STARTTLS is sent very early on in the registration phase, where the
+                * user hasn't built up much sendq. Handling a blocked write here would
+                * be very annoying.
+                */
+               user->eh.DoWrite();
+
+               ssl->OnAccept(&user->eh, NULL, NULL);
+
+               return CMD_SUCCESS;
+       }
+};
+
+class ModuleStartTLS : public Module
+{
+       CommandStartTLS starttls;
+       Cap::Capability tls;
+       dynamic_reference_nocheck<IOHookProvider> ssl;
+
+ public:
+       ModuleStartTLS()
+               : starttls(this, ssl)
+               , tls(this, "tls")
+               , ssl(this, "ssl")
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* conf = ServerInstance->Config->ConfValue("starttls");
+
+               std::string newprovider = conf->getString("provider");
+               if (newprovider.empty())
+                       ssl.SetProvider("ssl");
+               else
+                       ssl.SetProvider("ssl/" + newprovider);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the STARTTLS command", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleStartTLS)
index f1504edaf329a6bf270c4441742a7a4eddb7a8bd..6ea279422e4f9b237d606e5c4640a5a3f5cc978f 100644 (file)
 
 
 #include "inspircd.h"
-
-/* $ModDesc: Provides channel +S mode (strip ansi color) */
-
-/** Handles channel mode +S
- */
-class ChannelStripColor : public SimpleChannelModeHandler
-{
- public:
-       ChannelStripColor(Module* Creator) : SimpleChannelModeHandler(Creator, "stripcolor", 'S') { }
-};
-
-/** Handles user mode +S
- */
-class UserStripColor : public SimpleUserModeHandler
-{
- public:
-       UserStripColor(Module* Creator) : SimpleUserModeHandler(Creator, "u_stripcolor", 'S') { }
-};
-
+#include "modules/exemption.h"
 
 class ModuleStripColor : public Module
 {
-       ChannelStripColor csc;
-       UserStripColor usc;
+       CheckExemption::EventProvider exemptionprov;
+       SimpleChannelModeHandler csc;
+       SimpleUserModeHandler usc;
 
  public:
-       ModuleStripColor() : csc(this), usc(this)
+       ModuleStripColor()
+               : exemptionprov(this)
+               , csc(this, "stripcolor", 'S')
+               , usc(this, "u_stripcolor", 'S')
        {
        }
 
-       void init()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(usc);
-               ServerInstance->Modules->AddService(csc);
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               tokens["EXTBAN"].push_back('S');
        }
 
-       virtual ~ModuleStripColor()
-       {
-       }
-
-       virtual void On005Numeric(std::string &output)
-       {
-               ServerInstance->AddExtBanChar('S');
-       }
-
-       virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return MOD_RES_PASSTHRU;
 
                bool active = false;
-               if (target_type == TYPE_USER)
+               if (target.type == MessageTarget::TYPE_USER)
                {
-                       User* t = (User*)dest;
-                       active = t->IsModeSet('S');
+                       User* t = target.Get<User>();
+                       active = t->IsModeSet(usc);
                }
-               else if (target_type == TYPE_CHANNEL)
+               else if (target.type == MessageTarget::TYPE_CHANNEL)
                {
-                       Channel* t = (Channel*)dest;
-                       ModResult res = ServerInstance->OnCheckExemption(user,t,"stripcolor");
+                       Channel* t = target.Get<Channel>();
+                       ModResult res = CheckExemption::Call(exemptionprov, user, t, "stripcolor");
 
                        if (res == MOD_RES_ALLOW)
                                return MOD_RES_PASSTHRU;
 
-                       active = !t->GetExtBanStatus(user, 'S').check(!t->IsModeSet('S'));
+                       active = !t->GetExtBanStatus(user, 'S').check(!t->IsModeSet(csc));
                }
 
                if (active)
                {
-                       InspIRCd::StripColor(text);
+                       InspIRCd::StripColor(details.text);
                }
 
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+       void OnUserPart(Membership* memb, std::string& partmessage, CUList& except_list) CXX11_OVERRIDE
        {
-               return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
+               User* user = memb->user;
+               Channel* channel = memb->chan;
+
+               if (!IS_LOCAL(user))
+                       return;
+
+               if (channel->GetExtBanStatus(user, 'S').check(!user->IsModeSet(csc)))
+               {
+                       ModResult res = CheckExemption::Call(exemptionprov, user, channel, "stripcolor");
+
+                       if (res != MOD_RES_ALLOW)
+                               InspIRCd::StripColor(partmessage);
+               }
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides channel +S mode (strip ansi color)", VF_VENDOR);
+               return Version("Provides channel mode +S, strip ansi color", VF_VENDOR);
        }
 
 };
index e666b0fe24b977c1de8f5845e03c9152e01e12cc..df487199a48143c9bb4f38f028d28788e1de6475 100644 (file)
@@ -22,8 +22,7 @@
 
 #include "inspircd.h"
 #include "xline.h"
-
-/* $ModDesc: Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services. */
+#include "modules/stats.h"
 
 namespace
 {
@@ -35,44 +34,38 @@ namespace
 class SVSHold : public XLine
 {
 public:
-       irc::string nickname;
+       std::string nickname;
 
-       SVSHold(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& nick)
+       SVSHold(time_t s_time, unsigned long d, const std::string& src, const std::string& re, const std::string& nick)
                : XLine(s_time, d, src, re, "SVSHOLD")
        {
-               this->nickname = nick.c_str();
+               this->nickname = nick;
        }
 
-       ~SVSHold()
-       {
-       }
-
-       bool Matches(User *u)
+       bool Matches(User* u) CXX11_OVERRIDE
        {
                if (u->nick == nickname)
                        return true;
                return false;
        }
 
-       bool Matches(const std::string &s)
+       bool Matches(const std::string& s) CXX11_OVERRIDE
        {
-               if (nickname == s)
-                       return true;
-               return false;
+               return InspIRCd::Match(s, nickname);
        }
 
-       void DisplayExpiry()
+       void DisplayExpiry() CXX11_OVERRIDE
        {
                if (!silent)
                {
-                       ServerInstance->SNO->WriteToSnoMask('x',"Removing expired SVSHOLD %s (set by %s %ld seconds ago)",
-                               this->nickname.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
+                       ServerInstance->SNO->WriteToSnoMask('x', "Removing expired SVSHOLD %s (set by %s %s ago): %s",
+                               nickname.c_str(), source.c_str(), InspIRCd::DurationString(ServerInstance->Time() - set_time).c_str(), reason.c_str());
                }
        }
 
-       const char* Displayable()
+       const std::string& Displayable() CXX11_OVERRIDE
        {
-               return nickname.c_str();
+               return nickname;
        }
 };
 
@@ -83,14 +76,14 @@ class SVSHoldFactory : public XLineFactory
  public:
        SVSHoldFactory() : XLineFactory("SVSHOLD") { }
 
-       /** Generate a shun
+       /** Generate an SVSHOLD
        */
-       XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+       XLine* Generate(time_t set_time, unsigned long duration, const std::string& source, const std::string& reason, const std::string& xline_specific_mask) CXX11_OVERRIDE
        {
                return new SVSHold(set_time, duration, source, reason, xline_specific_mask);
        }
 
-       bool AutoApplyToUserList(XLine *x)
+       bool AutoApplyToUserList(XLine* x) CXX11_OVERRIDE
        {
                return false;
        }
@@ -103,16 +96,15 @@ class CommandSvshold : public Command
  public:
        CommandSvshold(Module* Creator) : Command(Creator, "SVSHOLD", 1)
        {
-               flags_needed = 'o'; this->syntax = "<nickname> [<duration> :<reason>]";
-               TRANSLATE4(TR_TEXT, TR_TEXT, TR_TEXT, TR_END);
+               flags_needed = 'o'; this->syntax = "<nick> [<duration> :<reason>]";
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                /* syntax: svshold nickname time :reason goes here */
                /* 'time' is a human-readable timestring, like 2d3h2s. */
 
-               if (!ServerInstance->ULine(user->server))
+               if (!user->server->IsULine())
                {
                        /* don't allow SVSHOLD from non-ulined clients */
                        return CMD_FAILURE;
@@ -120,14 +112,16 @@ class CommandSvshold : public Command
 
                if (parameters.size() == 1)
                {
-                       if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "SVSHOLD", user))
+                       std::string reason;
+
+                       if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "SVSHOLD", reason, user))
                        {
                                if (!silent)
-                                       ServerInstance->SNO->WriteToSnoMask('x',"%s removed SVSHOLD on %s",user->nick.c_str(),parameters[0].c_str());
+                                       ServerInstance->SNO->WriteToSnoMask('x', "%s removed SVSHOLD on %s: %s", user->nick.c_str(), parameters[0].c_str(), reason.c_str());
                        }
                        else
                        {
-                               user->WriteServ("NOTICE %s :*** SVSHOLD %s not found in list, try /stats S.",user->nick.c_str(),parameters[0].c_str());
+                               user->WriteNotice("*** SVSHOLD " + parameters[0] + " not found on the list.");
                        }
                }
                else
@@ -135,8 +129,12 @@ class CommandSvshold : public Command
                        if (parameters.size() < 3)
                                return CMD_FAILURE;
 
-                       // Adding - XXX todo make this respect <insane> tag perhaps..
-                       long duration = ServerInstance->Duration(parameters[1]);
+                       unsigned long duration;
+                       if (!InspIRCd::Duration(parameters[1], duration))
+                       {
+                               user->WriteNotice("*** Invalid duration for SVSHOLD.");
+                               return CMD_FAILURE;
+                       }
                        SVSHold* r = new SVSHold(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), parameters[0].c_str());
 
                        if (ServerInstance->XLines->AddLine(r, user))
@@ -150,9 +148,9 @@ class CommandSvshold : public Command
                                }
                                else
                                {
-                                       time_t c_requires_crap = duration + ServerInstance->Time();
-                                       std::string timestr = ServerInstance->TimeString(c_requires_crap);
-                                       ServerInstance->SNO->WriteGlobalSno('x', "%s added timed SVSHOLD for %s, expires on %s: %s", user->nick.c_str(), parameters[0].c_str(), timestr.c_str(), parameters[2].c_str());
+                                       ServerInstance->SNO->WriteGlobalSno('x', "%s added timed SVSHOLD for %s, expires in %s (on %s): %s",
+                                               user->nick.c_str(), parameters[0].c_str(), InspIRCd::DurationString(duration).c_str(),
+                                               InspIRCd::TimeString(ServerInstance->Time() + duration).c_str(), parameters[2].c_str());
                                }
                        }
                        else
@@ -165,69 +163,67 @@ class CommandSvshold : public Command
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_BROADCAST;
        }
 };
 
-class ModuleSVSHold : public Module
+class ModuleSVSHold : public Module, public Stats::EventListener
 {
        CommandSvshold cmd;
        SVSHoldFactory s;
 
 
  public:
-       ModuleSVSHold() : cmd(this)
+       ModuleSVSHold()
+               : Stats::EventListener(this)
+               , cmd(this)
        {
        }
 
-       void init()
+       void init() CXX11_OVERRIDE
        {
                ServerInstance->XLines->RegisterFactory(&s);
-               ServerInstance->Modules->AddService(cmd);
-               Implementation eventlist[] = { I_OnUserPreNick, I_OnStats, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-               OnRehash(NULL);
        }
 
-       void OnRehash(User* user)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("svshold");
-               silent = tag->getBool("silent");
+               silent = tag->getBool("silent", true);
        }
 
-       virtual ModResult OnStats(char symbol, User* user, string_list &out)
+       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;
        }
 
-       virtual ModResult OnUserPreNick(User *user, const std::string &newnick)
+       ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
        {
                XLine *rl = ServerInstance->XLines->MatchesLine("SVSHOLD", newnick);
 
                if (rl)
                {
-                       user->WriteServ( "432 %s %s :Services reserved nickname: %s", user->nick.c_str(), 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;
                }
 
                return MOD_RES_PASSTHRU;
        }
 
-       virtual ~ModuleSVSHold()
+       ~ModuleSVSHold()
        {
                ServerInstance->XLines->DelAll("SVSHOLD");
                ServerInstance->XLines->UnregisterFactory(&s);
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services.", VF_COMMON | VF_VENDOR);
+               return Version("Implements SVSHOLD, like Q-lines, but can only be added/removed by Services", VF_COMMON | VF_VENDOR);
        }
 };
 
index b29d268d14d1f6973136f08251087cf4b7793927..6c420ca3a8cd29fff0582704cbaa111836b813ce 100644 (file)
 
 
 #include "inspircd.h"
+#include "modules/whois.h"
 
-/* $ModDesc: Provides the SWHOIS command which allows setting of arbitrary WHOIS lines */
+enum
+{
+       // From UnrealIRCd.
+       RPL_WHOISSPECIAL = 320
+};
 
 /** Handle /SWHOIS
  */
@@ -35,21 +40,21 @@ class CommandSwhois : public Command
        LocalIntExt operblock;
        StringExtItem swhois;
        CommandSwhois(Module* Creator)
-               : Command(Creator,"SWHOIS", 2,2)
-               , operblock("swhois_operblock", Creator)
-               , swhois("swhois", Creator)
+               : Command(Creator, "SWHOIS", 2, 2)
+               , operblock("swhois_operblock", ExtensionItem::EXT_USER, Creator)
+               , swhois("swhois", ExtensionItem::EXT_USER, Creator)
        {
                flags_needed = 'o'; syntax = "<nick> :<swhois>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User* user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                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 %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
                        return CMD_FAILURE;
                }
 
@@ -57,11 +62,11 @@ class CommandSwhois : public Command
                if (text)
                {
                        // We already had it set...
-                       if (!ServerInstance->ULine(user->server))
+                       if (!user->server->IsULine())
                                // Ulines set SWHOISes silently
                                ServerInstance->SNO->WriteGlobalSno('a', "%s used SWHOIS to set %s's extra whois from '%s' to '%s'", user->nick.c_str(), dest->nick.c_str(), text->c_str(), parameters[1].c_str());
                }
-               else if (!ServerInstance->ULine(user->server))
+               else if (!user->server->IsULine())
                {
                        // Ulines set SWHOISes silently
                        ServerInstance->SNO->WriteGlobalSno('a', "%s used SWHOIS to set %s's extra whois to '%s'", user->nick.c_str(), dest->nick.c_str(), parameters[1].c_str());
@@ -86,34 +91,28 @@ class CommandSwhois : public Command
 
 };
 
-class ModuleSWhois : public Module
+class ModuleSWhois : public Module, public Whois::LineEventListener
 {
        CommandSwhois cmd;
 
  public:
-       ModuleSWhois() : cmd(this)
-       {
-       }
-
-       void init()
+       ModuleSWhois()
+               : Whois::LineEventListener(this)
+               , cmd(this)
        {
-               ServiceProvider* providerlist[] = { &cmd, &cmd.operblock, &cmd.swhois };
-               ServerInstance->Modules->AddServices(providerlist, sizeof(providerlist)/sizeof(ServiceProvider*));
-               Implementation eventlist[] = { I_OnWhoisLine, I_OnPostOper, I_OnPostDeoper, I_OnDecodeMetaData };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
 
        // :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games.
-       ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+       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 :%s",user->nick.c_str(), dest->nick.c_str(), swhois->c_str());
+                               whois.SendLine(RPL_WHOISSPECIAL, *swhois);
                        }
                }
 
@@ -121,7 +120,7 @@ class ModuleSWhois : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       void OnPostOper(User* user, const std::string &opertype, const std::string &opername)
+       void OnPostOper(User* user, const std::string &opertype, const std::string &opername) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return;
@@ -136,7 +135,7 @@ class ModuleSWhois : public Module
                ServerInstance->PI->SendMetaData(user, "swhois", swhois);
        }
 
-       void OnPostDeoper(User* user)
+       void OnPostDeoper(User* user) CXX11_OVERRIDE
        {
                std::string* swhois = cmd.swhois.get(user);
                if (!swhois)
@@ -150,20 +149,14 @@ class ModuleSWhois : public Module
                ServerInstance->PI->SendMetaData(user, "swhois", "");
        }
 
-       void OnDecodeMetaData(Extensible* target, const std::string& extname, const std::string&)
+       void OnDecodeMetaData(Extensible* target, const std::string& extname, const std::string&) CXX11_OVERRIDE
        {
-               // XXX: We use a dynamic_cast in m_services_account so I used one
-               // here but do we actually need it or is static_cast okay?
-               User* dest = dynamic_cast<User*>(target);
+               User* dest = static_cast<User*>(target);
                if (dest && (extname == "swhois"))
                        cmd.operblock.set(dest, 0);
        }
 
-       ~ModuleSWhois()
-       {
-       }
-
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides the SWHOIS command which allows setting of arbitrary WHOIS lines", VF_OPTCOMMON | VF_VENDOR);
        }
diff --git a/src/modules/m_testnet.cpp b/src/modules/m_testnet.cpp
deleted file mode 100644 (file)
index 401766d..0000000
+++ /dev/null
@@ -1,233 +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/>.
- */
-
-
-/* $ModDesc: Provides a module for testing the server while linked in a network */
-
-#include "inspircd.h"
-
-struct vtbase
-{
-       virtual void isok(const char* name, int impl, Module* basemod, std::vector<std::string>& allmods) = 0;
-       virtual ~vtbase() {}
-};
-
-template<typename T> struct vtable : public vtbase
-{
-       union u {
-               T function;
-               struct v {
-                       size_t delta;
-                       size_t vtoff;
-               } v;
-       } u;
-       vtable(T t) {
-               u.function = t;
-       }
-       /** member function pointer dereference from vtable; depends on the GCC 4.4 ABI (x86_64) */
-       template<typename E> void* read(E* obj)
-       {
-               if (u.v.delta & 1)
-               {
-                       uint8_t* optr = reinterpret_cast<uint8_t*>(obj);
-                       optr += u.v.vtoff;
-                       uint8_t* vptr = *reinterpret_cast<uint8_t**>(optr);
-                       vptr += u.v.delta - 1;
-                       return *reinterpret_cast<void**>(vptr);
-               }
-               else
-                       return reinterpret_cast<void*>(u.v.delta);
-       }
-       void isok(const char* name, int impl, Module* basemod, std::vector<std::string>& allmods)
-       {
-               void* base = read(basemod);
-               for(unsigned int i=0; i < allmods.size(); ++i)
-               {
-                       Module* mod = ServerInstance->Modules->Find(allmods[i]);
-                       void* fptr = read(mod);
-                       for(EventHandlerIter j = ServerInstance->Modules->EventHandlers[impl].begin();
-                               j != ServerInstance->Modules->EventHandlers[impl].end(); j++)
-                       {
-                               if (mod == *j)
-                               {
-                                       if (fptr == base)
-                                       {
-                                               ServerInstance->SNO->WriteToSnoMask('a', "Module %s implements %s but uses default function",
-                                                       mod->ModuleSourceFile.c_str(), name);
-                                       }
-                                       goto done;
-                               }
-                       }
-                       if (fptr != base)
-                       {
-                               ServerInstance->SNO->WriteToSnoMask('a', "Module %s does not implement %s but overrides function",
-                                       mod->ModuleSourceFile.c_str(), name);
-                       }
-                       done:;
-               }
-       }
-};
-
-template<typename T> vtbase* vtinit(T t)
-{
-       return new vtable<T>(t);
-}
-
-static void checkall(Module* noimpl)
-{
-       std::vector<std::string> allmods = ServerInstance->Modules->GetAllModuleNames(0);
-#define CHK(name) do { \
-       vtbase* vt = vtinit(&Module::name); \
-       vt->isok(#name, I_ ## name, noimpl, allmods); \
-       delete vt; \
-} while (0)
-       CHK(OnUserConnect);
-       CHK(OnUserQuit);
-       CHK(OnUserDisconnect);
-       CHK(OnUserJoin);
-       CHK(OnUserPart);
-       CHK(OnRehash);
-       CHK(OnSendSnotice);
-       CHK(OnUserPreJoin);
-       CHK(OnUserPreKick);
-       CHK(OnUserKick);
-       CHK(OnOper);
-       CHK(OnInfo);
-       CHK(OnWhois);
-       CHK(OnUserPreInvite);
-       CHK(OnUserInvite);
-       CHK(OnUserPreMessage);
-       CHK(OnUserPreNotice);
-       CHK(OnUserPreNick);
-       CHK(OnUserMessage);
-       CHK(OnUserNotice);
-       CHK(OnMode);
-       CHK(OnGetServerDescription);
-       CHK(OnSyncUser);
-       CHK(OnSyncChannel);
-       CHK(OnDecodeMetaData);
-       CHK(OnWallops);
-       CHK(OnAcceptConnection);
-       CHK(OnChangeHost);
-       CHK(OnChangeName);
-       CHK(OnAddLine);
-       CHK(OnDelLine);
-       CHK(OnExpireLine);
-       CHK(OnUserPostNick);
-       CHK(OnPreMode);
-       CHK(On005Numeric);
-       CHK(OnKill);
-       CHK(OnRemoteKill);
-       CHK(OnLoadModule);
-       CHK(OnUnloadModule);
-       CHK(OnBackgroundTimer);
-       CHK(OnPreCommand);
-       CHK(OnCheckReady);
-       CHK(OnCheckInvite);
-       CHK(OnRawMode);
-       CHK(OnCheckKey);
-       CHK(OnCheckLimit);
-       CHK(OnCheckBan);
-       CHK(OnCheckChannelBan);
-       CHK(OnExtBanCheck);
-       CHK(OnStats);
-       CHK(OnChangeLocalUserHost);
-       CHK(OnPreTopicChange);
-       CHK(OnPostTopicChange);
-       CHK(OnEvent);
-       CHK(OnGlobalOper);
-       CHK(OnPostConnect);
-       CHK(OnAddBan);
-       CHK(OnDelBan);
-       CHK(OnChangeLocalUserGECOS);
-       CHK(OnUserRegister);
-       CHK(OnChannelPreDelete);
-       CHK(OnChannelDelete);
-       CHK(OnPostOper);
-       CHK(OnSyncNetwork);
-       CHK(OnSetAway);
-       CHK(OnPostCommand);
-       CHK(OnPostJoin);
-       CHK(OnWhoisLine);
-       CHK(OnBuildNeighborList);
-       CHK(OnGarbageCollect);
-       CHK(OnText);
-       CHK(OnPassCompare);
-       CHK(OnRunTestSuite);
-       CHK(OnNamesListItem);
-       CHK(OnNumeric);
-       CHK(OnHookIO);
-       CHK(OnPreRehash);
-       CHK(OnModuleRehash);
-       CHK(OnSendWhoLine);
-       CHK(OnChangeIdent);
-}
-
-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());
-               }
-               else if (parameters[0] == "check")
-               {
-                       checkall(creator);
-                       ServerInstance->SNO->WriteToSnoMask('a', "Module check complete");
-               }
-               return CMD_SUCCESS;
-       }
-};
-
-class ModuleTest : public Module
-{
-       CommandTest cmd;
- public:
-       ModuleTest() : cmd(this)
-       {
-       }
-
-       void init()
-       {
-               if (!strstr(ServerInstance->Config->ServerName.c_str(), ".test"))
-                       throw ModuleException("Don't load modules without reading their descriptions!");
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       Version GetVersion()
-       {
-               return Version("Provides a module for testing the server while linked in a network", VF_VENDOR|VF_OPTCOMMON);
-       }
-};
-
-MODULE_INIT(ModuleTest)
-
index bbbc518bdd1b7a7947464c43b83a8942ea2c7d2f..a17d3111671b14aaed29e6317bf204419170ffc8 100644 (file)
  */
 
 
-/* $ModDesc: Adds timed bans */
-
 #include "inspircd.h"
+#include "listmode.h"
 
 /** Holds a timed ban
  */
 class TimedBan
 {
  public:
-       std::string channel;
        std::string mask;
        time_t expire;
        Channel* chan;
@@ -42,97 +40,129 @@ timedbans TimedBanList;
  */
 class CommandTban : public Command
 {
-       static bool IsBanSet(Channel* chan, const std::string& mask)
+       ChanModeReference banmode;
+
+       bool IsBanSet(Channel* chan, const std::string& mask)
        {
-               for (BanList::const_iterator i = chan->bans.begin(); i != chan->bans.end(); ++i)
+               ListModeBase* banlm = static_cast<ListModeBase*>(*banmode);
+               if (!banlm)
+                       return false;
+               const ListModeBase::ModeList* bans = banlm->GetList(chan);
+               if (bans)
                {
-                       if (!strcasecmp(i->data.c_str(), mask.c_str()))
-                               return true;
+                       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>";
-               TRANSLATE4(TR_TEXT, TR_TEXT, TR_TEXT, TR_END);
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                Channel* channel = ServerInstance->FindChan(parameters[0]);
                if (!channel)
                {
-                       user->WriteNumeric(401, "%s %s :No such channel",user->nick.c_str(), parameters[0].c_str());
+                       user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
                        return CMD_FAILURE;
                }
-               int cm = channel->GetPrefixValue(user);
+               unsigned int cm = channel->GetPrefixValue(user);
                if (cm < HALFOP_VALUE)
                {
-                       user->WriteNumeric(482, "%s %s :You do not have permission to set bans on this channel",
-                               user->nick.c_str(), 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];
-               long duration = ServerInstance->Duration(parameters[1]);
-               unsigned long expire = duration + ServerInstance->Time();
-               if (duration < 1)
+               unsigned long duration;
+               if (!InspIRCd::Duration(parameters[1], duration))
                {
-                       user->WriteServ("NOTICE "+user->nick+" :Invalid ban time");
+                       user->WriteNotice("Invalid ban time");
                        return CMD_FAILURE;
                }
+               unsigned long expire = duration + ServerInstance->Time();
                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 && !ServerInstance->IsValidMask(mask))
+               if (!isextban && !InspIRCd::IsValidMask(mask))
                        mask.append("!*@*");
-               if ((mask.length() > 250) || (!ServerInstance->IsValidMask(mask) && !isextban))
-               {
-                       user->WriteServ("NOTICE "+user->nick+" :Invalid ban mask");
-                       return CMD_FAILURE;
-               }
 
                if (IsBanSet(channel, mask))
                {
-                       user->WriteServ("NOTICE %s :Ban already set", user->nick.c_str());
+                       user->WriteNotice("Ban already set");
                        return CMD_FAILURE;
                }
 
-               setban.push_back(mask);
-               // use CallHandler to make it so that the user sets the mode
-               // themselves
-               ServerInstance->Parser->CallHandler("MODE",setban,user);
-               if (!IsBanSet(channel, mask))
+               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(user, channel, NULL, setban);
+               if (ServerInstance->Modes->GetLastChangeList().empty())
+               {
+                       user->WriteNotice("Invalid ban mask");
                        return CMD_FAILURE;
+               }
 
-               CUList tmp;
-               T.channel = channelname;
                T.mask = mask;
                T.expire = expire + (IS_REMOTE(user) ? 5 : 0);
                T.chan = channel;
                TimedBanList.push_back(T);
 
-               const std::string addban = user->nick + " added a timed ban on " + mask + " lasting for " + ConvToStr(duration) + " seconds.";
+               const std::string addban = user->nick + " added a timed ban on " + mask + " lasting for " + InspIRCd::DurationString(duration) + ".";
                // If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
-               ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
+               PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
                char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
 
-               channel->WriteAllExcept(ServerInstance->FakeClient, true, pfxchar, tmp, "NOTICE %s :%s", channel->name.c_str(), addban.c_str());
+               ClientProtocol::Messages::Privmsg notice(ServerInstance->FakeClient, channel, addban, MSG_NOTICE);
+               channel->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar);
                ServerInstance->PI->SendChannelNotice(channel, pfxchar, addban);
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_BROADCAST;
        }
 };
 
+class BanWatcher : public ModeWatcher
+{
+ public:
+       BanWatcher(Module* parent)
+               : ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
+       {
+       }
+
+       void AfterMode(User* source, User* dest, Channel* chan, const std::string& banmask, bool adding) CXX11_OVERRIDE
+       {
+               if (adding)
+                       return;
+
+               for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); ++i)
+               {
+                       if (i->chan != chan)
+                               continue;
+
+                       const std::string& target = i->mask;
+                       if (irc::equals(banmask, target))
+                       {
+                               TimedBanList.erase(i);
+                               break;
+                       }
+               }
+       }
+};
+
 class ChannelMatcher
 {
        Channel* const chan;
@@ -152,37 +182,16 @@ class ChannelMatcher
 class ModuleTimedBans : public Module
 {
        CommandTban cmd;
+       BanWatcher banwatcher;
+
  public:
        ModuleTimedBans()
                : cmd(this)
+               , banwatcher(this)
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-               Implementation eventlist[] = { I_OnDelBan, I_OnBackgroundTimer, I_OnChannelDelete };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       virtual ModResult OnDelBan(User* source, Channel* chan, const std::string &banmask)
-       {
-               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))
-                       {
-                               TimedBanList.erase(i);
-                               break;
-                       }
-               }
-               return MOD_RES_PASSTHRU;
-       }
-
-       virtual void OnBackgroundTimer(time_t curtime)
+       void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
        {
                timedbans expired;
                for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end();)
@@ -198,41 +207,35 @@ 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;
-                               const std::string expiry = "*** Timed ban on " + chan + " expired.";
+                               const std::string expiry = "*** Timed ban on " + cr->name + " expired.";
                                // If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
-                               ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
+                               PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
                                char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
 
-                               cr->WriteAllExcept(ServerInstance->FakeClient, true, pfxchar, empty, "NOTICE %s :%s", cr->name.c_str(), expiry.c_str());
+                               ClientProtocol::Messages::Privmsg notice(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, cr, expiry, MSG_NOTICE);
+                               cr->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar);
                                ServerInstance->PI->SendChannelNotice(cr, pfxchar, expiry);
 
-                               ServerInstance->SendGlobalMode(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)
+       void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
        {
                // Remove all timed bans affecting the channel from internal bookkeeping
                TimedBanList.erase(std::remove_if(TimedBanList.begin(), TimedBanList.end(), ChannelMatcher(chan)), TimedBanList.end());
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Adds timed bans", VF_COMMON | VF_VENDOR);
+               return Version("Provides the TBAN command, timed channel bans", VF_COMMON | VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleTimedBans)
-
index b4e7e5a995941e829ef33ca38969504f84b46b0e..f8c0842b72c16edd7f2955d87aa5ada832de4594 100644 (file)
@@ -20,8 +20,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides /tline command used to test who a mask matches */
-
 /** Handle /TLINE
  */
 class CommandTline : public Command
@@ -32,16 +30,15 @@ class CommandTline : public Command
                flags_needed = 'o'; this->syntax = "<mask>";
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               float n_counted = 0;
-               float n_matched = 0;
-               float n_match_host = 0;
-               float n_match_ip = 0;
+               unsigned int n_matched = 0;
+               unsigned int n_match_host = 0;
+               unsigned int n_match_ip = 0;
 
-               for (user_hash::const_iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); u++)
+               const user_hash& users = ServerInstance->Users->GetUsers();
+               for (user_hash::const_iterator u = users.begin(); u != users.end(); ++u)
                {
-                       n_counted++;
                        if (InspIRCd::Match(u->second->GetFullRealHost(),parameters[0]))
                        {
                                n_matched++;
@@ -57,10 +54,15 @@ class CommandTline : public Command
                                }
                        }
                }
+
+               unsigned long n_counted = users.size();
                if (n_matched)
-                       user->WriteServ( "NOTICE %s :*** TLINE: Counted %0.0f user(s). Matched '%s' against %0.0f user(s) (%0.2f%% of the userbase). %0.0f by hostname and %0.0f by IP address.",user->nick.c_str(), n_counted, parameters[0].c_str(), n_matched, (n_matched/n_counted)*100, n_match_host, n_match_ip);
+               {
+                       float p = (n_matched / (float)n_counted) * 100;
+                       user->WriteNotice(InspIRCd::Format("*** TLINE: Counted %lu user(s). Matched '%s' against %u user(s) (%0.2f%% of the userbase). %u by hostname and %u by IP address.", n_counted, parameters[0].c_str(), n_matched, p, n_match_host, n_match_ip));
+               }
                else
-                       user->WriteServ( "NOTICE %s :*** TLINE: Counted %0.0f user(s). Matched '%s' against no user(s).", user->nick.c_str(), n_counted, parameters[0].c_str());
+                       user->WriteNotice(InspIRCd::Format("*** TLINE: Counted %lu user(s). Matched '%s' against no user(s).", n_counted, parameters[0].c_str()));
 
                return CMD_SUCCESS;
        }
@@ -75,20 +77,10 @@ class ModuleTLine : public Module
        {
        }
 
-       void init()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleTLine()
-       {
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides /tline command used to test who a mask matches", VF_VENDOR);
+               return Version("Provides the TLINE command, used to test how many users a mask matches against", VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleTLine)
-
index 3e8a846e76ba60e62ba7a7db0f5e6fc169a51e14..b0d004b1cf1c25ce957aa4ccae3356e7ead3eca8 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* $ModDesc: Implements server-side topic locks and the server-to-server command SVSTOPIC */
-
 #include "inspircd.h"
 
+enum
+{
+       // InspIRCd-specific.
+       ERR_TOPICLOCK = 744
+};
+
 class CommandSVSTOPIC : public Command
 {
  public:
@@ -29,9 +33,9 @@ class CommandSVSTOPIC : public Command
                flags_needed = FLAG_SERVERONLY;
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               if (!ServerInstance->ULine(user->server))
+               if (!user->server->IsULine())
                {
                        // Ulines only
                        return CMD_FAILURE;
@@ -44,45 +48,26 @@ class CommandSVSTOPIC : public Command
                if (parameters.size() == 4)
                {
                        // 4 parameter version, set all topic data on the channel to the ones given in the parameters
-                       time_t topicts = ConvToInt(parameters[1]);
+                       time_t topicts = ConvToNum<time_t>(parameters[1]);
                        if (!topicts)
                        {
-                               ServerInstance->Logs->Log("m_topiclock", DEFAULT, "Received SVSTOPIC with a 0 topicts, dropped.");
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Received SVSTOPIC with a 0 topicts, dropped.");
                                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;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                return ROUTE_BROADCAST;
        }
@@ -92,11 +77,7 @@ class FlagExtItem : public ExtensionItem
 {
  public:
        FlagExtItem(const std::string& key, Module* owner)
-               : ExtensionItem(key, owner)
-       {
-       }
-
-       virtual ~FlagExtItem()
+               : ExtensionItem(key, ExtensionItem::EXT_CHANNEL, owner)
        {
        }
 
@@ -105,7 +86,7 @@ class FlagExtItem : public ExtensionItem
                return (get_raw(container) != NULL);
        }
 
-       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const
+       std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
        {
                if (format == FORMAT_USER)
                        return "true";
@@ -113,7 +94,7 @@ class FlagExtItem : public ExtensionItem
                return "1";
        }
 
-       void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
        {
                if (value == "1")
                        set_raw(container, this);
@@ -134,7 +115,7 @@ class FlagExtItem : public ExtensionItem
                unset_raw(container);
        }
 
-       void free(void* item)
+       void free(Extensible* container, void* item) CXX11_OVERRIDE
        {
                // nothing to free
        }
@@ -151,26 +132,19 @@ class ModuleTopicLock : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-               ServerInstance->Modules->AddService(topiclock);
-               ServerInstance->Modules->Attach(I_OnPreTopicChange, this);
-       }
-
-       ModResult OnPreTopicChange(User* user, Channel* chan, const std::string &topic)
+       ModResult OnPreTopicChange(User* user, Channel* chan, const std::string &topic) CXX11_OVERRIDE
        {
                // 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(ERR_TOPICLOCK, chan->name, "TOPIC cannot be changed due to topic lock being active on the channel");
                        return MOD_RES_DENY;
                }
 
                return MOD_RES_PASSTHRU;
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Implements server-side topic locks and the server-to-server command SVSTOPIC", VF_COMMON | VF_VENDOR);
        }
index 2cd090f977de7b7cfbef310794ac5821ddf05080..420ba2c846d7ecaa9276c16dfc62badf371c0d5d 100644 (file)
 
 
 #include "inspircd.h"
-#include "m_cap.h"
+#include "modules/cap.h"
+#include "modules/names.h"
 
-/* $ModDesc: Provides the UHNAMES facility. */
-
-class ModuleUHNames : public Module
+class ModuleUHNames
+       : public Module
+       , public Names::EventListener
 {
- public:
-       GenericCap cap;
-
-       ModuleUHNames() : cap(this, "userhost-in-names")
-       {
-       }
-
-       void init()
-       {
-               Implementation eventlist[] = { I_OnEvent, I_OnPreCommand, I_OnNamesListItem, I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-       }
+ private:
+       Cap::Capability cap;
 
-       ~ModuleUHNames()
+ public:
+       ModuleUHNames()
+               : Names::EventListener(this)
+               , cap(this, "userhost-in-names")
        {
        }
 
-       Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides the UHNAMES facility.",VF_VENDOR);
+               return Version("Provides the UHNAMES (CAP userhost-in-names) capability", VF_VENDOR);
        }
 
-       void On005Numeric(std::string &output)
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               output.append(" UHNAMES");
+               tokens["UHNAMES"];
        }
 
-       ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
        {
                /* We don't actually create a proper command handler class for PROTOCTL,
                 * because other modules might want to have PROTOCTL hooks too.
@@ -64,27 +58,19 @@ 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;
                        }
                }
                return MOD_RES_PASSTHRU;
        }
 
-       void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+       ModResult OnNamesListItem(LocalUser* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
        {
-               if (!cap.ext.get(issuer))
-                       return;
+               if (cap.get(issuer))
+                       nick = memb->user->GetFullHost();
 
-               if (nick.empty())
-                       return;
-
-               nick = memb->user->GetFullHost();
-       }
-
-       void OnEvent(Event& ev)
-       {
-               cap.HandleEvent(ev);
+               return MOD_RES_PASSTHRU;
        }
 };
 
index ff392edc31efb35c9ef6870192a836f390c38b3d..ae1553a230e0a781147768e49303aa526996a34d 100644 (file)
  */
 
 
-/* $ModDesc: Provides the UNINVITE command which lets users un-invite other users from channels (!) */
-
 #include "inspircd.h"
+#include "modules/invite.h"
+
+enum
+{
+       // InspIRCd-specific.
+       RPL_UNINVITED = 653
+};
 
 /** 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>";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+               TRANSLATE2(TR_NICK, TR_TEXT);
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
                User* u;
                if (IS_LOCAL(user))
@@ -49,11 +57,11 @@ class CommandUninvite : public Command
                {
                        if (!c)
                        {
-                               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[1].c_str());
+                               user->WriteNumeric(Numerics::NoSuchChannel(parameters[1]));
                        }
                        else
                        {
-                               user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
+                               user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
                        }
 
                        return CMD_FAILURE;
@@ -63,7 +71,7 @@ class CommandUninvite : public Command
                {
                        if (c->GetPrefixValue(user) < HALFOP_VALUE)
                        {
-                               user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator", user->nick.c_str(), 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;
                        }
                }
@@ -75,29 +83,35 @@ class CommandUninvite : public Command
                LocalUser* lu = IS_LOCAL(u);
                if (lu)
                {
-                       irc::string xname(c->name.c_str());
-                       if (!lu->IsInvited(xname))
+                       // 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.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.c_str(), user->nick.c_str(), c->name.c_str(), u->nick.c_str());
-                       lu->RemoveInvite(xname);
-                       lu->WriteNumeric(493, "%s :You were uninvited from %s by %s", u->nick.c_str(), 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(RPL_UNINVITED, 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);
                }
 
                return CMD_SUCCESS;
        }
 
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               User* u = ServerInstance->FindNick(parameters[0]);
-               return u ? ROUTE_OPT_UCAST(u->server) : ROUTE_LOCALONLY;
+               return ROUTE_OPT_UCAST(parameters[0]);
        }
 };
 
@@ -111,20 +125,10 @@ class ModuleUninvite : public Module
        {
        }
 
-       void init()
-       {
-               ServerInstance->Modules->AddService(cmd);
-       }
-
-       virtual ~ModuleUninvite()
-       {
-       }
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides the UNINVITE command which lets users un-invite other users from channels", VF_VENDOR | VF_OPTCOMMON);
        }
 };
 
 MODULE_INIT(ModuleUninvite)
-
index 9502c91b19d99f852b8290952b3ba429e7a5a5a1..f6589acffa5e0b5d42f91a42740906ed380eb2d6 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides support for USERIP command */
-
 /** Handle /USERIP
  */
 class CommandUserip : public Command
@@ -30,17 +28,17 @@ 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)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               std::string retbuf = "340 " + user->nick + " :";
+               std::string retbuf;
                int nicks = 0;
                bool checked_privs = false;
                bool has_privs = false;
 
-               for (int i = 0; i < (int)parameters.size(); i++)
+               for (size_t i = 0; i < parameters.size(); i++)
                {
                        User *u = ServerInstance->FindNickOnly(parameters[i]);
                        if ((u) && (u->registered == REG_ALL))
@@ -54,15 +52,15 @@ class CommandUserip : public Command
                                                checked_privs = true;
                                                has_privs = user->HasPrivPermission("users/auspex");
                                                if (!has_privs)
-                                                       user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - You do not have the required operator privileges",user->nick.c_str());
+                                                       user->WriteNumeric(ERR_NOPRIVILEGES, "Permission Denied - You do not have the required operator privileges");
                                        }
 
                                        if (!has_privs)
                                                continue;
                                }
 
-                               retbuf = retbuf + u->nick + (IS_OPER(u) ? "*" : "") + "=";
-                               if (IS_AWAY(u))
+                               retbuf = retbuf + u->nick + (u->IsOper() ? "*" : "") + "=";
+                               if (u->IsAway())
                                        retbuf += "-";
                                else
                                        retbuf += "+";
@@ -72,7 +70,7 @@ class CommandUserip : public Command
                }
 
                if (nicks != 0)
-                       user->WriteServ(retbuf);
+                       user->WriteNumeric(RPL_USERIP, retbuf);
 
                return CMD_SUCCESS;
        }
@@ -87,28 +85,15 @@ class ModuleUserIP : public Module
        {
        }
 
-       void init()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd);
-               Implementation eventlist[] = { I_On005Numeric };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               tokens["USERIP"];
        }
 
-       virtual void On005Numeric(std::string &output)
+       Version GetVersion() CXX11_OVERRIDE
        {
-               output = output + " USERIP";
+               return Version("Provides the USERIP command", VF_VENDOR);
        }
-
-       virtual ~ModuleUserIP()
-       {
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version("Provides support for USERIP command",VF_VENDOR);
-       }
-
 };
 
 MODULE_INIT(ModuleUserIP)
-
index 31c504af8df7084a4ec713af73d631991eeb6dbd..ae126c89e2c8946748aff14c79f4f55dd4c02ecc 100644 (file)
 
 #include "inspircd.h"
 
-/* $ModDesc: Provides masking of user hostnames via traditional /VHOST command */
+struct CustomVhost
+{
+       const std::string name;
+       const std::string password;
+       const std::string hash;
+       const std::string vhost;
+
+       CustomVhost(const std::string& n, const std::string& p, const std::string& h, const std::string& v)
+               : name(n)
+               , password(p)
+               , hash(h)
+               , vhost(v)
+       {
+       }
+
+       bool CheckPass(User* user, const std::string& pass) const
+       {
+               return ServerInstance->PassCompare(user, password, pass, hash);
+       }
+};
+
+typedef std::multimap<std::string, CustomVhost> CustomVhostMap;
+typedef std::pair<CustomVhostMap::iterator, CustomVhostMap::iterator> MatchingConfigs;
 
 /** Handle /VHOST
  */
 class CommandVhost : public Command
 {
  public:
-       CommandVhost(Module* Creator) : Command(Creator,"VHOST", 2)
+       CustomVhostMap vhosts;
+
+       CommandVhost(Module* Creator)
+               : Command(Creator, "VHOST", 2)
        {
                syntax = "<username> <password>";
        }
 
-       CmdResult Handle (const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               ConfigTagList tags = ServerInstance->Config->ConfTags("vhost");
-               for(ConfigIter i = tags.first; i != tags.second; ++i)
-               {
-                       ConfigTag* tag = i->second;
-                       std::string mask = tag->getString("host");
-                       std::string username = tag->getString("user");
-                       std::string pass = tag->getString("pass");
-                       std::string hash = tag->getString("hash");
+               MatchingConfigs matching = vhosts.equal_range(parameters[0]);
 
-                       if (parameters[0] == username && !ServerInstance->PassCompare(user, pass, parameters[1], hash))
+               for (MatchingConfigs::first_type i = matching.first; i != matching.second; ++i)
+               {
+                       CustomVhost config = i->second;
+                       if (config.CheckPass(user, parameters[1]))
                        {
-                               if (!mask.empty())
-                               {
-                                       user->WriteServ("NOTICE "+user->nick+" :Setting your VHost: " + mask);
-                                       user->ChangeDisplayedHost(mask.c_str());
-                                       return CMD_SUCCESS;
-                               }
+                               user->WriteNotice("Setting your VHost: " + config.vhost);
+                               user->ChangeDisplayedHost(config.vhost);
+                               return CMD_SUCCESS;
                        }
                }
 
-               user->WriteServ("NOTICE "+user->nick+" :Invalid username or password.");
+               user->WriteNotice("Invalid username or password.");
                return CMD_FAILURE;
        }
 };
 
 class ModuleVHost : public Module
 {
- private:
        CommandVhost cmd;
 
  public:
-       ModuleVHost() : cmd(this)
+       ModuleVHost()
+               : cmd(this)
        {
        }
 
-       void init()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               ServerInstance->Modules->AddService(cmd);
-       }
+               CustomVhostMap newhosts;
+               ConfigTagList tags = ServerInstance->Config->ConfTags("vhost");
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+                       std::string mask = tag->getString("host");
+                       if (mask.empty())
+                               throw ModuleException("<vhost:host> is empty! at " + tag->getTagLocation());
+                       std::string username = tag->getString("user");
+                       if (username.empty())
+                               throw ModuleException("<vhost:user> is empty! at " + tag->getTagLocation());
+                       std::string pass = tag->getString("pass");
+                       if (pass.empty())
+                               throw ModuleException("<vhost:pass> is empty! at " + tag->getTagLocation());
+                       std::string hash = tag->getString("hash");
 
-       virtual ~ModuleVHost()
-       {
-       }
+                       CustomVhost vhost(username, pass, hash, mask);
+                       newhosts.insert(std::make_pair(username, vhost));
+               }
 
+               cmd.vhosts.swap(newhosts);
+       }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides masking of user hostnames via traditional /VHOST command",VF_VENDOR);
+               return Version("Provides masking of user hostnames via the VHOST command", VF_VENDOR);
        }
-
 };
 
 MODULE_INIT(ModuleVHost)
-
index a86483291805b8250a6320c768b18b48ecba3799..385ec9e02d6cd29577c7a7170c2141c632b06180 100644 (file)
@@ -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
 
 
 #include "inspircd.h"
+#include "modules/away.h"
 
-/* $ModDesc: Provides support for the /WATCH command */
+#define INSPIRCD_MONITOR_MANAGER_ONLY
+#include "m_monitor.cpp"
 
+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,
+       ERR_INVALIDWATCHNICK = 942
+};
 
-/*
- * 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.
- */
-
-/*
- * Before you start screaming, this definition is only used here, so moving it to a header is pointless.
- * Yes, it's horrid. Blame cl for being different. -- w00t
- */
-
-typedef nspace::hash_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>]";
-               TRANSLATE3(TR_NICK, TR_TEXT, TR_END); /* 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->GetDisplayedHost(), (unsigned long)target->awaytime, "is away");
+                       else
+                               user->WriteNumeric(RPL_NOWON, target->nick, target->ident, target->GetDisplayedHost(), (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 (!ServerInstance->ULine(user->server))
-                       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(ERR_INVALIDWATCHNICK, 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->GetDisplayedHost(), (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, ServerInstance->Config->Limits.NickMax))
+               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 %s :Invalid nickname", user->nick.c_str(), 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 (n != wl->end())
-                       {
-                               if (!n->second.empty())
-                                       user->WriteNumeric(602, "%s %s %s :stopped watching", user->nick.c_str(), n->first.c_str(), n->second.c_str());
-                               else
-                                       user->WriteNumeric(602, "%s %s * * 0 :stopped watching", user->nick.c_str(), nick);
+       void HandleStats(LocalUser* user)
+       {
+               user->CommandFloodPenalty += ListPenalty;
 
-                               wl->erase(n);
-                       }
+               // 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()));
 
-                       if (wl->empty())
-                       {
-                               ext.unset(user);
-                       }
-
-                       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());
                }
+               out.Flush();
+               user->WriteNumeric(RPL_ENDOFWATCHLIST, "End of WATCH S");
+       }
 
-               return CMD_SUCCESS;
+ public:
+       unsigned int maxwatch;
+
+       CommandWatch(Module* mod, IRCv3::Monitor::Manager& managerref)
+               : SplitCommand(mod, "WATCH")
+               , manager(managerref)
+       {
+               allow_empty_last_param = false;
+               syntax = "C|L|l|S|(+|-)<nick> [(+|-)<nick>]+";
        }
 
-       CmdResult add_watch(User* user, const char* nick)
+       CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
        {
-               if (!ServerInstance->IsNick(nick, ServerInstance->Config->Limits.NickMax))
+               if (parameters.empty())
                {
-                       user->WriteNumeric(942, "%s %s :Invalid nickname",user->nick.c_str(),nick);
-                       return CMD_FAILURE;
+                       HandleList(user, false);
+                       return CMD_SUCCESS;
                }
 
-               watchlist* wl = ext.get(user);
-               if (!wl)
-               {
-                       wl = new watchlist();
-                       ext.set(user, wl);
-               }
+               bool watch_l_done = false;
+               bool watch_s_done = false;
 
-               if (wl->size() >= MAX_WATCH)
+               for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end(); ++i)
                {
-                       user->WriteNumeric(512, "%s %s :Too many WATCH entries", user->nick.c_str(), nick);
-                       return CMD_FAILURE;
-               }
-
-               watchlist::iterator n = wl->find(nick);
-               if (n == wl->end())
-               {
-                       /* 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 %s :is online",user->nick.c_str(), nick, (*wl)[nick].c_str());
-                               if (IS_AWAY(target))
-                               {
-                                       user->WriteNumeric(609, "%s %s %s %s %lu :is away", user->nick.c_str(), 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 %s * * 0 :is offline",user->nick.c_str(), 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>]";
-               TRANSLATE2(TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
-       }
-
-       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 %s :is online", user->nick.c_str(), q->first.c_str(), q->second.c_str());
-                               }
-                       }
-                       user->WriteNumeric(607, "%s :End of WATCH list",user->nick.c_str());
-               }
-               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 %s :is online", user->nick.c_str(), q->first.c_str(), q->second.c_str());
-                                                               if (IS_AWAY(targ))
-                                                               {
-                                                                       user->WriteNumeric(609, "%s %s %s %s %lu :is away", user->nick.c_str(), targ->nick.c_str(), targ->ident.c_str(), targ->dhost.c_str(), (unsigned long) targ->awaytime);
-                                                               }
-                                                       }
-                                                       else
-                                                               user->WriteNumeric(605, "%s %s * * 0 :is offline", user->nick.c_str(), q->first.c_str());
-                                               }
-                                       }
-                                       user->WriteNumeric(607, "%s :End of WATCH list",user->nick.c_str());
-                               }
-                               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, "%s :You have %d and are on %d WATCH entries", user->nick.c_str(), you_have, youre_on);
-                                       user->WriteNumeric(606, "%s :%s",user->nick.c_str(), list.c_str());
-                                       user->WriteNumeric(607, "%s :End of WATCH S",user->nick.c_str());
-                               }
-                               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
+       , public Away::EventListener
 {
-       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->GetDisplayedHost()).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 init()
+       void Online(User* user)
        {
-               OnRehash(NULL);
-               ServerInstance->Modules->AddService(cmdw);
-               ServerInstance->Modules->AddService(sw);
-               ServerInstance->Modules->AddService(cmdw.ext);
-               Implementation eventlist[] = { I_OnRehash, I_OnGarbageCollect, I_OnUserQuit, I_OnPostConnect, I_OnUserPostNick, I_On005Numeric, I_OnSetAway };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+               SendAlert(user, user->nick, RPL_LOGON, "arrived online", user->age);
        }
 
-       virtual void OnRehash(User* user)
+       void Offline(User* user, const std::string& nick)
        {
-               maxwatch = ServerInstance->Config->ConfValue("watch")->getInt("maxentries", 32);
-               if (!maxwatch)
-                       maxwatch = 32;
+               SendAlert(user, nick, RPL_LOGOFF, "went offline", user->age);
        }
 
-       virtual ModResult OnSetAway(User *user, const std::string &awaymsg)
+ public:
+       ModuleWatch()
+               : Away::EventListener(this)
+               , manager(this, "watch")
+               , cmd(this, manager)
        {
-               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, (*n)->nick + " " + numeric);
-                       }
-               }
-
-               return MOD_RES_PASSTHRU;
        }
 
-       virtual void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
+       void ReadConfig(ConfigStatus& status) 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(601, "%s %s %s %s %lu :went offline", (*n)->nick.c_str() ,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);
-                               }
-                       }
-               }
+               ConfigTag* tag = ServerInstance->Config->ConfValue("watch");
+               cmd.maxwatch = tag->getUInt("maxwatch", 30, 1);
        }
 
-       virtual void OnGarbageCollect()
+       void OnPostConnect(User* user) 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;
+               Online(user);
        }
 
-       virtual void OnPostConnect(User* user)
+       void OnUserPostNick(User* user, const std::string& oldnick) 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 %s %lu :arrived online", (*n)->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
+               // Detect and ignore nickname case change
+               if (ServerInstance->FindNickOnly(oldnick) == user)
+                       return;
 
-                               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));
-                       }
-               }
+               Offline(user, oldnick);
+               Online(user);
        }
 
-       virtual void OnUserPostNick(User* user, const std::string &oldnick)
+       void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) 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());
-
-               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 %s %lu :went offline", (*n)->nick.c_str(), oldnick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
-                                       (*wl)[oldnick.c_str()].clear();
-                               }
-                       }
-               }
+               LocalUser* localuser = IS_LOCAL(user);
+               if (localuser)
+                       manager.UnwatchAll(localuser);
+               Offline(user, user->nick);
+       }
 
-               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 %s :arrived online", (*n)->nick.c_str(), user->nick.c_str(), (*wl)[user->nick.c_str()].c_str());
-                               }
-                       }
-               }
+       void OnUserAway(User* user) CXX11_OVERRIDE
+       {
+               SendAlert(user, user->nick, RPL_GONEAWAY, user->awaymsg.c_str(), user->awaytime);
        }
 
-       virtual void On005Numeric(std::string &output)
+       void OnUserBack(User* user) CXX11_OVERRIDE
        {
-               // we don't really have a limit...
-               output = output + " WATCH=" + ConvToStr(maxwatch);
+               SendAlert(user, user->nick, RPL_NOTAWAY, "is no longer away", ServerInstance->Time());
        }
 
-       virtual ~Modulewatch()
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
-               delete whos_watching_me;
+               tokens["WATCH"] = ConvToStr(cmd.maxwatch);
        }
 
-       virtual Version GetVersion()
+       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 (file)
index 0000000..51dada2
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * 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/>.
+ */
+
+/// $CompilerFlags: -Ivendor_directory("utfcpp")
+
+
+#include "inspircd.h"
+#include "iohook.h"
+#include "modules/hash.h"
+
+#include <utf8.h>
+
+typedef std::vector<std::string> OriginList;
+
+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:
+       OriginList allowedorigins;
+       bool sendastext;
+
+       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;
+       OriginList& allowedorigins;
+       bool& sendastext;
+
+       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];
+               switch (opcode & ~WS_FINBIT)
+               {
+                       case OP_CONTINUATION:
+                       case OP_TEXT:
+                       case OP_BINARY:
+                       {
+                               std::string appdata;
+                               const int result = HandleAppData(sock, appdata, true);
+                               if (result != 1)
+                                       return result;
+
+                               // Strip out any CR+LF which may have been erroneously sent.
+                               for (std::string::const_iterator iter = appdata.begin(); iter != appdata.end(); ++iter)
+                               {
+                                       if (*iter != '\r' && *iter != '\n')
+                                               destrecvq.push_back(*iter);
+                               }
+
+                               // If we are on the final message of this block append a line terminator.
+                               if (opcode & WS_FINBIT)
+                                       destrecvq.append("\r\n");
+
+                               return 1;
+                       }
+
+                       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;
+
+               bool allowedorigin = false;
+               HTTPHeaderFinder originheader;
+               if (originheader.Find(recvq, "Origin:", 7, reqend))
+               {
+                       const std::string origin = originheader.ExtractValue(recvq);
+                       for (OriginList::const_iterator iter = allowedorigins.begin(); iter != allowedorigins.end(); ++iter)
+                       {
+                               if (InspIRCd::Match(origin, *iter, ascii_case_insensitive_map))
+                               {
+                                       allowedorigin = true;
+                                       break;
+                               }
+                       }
+               }
+
+               if (!allowedorigin)
+               {
+                       FailHandshake(sock, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n", "WebSocket: Received HTTP request from a non-whitelisted origin");
+                       return -1;
+               }
+
+               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, OriginList& AllowedOrigins, bool& SendAsText)
+               : IOHookMiddle(Prov)
+               , state(STATE_HTTPREQ)
+               , lastpingpong(0)
+               , allowedorigins(AllowedOrigins)
+               , sendastext(SendAsText)
+       {
+               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);
+
+               std::string message;
+               for (StreamSocket::SendQueue::const_iterator elem = uppersendq.begin(); elem != uppersendq.end(); ++elem)
+               {
+                       for (StreamSocket::SendQueue::Element::const_iterator chr = elem->begin(); chr != elem->end(); ++chr)
+                       {
+                               if (*chr == '\n')
+                               {
+                                       // We have found an entire message. Send it in its own frame.
+                                       if (sendastext)
+                                       {
+                                               // If we send messages as text then we need to ensure they are valid UTF-8.
+                                               std::string encoded;
+                                               utf8::replace_invalid(message.begin(), message.end(), std::back_inserter(encoded));
+
+                                               mysendq.push_back(PrepareSendQElem(encoded.length(), OP_TEXT));
+                                               mysendq.push_back(encoded);
+                                       }
+                                       else
+                                       {
+                                               // Otherwise, send the raw message as a binary frame.
+                                               mysendq.push_back(PrepareSendQElem(message.length(), OP_BINARY));
+                                               mysendq.push_back(message);
+                                       }
+                                       message.clear();
+                               }
+                               else if (*chr != '\r')
+                               {
+                                       message.push_back(*chr);
+                               }
+                       }
+               }
+
+               // Empty the upper send queue and push whatever is left back onto it.
+               uppersendq.clear();
+               if (!message.empty())
+               {
+                       uppersendq.push_back(message);
+                       return 0;
+               }
+
+               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, allowedorigins, sendastext);
+}
+
+class ModuleWebSocket : public Module
+{
+       dynamic_reference_nocheck<HashProvider> hash;
+       reference<WebSocketHookProvider> hookprov;
+
+ public:
+       ModuleWebSocket()
+               : hash(this, "hash/sha1")
+               , hookprov(new WebSocketHookProvider(this))
+       {
+               sha1 = &hash;
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTagList tags = ServerInstance->Config->ConfTags("wsorigin");
+               if (tags.first == tags.second)
+                       throw ModuleException("You have loaded the websocket module but not configured any allowed origins!");
+
+               OriginList allowedorigins;
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+
+                       // Ensure that we have the <wsorigin:allow> parameter.
+                       const std::string allow = tag->getString("allow");
+                       if (allow.empty())
+                               throw ModuleException("<wsorigin:allow> is a mandatory field, at " + tag->getTagLocation());
+
+                       allowedorigins.push_back(allow);
+               }
+
+               ConfigTag* tag = ServerInstance->Config->ConfValue("websocket");
+               hookprov->sendastext = tag->getBool("sendastext", true);
+               hookprov->allowedorigins.swap(allowedorigins);
+       }
+
+       void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+       {
+               if (type != ExtensionItem::EXT_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)
index fb2a6f65a1325ecdeebad5b7f2d0948eee991b28..925024aea51baed27027aa74304692daf173aacd 100644 (file)
 
 #include "inspircd.h"
 #include "xline.h"
-
-/* $ModConfig: <xlinedb filename="data/xline.db">
- *  Specify the filename for the xline database here*/
-/* $ModDesc: Keeps a dynamic log of all XLines created, and stores them in a seperate conf file (xline.db). */
+#include <fstream>
 
 class ModuleXLineDB : public Module
 {
        bool dirty;
        std::string xlinedbpath;
  public:
-       void init()
+       void init() CXX11_OVERRIDE
        {
                /* Load the configuration
                 * Note:
-                *              this is on purpose not in the OnRehash() method. It would be non-trivial to change the database on-the-fly.
+                *              This is on purpose not changed on a rehash. It would be non-trivial to change the database on-the-fly.
                 *              Imagine a scenario where the new file already exists. Merging the current XLines with the existing database is likely a bad idea
                 *              ...and so is discarding all current in-memory XLines for the ones in the database.
                 */
                ConfigTag* Conf = ServerInstance->Config->ConfValue("xlinedb");
-               xlinedbpath = Conf->getString("filename", DATA_PATH "/xline.db");
+               xlinedbpath = ServerInstance->Config->Paths.PrependData(Conf->getString("filename", "xline.db"));
 
                // Read xlines before attaching to events
                ReadDatabase();
 
-               Implementation eventlist[] = { I_OnAddLine, I_OnDelLine, I_OnExpireLine, I_OnBackgroundTimer };
-               ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
                dirty = false;
        }
 
-       virtual ~ModuleXLineDB()
-       {
-       }
-
        /** Called whenever an xline is added by a local user.
         * This method is triggered after the line is added.
         * @param source The sender of the line or NULL for local server
         * @param line The xline being added
         */
-       void OnAddLine(User* source, XLine* line)
+       void OnAddLine(User* source, XLine* line) CXX11_OVERRIDE
        {
-               dirty = true;
+               if (!line->from_config)
+                       dirty = true;
        }
 
        /** Called whenever an xline is deleted.
@@ -68,17 +60,13 @@ class ModuleXLineDB : public Module
         * @param source The user removing the line or NULL for local server
         * @param line the line being deleted
         */
-       void OnDelLine(User* source, XLine* line)
+       void OnDelLine(User* source, XLine* line) CXX11_OVERRIDE
        {
-               dirty = true;
+               if (!line->from_config)
+                       dirty = true;
        }
 
-       void OnExpireLine(XLine *line)
-       {
-               dirty = true;
-       }
-
-       void OnBackgroundTimer(time_t now)
+       void OnBackgroundTimer(time_t now) CXX11_OVERRIDE
        {
                if (dirty)
                {
@@ -89,25 +77,23 @@ class ModuleXLineDB : public Module
 
        bool WriteDatabase()
        {
-               FILE *f;
-
                /*
                 * We need to perform an atomic write so as not to fuck things up.
-                * So, let's write to a temporary file, flush and sync the FD, then rename the file..
+                * So, let's write to a temporary file, flush it, then rename the file..
                 * Technically, that means that this can block, but I have *never* seen that.
-                *              -- w00t
+                *     -- w00t
                 */
-               ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Opening temporary database");
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Opening temporary database");
                std::string xlinenewdbpath = xlinedbpath + ".new";
-               f = fopen(xlinenewdbpath.c_str(), "w");
-               if (!f)
+               std::ofstream stream(xlinenewdbpath.c_str());
+               if (!stream.is_open())
                {
-                       ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: 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('x', "database: cannot create new xline db \"%s\": %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
                        return false;
                }
 
-               ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Opened. Writing..");
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Opened. Writing..");
 
                /*
                 * Now, much as I hate writing semi-unportable formats, additional
@@ -116,7 +102,7 @@ class ModuleXLineDB : public Module
                 * semblance of backwards compatibility for reading on startup..
                 *              -- w00t
                 */
-               fprintf(f, "VERSION 1\n");
+               stream << "VERSION 1" << std::endl;
 
                // Now, let's write.
                std::vector<std::string> types = ServerInstance->XLines->GetAllTypes();
@@ -129,22 +115,24 @@ class ModuleXLineDB : public Module
                        for (LookupIter i = lookup->begin(); i != lookup->end(); ++i)
                        {
                                XLine* line = i->second;
-                               fprintf(f, "LINE %s %s %s %lu %lu :%s\n", line->type.c_str(), line->Displayable(),
-                                       line->source.c_str(), (unsigned long)line->set_time, (unsigned long)line->duration, line->reason.c_str());
+                               if (line->from_config)
+                                       continue;
+
+                               stream << "LINE " << line->type << " " << line->Displayable() << " "
+                                       << line->source << " " << line->set_time << " "
+                                       << line->duration << " :" << line->reason << std::endl;
                        }
                }
 
-               ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Finished writing XLines. Checking for error..");
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Finished writing XLines. Checking for error..");
 
-               int write_error = 0;
-               write_error = ferror(f);
-               write_error |= fclose(f);
-               if (write_error)
+               if (stream.fail())
                {
-                       ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: 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('x', "database: cannot write to new xline db \"%s\": %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
                        return false;
                }
+               stream.close();
 
 #ifdef _WIN32
                remove(xlinedbpath.c_str());
@@ -152,8 +140,8 @@ class ModuleXLineDB : public Module
                // 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("m_xline_db",DEBUG, "xlinedb: 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('x', "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,65 +150,42 @@ class ModuleXLineDB : public Module
 
        bool ReadDatabase()
        {
-               FILE *f;
-               char linebuf[MAXBUF];
+               // If the xline database doesn't exist then we don't need to load it.
+               if (!FileSystem::FileExists(xlinedbpath))
+                       return true;
 
-               f = fopen(xlinedbpath.c_str(), "r");
-               if (!f)
+               std::ifstream stream(xlinedbpath.c_str());
+               if (!stream.is_open())
                {
-                       if (errno == ENOENT)
-                       {
-                               /* xline.db doesn't exist, fake good return value (we don't care about this) */
-                               return true;
-                       }
-                       else
-                       {
-                               /* this might be slightly more problematic. */
-                               ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot read database! %s (%d)", strerror(errno), errno);
-                               ServerInstance->SNO->WriteToSnoMask('a', "database: cannot read db: %s (%d)", strerror(errno), errno);
-                               return false;
-                       }
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot read database \"%s\"! %s (%d)", xlinedbpath.c_str(), strerror(errno), errno);
+                       ServerInstance->SNO->WriteToSnoMask('x', "database: cannot read xline db \"%s\": %s (%d)", xlinedbpath.c_str(), strerror(errno), errno);
+                       return false;
                }
 
-               while (fgets(linebuf, MAXBUF, f))
+               std::string line;
+               while (std::getline(stream, line))
                {
-                       char *c = linebuf;
-
-                       while (c && *c)
-                       {
-                               if (*c == '\n')
-                               {
-                                       *c = '\0';
-                               }
-
-                               c++;
-                       }
-
                        // Inspired by the command parser. :)
-                       irc::tokenstream tokens(linebuf);
+                       irc::tokenstream tokens(line);
                        int items = 0;
                        std::string command_p[7];
                        std::string tmp;
 
-                       while (tokens.GetToken(tmp) && (items < 7))
+                       while (tokens.GetTrailing(tmp) && (items < 7))
                        {
                                command_p[items] = tmp;
                                items++;
                        }
 
-                       ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Processing %s", linebuf);
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Processing %s", line.c_str());
 
                        if (command_p[0] == "VERSION")
                        {
-                               if (command_p[1] == "1")
+                               if (command_p[1] != "1")
                                {
-                                       ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Reading db version %s", command_p[1].c_str());
-                               }
-                               else
-                               {
-                                       fclose(f);
-                                       ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: I got database version %s - I don't understand it", command_p[1].c_str());
-                                       ServerInstance->SNO->WriteToSnoMask('a', "database: I got a database version (%s) I don't understand", command_p[1].c_str());
+                                       stream.close();
+                                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "I got database version %s - I don't understand it", command_p[1].c_str());
+                                       ServerInstance->SNO->WriteToSnoMask('x', "database: I got a database version (%s) I don't understand", command_p[1].c_str());
                                        return false;
                                }
                        }
@@ -231,7 +196,7 @@ class ModuleXLineDB : public Module
 
                                if (!xlf)
                                {
-                                       ServerInstance->SNO->WriteToSnoMask('a', "database: Unknown line type (%s).", command_p[1].c_str());
+                                       ServerInstance->SNO->WriteToSnoMask('x', "database: Unknown line type (%s).", command_p[1].c_str());
                                        continue;
                                }
 
@@ -246,18 +211,14 @@ class ModuleXLineDB : public Module
                                        delete xl;
                        }
                }
-
-               fclose(f);
+               stream.close();
                return true;
        }
 
-
-
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Keeps a dynamic log of all XLines created, and stores them in a separate conf file (xline.db).", VF_VENDOR);
+               return Version("Provides the ability to store X-lines in a database file", VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleXLineDB)
-
diff --git a/src/modules/sasl.h b/src/modules/sasl.h
deleted file mode 100644 (file)
index f673511..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 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
- * 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/>.
- */
-
-
-#ifndef SASL_H
-#define SASL_H
-
-class SASLFallback : public Event
-{
- public:
-       const parameterlist& params;
-       SASLFallback(Module* me, const parameterlist& p)
-               : Event(me, "sasl_fallback"), params(p)
-       {
-               Send();
-       }
-};
-
-#endif
diff --git a/src/modules/spanningtree.h b/src/modules/spanningtree.h
deleted file mode 100644 (file)
index 212f35f..0000000
+++ /dev/null
@@ -1,43 +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/>.
- */
-
-
-#ifndef SPANNINGTREE_H
-#define SPANNINGTREE_H
-
-struct AddServerEvent : public Event
-{
-       const std::string servername;
-       AddServerEvent(Module* me, const std::string& name)
-               : Event(me, "new_server"), servername(name)
-       {
-               Send();
-       }
-};
-
-struct DelServerEvent : public Event
-{
-       const std::string servername;
-       DelServerEvent(Module* me, const std::string& name)
-               : Event(me, "lost_server"), servername(name)
-       {
-               Send();
-       }
-};
-
-#endif
diff --git a/src/modules/sql.h b/src/modules/sql.h
deleted file mode 100644 (file)
index 436cd1d..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 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
- * 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/>.
- */
-
-
-#ifndef INSPIRCD_SQLAPI_3
-#define INSPIRCD_SQLAPI_3
-
-/** Defines the error types which SQLerror may be set to
- */
-enum SQLerrorNum { SQL_NO_ERROR, SQL_BAD_DBID, SQL_BAD_CONN, SQL_QSEND_FAIL, SQL_QREPLY_FAIL };
-
-/** A list of format parameters for an SQLquery object.
- */
-typedef std::vector<std::string> ParamL;
-
-typedef std::map<std::string, std::string> ParamM;
-
-class SQLEntry
-{
- public:
-       std::string value;
-       bool nul;
-       SQLEntry() : nul(true) {}
-       SQLEntry(const std::string& v) : value(v), nul(false) {}
-       inline operator std::string&() { return value; }
-};
-
-typedef std::vector<SQLEntry> SQLEntries;
-
-/**
- * Result of an SQL query. Only valid inside OnResult
- */
-class SQLResult : public classbase
-{
- public:
-       /**
-        * Return the number of rows in the result.
-        *
-        * Note that if you have perfomed an INSERT or UPDATE query or other
-        * query which will not return rows, this will return the number of
-        * affected rows. In this case you SHOULD NEVER access any of the result
-        * set rows, as there aren't any!
-        * @returns Number of rows in the result set.
-        */
-       virtual int Rows() = 0;
-
-       /**
-        * Return a single row (result of the query). The internal row counter
-        * is incremented by one.
-        *
-        * @param result Storage for the result data.
-        * @returns true if there was a row, false if no row exists (end of
-        * iteration)
-        */
-       virtual bool GetRow(SQLEntries& result) = 0;
-
-       /** Returns column names for the items in this row
-        */
-       virtual void GetCols(std::vector<std::string>& result) = 0;
-};
-
-/** SQLerror holds the error state of a request.
- * The error string varies from database software to database software
- * and should be used to display informational error messages to users.
- */
-class SQLerror
-{
- public:
-       /** The error id
-        */
-       SQLerrorNum id;
-
-       /** The error string
-        */
-       std::string str;
-
-       /** Initialize an SQLerror
-        * @param i The error ID to set
-        * @param s The (optional) error string to set
-        */
-       SQLerror(SQLerrorNum i, const std::string &s = "")
-       : id(i), str(s)
-       {
-       }
-
-       /** Return the error string for an error
-        */
-       const char* Str()
-       {
-               if(str.length())
-                       return str.c_str();
-
-               switch(id)
-               {
-                       case SQL_BAD_DBID:
-                               return "Invalid database ID";
-                       case SQL_BAD_CONN:
-                               return "Invalid connection";
-                       case SQL_QSEND_FAIL:
-                               return "Sending query failed";
-                       case SQL_QREPLY_FAIL:
-                               return "Getting query result failed";
-                       default:
-                               return "Unknown error";
-               }
-       }
-};
-
-/**
- * Object representing an SQL query. This should be allocated on the heap and
- * passed to an SQLProvider, which will free it when the query is complete or
- * when the querying module is unloaded.
- *
- * You should store whatever information is needed to have the callbacks work in
- * this object (UID of user, channel name, etc).
- */
-class SQLQuery : public classbase
-{
- public:
-       ModuleRef creator;
-
-       SQLQuery(Module* Creator) : creator(Creator) {}
-       virtual ~SQLQuery() {}
-
-       virtual void OnResult(SQLResult& result) = 0;
-       /**
-        * Called when the query fails
-        */
-       virtual void OnError(SQLerror& error) { }
-};
-
-/**
- * Provider object for SQL servers
- */
-class SQLProvider : public DataProvider
-{
- public:
-       SQLProvider(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) {}
-       /** Submit an asynchronous SQL request
-        * @param callback The result reporting point
-        * @param query The hardcoded query string. If you have parameters to substitute, see below.
-        */
-       virtual void submit(SQLQuery* callback, const std::string& query) = 0;
-
-       /** Submit an asynchronous SQL request
-        * @param callback The result reporting point
-        * @param format The simple parameterized query string ('?' parameters)
-        * @param p Parameters to fill in for the '?' entries
-        */
-       virtual void submit(SQLQuery* callback, const std::string& format, const ParamL& p) = 0;
-
-       /** Submit an asynchronous SQL request.
-        * @param callback The result reporting point
-        * @param format The parameterized query string ('$name' parameters)
-        * @param p Parameters to fill in for the '$name' entries
-        */
-       virtual void submit(SQLQuery* callback, const std::string& format, const ParamM& p) = 0;
-
-       /** Convenience function to prepare a map from a User* */
-       void PopulateUserInfo(User* user, ParamM& userinfo)
-       {
-               userinfo["nick"] = user->nick;
-               userinfo["host"] = user->host;
-               userinfo["ip"] = user->GetIPString();
-               userinfo["gecos"] = user->fullname;
-               userinfo["ident"] = user->ident;
-               userinfo["server"] = user->server;
-               userinfo["uuid"] = user->uuid;
-       }
-};
-
-#endif
diff --git a/src/modules/ssl.h b/src/modules/ssl.h
deleted file mode 100644 (file)
index 4c87755..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   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/>.
- */
-
-
-#ifndef SSL_H
-#define SSL_H
-
-#include <map>
-#include <string>
-
-/** ssl_cert is a class which abstracts SSL certificate
- * and key information.
- *
- * Because gnutls and openssl represent key information in
- * wildly different ways, this class allows it to be accessed
- * in a unified manner. These classes are attached to ssl-
- * connected local users using SSLCertExt
- */
-class ssl_cert : public refcountbase
-{
- public:
-       std::string dn;
-       std::string issuer;
-       std::string error;
-       std::string fingerprint;
-       bool trusted, invalid, unknownsigner, revoked;
-
-       ssl_cert() : trusted(false), invalid(true), unknownsigner(true), revoked(false) {}
-
-       /** Get certificate distinguished name
-        * @return Certificate DN
-        */
-       const std::string& GetDN()
-       {
-               return dn;
-       }
-
-       /** Get Certificate issuer
-        * @return Certificate issuer
-        */
-       const std::string& GetIssuer()
-       {
-               return issuer;
-       }
-
-       /** Get error string if an error has occured
-        * @return The error associated with this users certificate,
-        * or an empty string if there is no error.
-        */
-       const std::string& GetError()
-       {
-               return error;
-       }
-
-       /** Get key fingerprint.
-        * @return The key fingerprint as a hex string.
-        */
-       const std::string& GetFingerprint()
-       {
-               return fingerprint;
-       }
-
-       /** Get trust status
-        * @return True if this is a trusted certificate
-        * (the certificate chain validates)
-        */
-       bool IsTrusted()
-       {
-               return trusted;
-       }
-
-       /** Get validity status
-        * @return True if the certificate itself is
-        * correctly formed.
-        */
-       bool IsInvalid()
-       {
-               return invalid;
-       }
-
-       /** Get signer status
-        * @return True if the certificate appears to be
-        * self-signed.
-        */
-       bool IsUnknownSigner()
-       {
-               return unknownsigner;
-       }
-
-       /** Get revokation status.
-        * @return True if the certificate is revoked.
-        * Note that this only works properly for GnuTLS
-        * right now.
-        */
-       bool IsRevoked()
-       {
-               return revoked;
-       }
-
-       bool IsCAVerified()
-       {
-               return trusted && !invalid && !revoked && !unknownsigner && error.empty();
-       }
-
-       std::string GetMetaLine()
-       {
-               std::stringstream value;
-               bool hasError = !error.empty();
-               value << (IsInvalid() ? "v" : "V") << (IsTrusted() ? "T" : "t") << (IsRevoked() ? "R" : "r")
-                       << (IsUnknownSigner() ? "s" : "S") << (hasError ? "E" : "e") << " ";
-               if (hasError)
-                       value << GetError();
-               else
-                       value << GetFingerprint() << " " << GetDN() << " " << GetIssuer();
-               return value.str();
-       }
-};
-
-/** Get certificate from a socket (only useful with an SSL module) */
-struct SocketCertificateRequest : public Request
-{
-       StreamSocket* const sock;
-       ssl_cert* cert;
-
-       SocketCertificateRequest(StreamSocket* ss, Module* Me)
-               : Request(Me, ss->GetIOHook(), "GET_SSL_CERT"), sock(ss), cert(NULL)
-       {
-               Send();
-       }
-
-       std::string GetFingerprint()
-       {
-               if (cert)
-                       return cert->GetFingerprint();
-               return "";
-       }
-};
-
-/** Get certificate from a user (requires m_sslinfo) */
-struct UserCertificateRequest : public Request
-{
-       User* const user;
-       ssl_cert* cert;
-
-       UserCertificateRequest(User* u, Module* Me, Module* info = ServerInstance->Modules->Find("m_sslinfo.so"))
-               : Request(Me, info, "GET_USER_CERT"), user(u), cert(NULL)
-       {
-               Send();
-       }
-
-       std::string GetFingerprint()
-       {
-               if (cert)
-                       return cert->GetFingerprint();
-               return "";
-       }
-};
-
-class SSLRawSessionRequest : public Request
-{
- public:
-       const int fd;
-       void* data;
-
-       SSLRawSessionRequest(int FD, Module* srcmod, Module* destmod)
-               : Request(srcmod, destmod, "GET_RAW_SSL_SESSION")
-               , fd(FD)
-               , data(NULL)
-       {
-               Send();
-       }
-};
-
-#endif
diff --git a/src/modules/u_listmode.h b/src/modules/u_listmode.h
deleted file mode 100644 (file)
index a728eb8..0000000
+++ /dev/null
@@ -1,425 +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/>.
- */
-
-
-#ifndef INSPIRCD_LISTMODE_PROVIDER
-#define INSPIRCD_LISTMODE_PROVIDER
-
-/** Get the time as a string
- */
-inline std::string stringtime()
-{
-       std::ostringstream TIME;
-       TIME << ServerInstance->Time();
-       return TIME.str();
-}
-
-/** An item in a listmode's list
- */
-class ListItem
-{
-public:
-       std::string nick;
-       std::string mask;
-       std::string time;
-};
-
-/** The number of items a listmode's list may contain
- */
-class ListLimit
-{
-public:
-       std::string mask;
-       unsigned int limit;
-};
-
-/** Items stored in the channel's list
- */
-typedef std::list<ListItem> modelist;
-/** Max items per channel by name
- */
-typedef std::list<ListLimit> limitlist;
-
-/** The base class for list modes, should be inherited.
- */
-class ListModeBase : public ModeHandler
-{
- protected:
-       /** Numeric to use when outputting the list
-        */
-       unsigned int listnumeric;
-       /** Numeric to indicate end of list
-        */
-       unsigned int endoflistnumeric;
-       /** String to send for end of list
-        */
-       std::string endofliststring;
-       /** Automatically tidy up entries
-        */
-       bool tidy;
-       /** Config tag to check for max items per channel
-        */
-       std::string configtag;
-       /** Limits on a per-channel basis read from the tag
-        * specified in ListModeBase::configtag
-        */
-       limitlist chanlimits;
-
- public:
-       /** Storage key
-        */
-       SimpleExtItem<modelist> extItem;
-
-       /** Constructor.
-        * @param Instance The creator of this class
-        * @param modechar Mode character
-        * @param eolstr End of list string
-        * @pram lnum List numeric
-        * @param eolnum End of list numeric
-        * @param autotidy Automatically tidy list entries on add
-        * @param ctag Configuration tag to get limits from
-        */
-       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 = "banlist")
-               : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL), 
-               listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy),
-               configtag(ctag), extItem("listbase_mode_" + name + "_list", Creator)
-       {
-               list = true;
-       }
-
-       /** See mode.h
-        */
-       std::pair<bool,std::string> ModeSet(User*, User*, Channel* channel, const std::string &parameter)
-       {
-               modelist* el = extItem.get(channel);
-               if (el)
-               {
-                       for (modelist::iterator it = el->begin(); it != el->end(); it++)
-                       {
-                               if(parameter == it->mask)
-                               {
-                                       return std::make_pair(true, parameter);
-                               }
-                       }
-               }
-               return std::make_pair(false, parameter);
-       }
-
-       /** Display the list for this mode
-        * @param user The user to send the list to
-        * @param channel The channel the user is requesting the list for
-        */
-       virtual void DisplayList(User* user, Channel* channel)
-       {
-               modelist* el = extItem.get(channel);
-               if (el)
-               {
-                       for (modelist::reverse_iterator it = el->rbegin(); it != el->rend(); ++it)
-                       {
-                               user->WriteNumeric(listnumeric, "%s %s %s %s %s", user->nick.c_str(), channel->name.c_str(), it->mask.c_str(), (it->nick.length() ? it->nick.c_str() : ServerInstance->Config->ServerName.c_str()), it->time.c_str());
-                       }
-               }
-               user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
-       }
-
-       virtual void DisplayEmptyList(User* user, Channel* channel)
-       {
-               user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
-       }
-
-       /** Remove all instances of the mode from a channel.
-        * See mode.h
-        * @param channel The channel to remove all instances of the mode from
-        */
-       virtual void RemoveMode(Channel* channel, irc::modestacker* stack)
-       {
-               modelist* el = extItem.get(channel);
-               if (el)
-               {
-                       irc::modestacker modestack(false);
-
-                       for (modelist::iterator it = el->begin(); it != el->end(); it++)
-                       {
-                               if (stack)
-                                       stack->Push(this->GetModeChar(), it->mask);
-                               else
-                                       modestack.Push(this->GetModeChar(), it->mask);
-                       }
-
-                       if (stack)
-                               return;
-
-                       std::vector<std::string> stackresult;
-                       stackresult.push_back(channel->name);
-                       while (modestack.GetStackedLine(stackresult))
-                       {
-                               ServerInstance->SendMode(stackresult, ServerInstance->FakeClient);
-                               stackresult.clear();
-                               stackresult.push_back(channel->name);
-                       }
-               }
-       }
-
-       /** See mode.h
-        */
-       virtual void RemoveMode(User*, irc::modestacker* stack)
-       {
-               /* Listmodes dont get set on users */
-       }
-
-       /** Perform a rehash of this mode's configuration data
-        */
-       virtual void DoRehash()
-       {
-               ConfigTagList tags = ServerInstance->Config->ConfTags(configtag);
-
-               chanlimits.clear();
-
-               for (ConfigIter i = tags.first; i != tags.second; i++)
-               {
-                       // For each <banlist> tag
-                       ConfigTag* c = i->second;
-                       ListLimit limit;
-                       limit.mask = c->getString("chan");
-                       limit.limit = c->getInt("limit");
-
-                       if (limit.mask.size() && limit.limit > 0)
-                               chanlimits.push_back(limit);
-               }
-
-               // 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.
-               ListLimit limit;
-               limit.mask = "*";
-               limit.limit = 64;
-               chanlimits.push_back(limit);
-       }
-
-       /** Populate the Implements list with the correct events for a List Mode
-        */
-       virtual void DoImplements(Module* m)
-       {
-               ServerInstance->Modules->AddService(extItem);
-               this->DoRehash();
-               Implementation eventlist[] = { I_OnSyncChannel, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, m, sizeof(eventlist)/sizeof(Implementation));
-       }
-
-       /** Handle the list mode.
-        * See mode.h
-        */
-       virtual ModeAction OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-       {
-               // Try and grab the list
-               modelist* el = extItem.get(channel);
-
-               if (adding)
-               {
-                       if (tidy)
-                               ModeParser::CleanMask(parameter);
-
-                       if (parameter.length() > 250)
-                               return MODEACTION_DENY;
-
-                       // If there was no list
-                       if (!el)
-                       {
-                               // Make one
-                               el = new modelist;
-                               extItem.set(channel, el);
-                       }
-
-                       // Check if the item already exists in the list
-                       for (modelist::iterator it = el->begin(); it != el->end(); it++)
-                       {
-                               if (parameter == it->mask)
-                               {
-                                       /* Give a subclass a chance to error about this */
-                                       TellAlreadyOnList(source, channel, parameter);
-
-                                       // it does, deny the change
-                                       return MODEACTION_DENY;
-                               }
-                       }
-
-                       unsigned int maxsize = 0;
-
-                       for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); it++)
-                       {
-                               if (InspIRCd::Match(channel->name, it->mask))
-                               {
-                                       // We have a pattern matching the channel...
-                                       maxsize = el->size();
-                                       if (!IS_LOCAL(source) || (maxsize < it->limit))
-                                       {
-                                               /* Ok, it *could* be allowed, now give someone subclassing us
-                                                * a chance to validate the parameter.
-                                                * The param is passed by reference, so they can both modify it
-                                                * and tell us if we allow it or not.
-                                                *
-                                                * eg, the subclass could:
-                                                * 1) allow
-                                                * 2) 'fix' parameter and then allow
-                                                * 3) deny
-                                                */
-                                               if (ValidateParam(source, channel, parameter))
-                                               {
-                                                       // And now add the mask onto the list...
-                                                       ListItem e;
-                                                       e.mask = parameter;
-                                                       e.nick = source->nick;
-                                                       e.time = stringtime();
-
-                                                       el->push_back(e);
-                                                       return MODEACTION_ALLOW;
-                                               }
-                                               else
-                                               {
-                                                       /* If they deny it they have the job of giving an error message */
-                                                       return MODEACTION_DENY;
-                                               }
-                                       }
-                                       else
-                                               break;
-                               }
-                       }
-
-                       /* List is full, give subclass a chance to send a custom message */
-                       if (!TellListTooLong(source, channel, parameter))
-                       {
-                               source->WriteNumeric(478, "%s %s %s :Channel ban/ignore list is full", source->nick.c_str(), channel->name.c_str(), parameter.c_str());
-                       }
-
-                       parameter.clear();
-                       return MODEACTION_DENY;
-               }
-               else
-               {
-                       // We're taking the mode off
-                       if (el)
-                       {
-                               for (modelist::iterator it = el->begin(); it != el->end(); it++)
-                               {
-                                       if (parameter == it->mask)
-                                       {
-                                               el->erase(it);
-                                               if (el->empty())
-                                               {
-                                                       extItem.unset(channel);
-                                               }
-                                               return MODEACTION_ALLOW;
-                                       }
-                               }
-                               /* Tried to remove something that wasn't set */
-                               TellNotSet(source, channel, parameter);
-                               parameter.clear();
-                               return MODEACTION_DENY;
-                       }
-                       else
-                       {
-                               /* Hmm, taking an exception off a non-existant list, DIE */
-                               TellNotSet(source, channel, parameter);
-                               parameter.clear();
-                               return MODEACTION_DENY;
-                       }
-               }
-               return MODEACTION_DENY;
-       }
-
-       /** Syncronize channel item list with another server.
-        * See modules.h
-        * @param chan Channel to syncronize
-        * @param proto Protocol module pointer
-        * @param opaque Opaque connection handle
-        */
-       virtual void DoSyncChannel(Channel* chan, Module* proto, void* opaque)
-       {
-               modelist* mlist = extItem.get(chan);
-               irc::modestacker modestack(true);
-               std::vector<std::string> stackresult;
-               std::vector<TranslateType> types;
-               types.push_back(TR_TEXT);
-               if (mlist)
-               {
-                       for (modelist::iterator it = mlist->begin(); it != mlist->end(); it++)
-                       {
-                               modestack.Push(std::string(1, mode)[0], it->mask);
-                       }
-               }
-               while (modestack.GetStackedLine(stackresult))
-               {
-                       types.assign(stackresult.size(), this->GetTranslateType());
-                       proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, stackresult, types);
-                       stackresult.clear();
-               }
-       }
-
-       /** Clean up module on unload
-        * @param target_type Type of target to clean
-        * @param item Item to clean
-        */
-       virtual void DoCleanup(int, void*)
-       {
-       }
-
-       /** Validate parameters.
-        * Overridden by implementing module.
-        * @param source Source user adding the parameter
-        * @param channel Channel the parameter is being added to
-        * @param parameter The actual parameter being added
-        * @return true if the parameter is valid
-        */
-       virtual bool ValidateParam(User*, Channel*, std::string&)
-       {
-               return true;
-       }
-
-       /** Tell the user the list is too long.
-        * Overridden by implementing module.
-        * @param source Source user adding the parameter
-        * @param channel Channel the parameter is being added to
-        * @param parameter The actual parameter being added
-        * @return Ignored
-        */
-       virtual bool TellListTooLong(User*, Channel*, std::string&)
-       {
-               return false;
-       }
-
-       /** Tell the user an item is already on the list.
-        * Overridden by implementing module.
-        * @param source Source user adding the parameter
-        * @param channel Channel the parameter is being added to
-        * @param parameter The actual parameter being added
-        */
-       virtual void TellAlreadyOnList(User*, Channel*, std::string&)
-       {
-       }
-
-       /** Tell the user that the parameter is not in the list.
-        * Overridden by implementing module.
-        * @param source Source user removing the parameter
-        * @param channel Channel the parameter is being removed from
-        * @param parameter The actual parameter being removed
-        */
-       virtual void TellNotSet(User*, Channel*, std::string&)
-       {
-       }
-};
-
-#endif
index d05ece8a45ac2c7f86bce4308dbb3fcc598687c9..ff8f6211fbaa663cc2399a56c9f2f396649e5859 100644 (file)
@@ -23,7 +23,6 @@
 #include <signal.h>
 #include "exitcodes.h"
 #include "inspircd.h"
-#include "inspircd_version.h"
 
 void InspIRCd::SignalHandler(int signal)
 {
@@ -32,12 +31,13 @@ void InspIRCd::SignalHandler(int signal)
 #else
        if (signal == SIGHUP)
        {
-               Rehash("Caught SIGHUP");
+               ServerInstance->SNO->WriteGlobalSno('a', "Rehashing due to SIGHUP");
+               Rehash();
        }
        else if (signal == SIGTERM)
 #endif
        {
-               Exit(signal);
+               Exit(EXIT_STATUS_SIGTERM);
        }
 }
 
@@ -46,55 +46,43 @@ void InspIRCd::Exit(int status)
 #ifdef _WIN32
        SetServiceStopped(status);
 #endif
-       this->SendError("Exiting with status " + ConvToStr(status) + " (" + std::string(ExitCodes[status]) + ")");
        this->Cleanup();
-       delete this;
        ServerInstance = NULL;
+       delete this;
        exit (status);
 }
 
-void RehashHandler::Call(const std::string &reason)
+void InspIRCd::Rehash(const std::string& uuid)
 {
-       ServerInstance->SNO->WriteToSnoMask('a', "Rehashing config file %s %s",ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()), reason.c_str());
-       ServerInstance->RehashUsersAndChans();
-       FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
        if (!ServerInstance->ConfigThread)
        {
-               ServerInstance->ConfigThread = new ConfigReaderThread("");
-               ServerInstance->Threads->Start(ServerInstance->ConfigThread);
+               ServerInstance->ConfigThread = new ConfigReaderThread(uuid);
+               ServerInstance->Threads.Start(ServerInstance->ConfigThread);
        }
 }
 
-std::string InspIRCd::GetVersionString(bool operstring)
+std::string InspIRCd::GetVersionString(bool getFullVersion)
 {
-       char versiondata[MAXBUF];
-       if (operstring)
-       {
-               std::string sename = SE->GetName();
-               snprintf(versiondata,MAXBUF,"%s %s :%s [%s,%s,%s]",VERSION, Config->ServerName.c_str(), SYSTEM,REVISION, sename.c_str(), Config->sid.c_str());
-       }
-       else
-               snprintf(versiondata,MAXBUF,"%s %s :%s",BRANCH,Config->ServerName.c_str(),Config->CustomVersion.c_str());
-       return versiondata;
+       if (getFullVersion)
+               return INSPIRCD_VERSION ". " + Config->ServerName + " :[" + Config->sid + "] " + Config->CustomVersion;
+       return INSPIRCD_BRANCH ". " + Config->ServerName + " :" + Config->CustomVersion;
 }
 
-const char InspIRCd::LogHeader[] =
-       "Log started for " VERSION " (" REVISION ", " MODULE_INIT_STR ")"
-       " - compiled on " SYSTEM;
-
-void InspIRCd::BuildISupport()
+std::string UIDGenerator::GenerateSID(const std::string& servername, const std::string& serverdesc)
 {
-       // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it...
-       std::stringstream v;
-       v << "WALLCHOPS WALLVOICES MODES=" << Config->Limits.MaxModes << " CHANTYPES=# PREFIX=" << this->Modes->BuildPrefixes() << " MAP MAXCHANNELS=" << Config->MaxChans << " MAXBANS=60 VBANLIST NICKLEN=" << Config->Limits.NickMax;
-       v << " CASEMAPPING=rfc1459 STATUSMSG=" << Modes->BuildPrefixes(false) << " CHARSET=ascii TOPICLEN=" << Config->Limits.MaxTopic << " KICKLEN=" << Config->Limits.MaxKick << " MAXTARGETS=" << Config->MaxTargets;
-       v << " AWAYLEN=" << Config->Limits.MaxAway << " CHANMODES=" << this->Modes->GiveModeList(MASK_CHANNEL) << " FNC NETWORK=" << Config->Network << " MAXPARA=32 ELIST=MU" << " CHANNELLEN=" << Config->Limits.ChanMax;
-       Config->data005 = v.str();
-       FOREACH_MOD(I_On005Numeric,On005Numeric(Config->data005));
-       Config->Update005();
+       unsigned int sid = 0;
+
+       for (std::string::const_iterator i = servername.begin(); i != servername.end(); ++i)
+               sid = 5 * sid + *i;
+       for (std::string::const_iterator i = serverdesc.begin(); i != serverdesc.end(); ++i)
+               sid = 5 * sid + *i;
+
+       std::string sidstr = ConvToStr(sid % 1000);
+       sidstr.insert(0, 3 - sidstr.length(), '0');
+       return sidstr;
 }
 
-void InspIRCd::IncrementUID(int pos)
+void UIDGenerator::IncrementUID(unsigned int pos)
 {
        /*
         * Okay. The rules for generating a UID go like this...
@@ -103,85 +91,155 @@ void InspIRCd::IncrementUID(int pos)
         * A again, in an iterative fashion.. so..
         * AAA9 -> AABA, and so on. -- w00t
         */
-       if ((pos == 3) && (current_uid[3] == '9'))
+
+       // If we hit Z, wrap around to 0.
+       if (current_uid[pos] == 'Z')
+       {
+               current_uid[pos] = '0';
+       }
+       else if (current_uid[pos] == '9')
        {
-               // At pos 3, if we hit '9', we've run out of available UIDs, and need to reset to AAA..AAA.
-               for (int i = 3; i < UUID_LENGTH-1; i++)
+               /*
+                * Or, if we hit 9, wrap around to pos = 'A' and (pos - 1)++,
+                * e.g. A9 -> BA -> BB ..
+                */
+               current_uid[pos] = 'A';
+               if (pos == 3)
                {
-                       current_uid[i] = 'A';
+                       // At pos 3, if we hit '9', we've run out of available UIDs, and reset to AAA..AAA.
+                       return;
                }
+               this->IncrementUID(pos - 1);
        }
        else
        {
-               // If we hit Z, wrap around to 0.
-               if (current_uid[pos] == 'Z')
-               {
-                       current_uid[pos] = '0';
-               }
-               else if (current_uid[pos] == '9')
-               {
-                       /*
-                        * Or, if we hit 9, wrap around to pos = 'A' and (pos - 1)++,
-                        * e.g. A9 -> BA -> BB ..
-                        */
-                       current_uid[pos] = 'A';
-                       this->IncrementUID(pos - 1);
-               }
-               else
-               {
-                       // Anything else, nobody gives a shit. Just increment.
-                       current_uid[pos]++;
-               }
+               // Anything else, nobody gives a shit. Just increment.
+               current_uid[pos]++;
        }
 }
 
-/*
- * Retrieve the next valid UUID that is free for this server.
- */
-std::string InspIRCd::GetUID()
+void UIDGenerator::init(const std::string& sid)
 {
-       static bool inited = false;
-
        /*
-        * If we're setting up, copy SID into the first three digits, 9's to the rest, null term at the end
+        * Copy SID into the first three digits, 9's to the rest, null term at the end
         * Why 9? Well, we increment before we find, otherwise we have an unnecessary copy, and I want UID to start at AAA..AA
         * and not AA..AB. So by initialising to 99999, we force it to rollover to AAAAA on the first IncrementUID call.
         * Kind of silly, but I like how it looks.
         *              -- w
         */
-       if (!inited)
-       {
-               inited = true;
-               current_uid[0] = Config->sid[0];
-               current_uid[1] = Config->sid[1];
-               current_uid[2] = Config->sid[2];
 
-               for (int i = 3; i < (UUID_LENGTH - 1); i++)
-                       current_uid[i] = '9';
-
-               // Null terminator. Important.
-               current_uid[UUID_LENGTH - 1] = '\0';
-       }
+       current_uid.resize(UUID_LENGTH, '9');
+       current_uid[0] = sid[0];
+       current_uid[1] = sid[1];
+       current_uid[2] = sid[2];
+}
 
+/*
+ * Retrieve the next valid UUID that is free for this server.
+ */
+std::string UIDGenerator::GetUID()
+{
        while (1)
        {
                // Add one to the last UID
-               this->IncrementUID(UUID_LENGTH - 2);
+               this->IncrementUID(UUID_LENGTH - 1);
 
-               if (this->FindUUID(current_uid))
-               {
-                       /*
-                        * It's in use. We need to try the loop again.
-                        */
-                       continue;
-               }
+               if (!ServerInstance->FindUUID(current_uid))
+                       break;
 
-               return current_uid;
+               /*
+                * It's in use. We need to try the loop again.
+                */
        }
 
-       /* not reached. */
-       return "";
+       return current_uid;
 }
 
+void ISupportManager::AppendValue(std::string& buffer, const std::string& value)
+{
+       // If this token has no value then we have nothing to do.
+       if (value.empty())
+               return;
+
+       // This function implements value escaping according to the rules of the ISUPPORT draft:
+       // https://tools.ietf.org/html/draft-brocklesby-irc-isupport-03
+       buffer.push_back('=');
+       for (std::string::const_iterator iter = value.begin(); iter != value.end(); ++iter)
+       {
+               // The value must be escaped if:
+               //   (1) It is a banned character in an IRC <middle> parameter (NUL, LF, CR, SPACE).
+               //   (2) It has special meaning within an ISUPPORT token (EQUALS, BACKSLASH).
+               if (*iter == '\0' || *iter == '\n' || *iter == '\r' || *iter == ' ' || *iter == '=' || *iter == '\\')
+                       buffer.append(InspIRCd::Format("\\x%X", *iter));
+               else
+                       buffer.push_back(*iter);
+       }
+}
+
+void ISupportManager::Build()
+{
+       /**
+        * This is currently the neatest way we can build the initial ISUPPORT map. In
+        * the future we can use an initializer list here.
+        */
+       std::map<std::string, std::string> tokens;
+
+       tokens["AWAYLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxAway);
+       tokens["CASEMAPPING"] = ServerInstance->Config->CaseMapping;
+       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["HOSTLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxHost);
+       tokens["KICKLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxKick);
+       tokens["LINELEN"] = ConvToStr(ServerInstance->Config->Limits.MaxLine);
+       tokens["MAXTARGETS"] = ConvToStr(ServerInstance->Config->MaxTargets);
+       tokens["MODES"] = ConvToStr(ServerInstance->Config->Limits.MaxModes);
+       tokens["NETWORK"] = ServerInstance->Config->Network;
+       tokens["NICKLEN"] = ConvToStr(ServerInstance->Config->Limits.NickMax);
+       tokens["PREFIX"] = ServerInstance->Modes->BuildPrefixes();
+       tokens["STATUSMSG"] = ServerInstance->Modes->BuildPrefixes(false);
+       tokens["TOPICLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxTopic);
+       tokens["USERLEN"] = ConvToStr(ServerInstance->Config->Limits.IdentMax);
+       tokens["VBANLIST"];
+
+       // Modules can add new tokens and also edit or remove existing tokens
+       FOREACH_MOD(On005Numeric, (tokens));
+
+       // EXTBAN is a special case as we need to sort it and prepend a comma.
+       std::map<std::string, std::string>::iterator extban = tokens.find("EXTBAN");
+       if (extban != tokens.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
+       Numeric::Numeric numeric(RPL_ISUPPORT);
+       unsigned int token_count = 0;
+       cachedlines.clear();
 
+       for (std::map<std::string, std::string>::const_iterator it = tokens.begin(); it != tokens.end(); ++it)
+       {
+               numeric.push(it->first);
+               std::string& token = numeric.GetParams().back();
+               AppendValue(token, it->second);
+
+               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
+                       numeric.push("are supported by this server");
+                       cachedlines.push_back(numeric);
+                       numeric.GetParams().clear();
+               }
+       }
+}
+
+void ISupportManager::SendTo(LocalUser* user)
+{
+       for (std::vector<Numeric::Numeric>::const_iterator i = cachedlines.begin(); i != cachedlines.end(); ++i)
+               user->WriteNumeric(*i);
+}
index 4b9c9d86b29194162eff736b61bee1da848af5c9..e39fb84fb748f05a131ce75174280c24d86c04f4 100644 (file)
@@ -21,7 +21,6 @@
 
 
 #include "inspircd.h"
-#include <stdarg.h>
 
 void SnomaskManager::FlushSnotices()
 {
@@ -47,31 +46,21 @@ void SnomaskManager::WriteGlobalSno(char letter, const std::string& text)
 {
        WriteToSnoMask(letter, text);
        letter = toupper(letter);
-       ServerInstance->PI->SendSNONotice(std::string(1, letter), text);
+       ServerInstance->PI->SendSNONotice(letter, text);
 }
 
 void SnomaskManager::WriteToSnoMask(char letter, const char* text, ...)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteToSnoMask(letter, std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteToSnoMask(letter, textbuffer);
 }
 
 void SnomaskManager::WriteGlobalSno(char letter, const char* text, ...)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteGlobalSno(letter, std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteGlobalSno(letter, textbuffer);
 }
 
 SnomaskManager::SnomaskManager()
@@ -79,54 +68,41 @@ SnomaskManager::SnomaskManager()
        EnableSnomask('c',"CONNECT");                   /* Local connect notices */
        EnableSnomask('q',"QUIT");                      /* Local quit notices */
        EnableSnomask('k',"KILL");                      /* Kill notices */
-       EnableSnomask('l',"LINK");                      /* Linking notices */
        EnableSnomask('o',"OPER");                      /* Oper up/down notices */
-       EnableSnomask('a',"ANNOUNCEMENT");      /* formerly WriteOpers() - generic notices to all opers */
-       EnableSnomask('d',"DEBUG");                     /* Debug notices */
-       EnableSnomask('x',"XLINE");                     /* Xline notice (g/z/q/k/e) */
+       EnableSnomask('a',"ANNOUNCEMENT");              /* formerly WriteOpers() - generic notices to all opers */
+       EnableSnomask('x',"XLINE");                     /* X-line notices (G/Z/Q/K/E/R/SHUN/CBan) */
        EnableSnomask('t',"STATS");                     /* Local or remote stats request */
-       EnableSnomask('f',"FLOOD");                     /* Flooding notices */
 }
 
-/*************************************************************************************/
+bool SnomaskManager::IsSnomaskUsable(char ch) const
+{
+       return ((isalpha(ch)) && (!masks[tolower(ch) - 'a'].Description.empty()));
+}
 
-void Snomask::SendMessage(const std::string &message, char mysnomask)
+Snomask::Snomask()
+       : Count(0)
 {
-       if (ServerInstance->Config->NoSnoticeStack || message != LastMessage || mysnomask != LastLetter)
+}
+
+void Snomask::SendMessage(const std::string& message, char letter)
+{
+       if ((!ServerInstance->Config->NoSnoticeStack) && (message == LastMessage) && (letter == LastLetter))
        {
-               this->Flush();
-               LastMessage = message;
-               LastLetter = mysnomask;
-
-               std::string desc = Description;
-               if (desc.empty())
-                       desc = std::string("SNO-") + (char)tolower(mysnomask);
-               if (isupper(mysnomask))
-                       desc = "REMOTE" + desc;
-               ModResult MOD_RESULT;
-               ServerInstance->Logs->Log("snomask", DEFAULT, "%s: %s", desc.c_str(), message.c_str());
-
-               FIRST_MOD_RESULT(OnSendSnotice, MOD_RESULT, (mysnomask, desc, message));
-
-               LastBlocked = (MOD_RESULT == MOD_RES_DENY);
-
-               if (!LastBlocked)
-               {
-                       /* Only opers can receive snotices, so we iterate the oper list */
-                       std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin();
-
-                       while (i != ServerInstance->Users->all_opers.end())
-                       {
-                               User* a = *i;
-                               if (IS_LOCAL(a) && a->IsModeSet('s') && a->IsNoticeMaskSet(mysnomask) && !a->quitting)
-                               {
-                                       a->WriteServ("NOTICE %s :*** %s: %s", a->nick.c_str(), desc.c_str(), message.c_str());
-                               }
-
-                               i++;
-                       }
-               }
+               Count++;
+               return;
        }
+
+       this->Flush();
+
+       std::string desc = GetDescription(letter);
+       ModResult MOD_RESULT;
+       FIRST_MOD_RESULT(OnSendSnotice, MOD_RESULT, (letter, desc, message));
+       if (MOD_RESULT == MOD_RES_DENY)
+               return;
+
+       Snomask::Send(letter, desc, message);
+       LastMessage = message;
+       LastLetter = letter;
        Count++;
 }
 
@@ -134,36 +110,41 @@ void Snomask::Flush()
 {
        if (Count > 1)
        {
-               std::string desc = Description;
-               if (desc.empty())
-                       desc = std::string("SNO-") + (char)tolower(LastLetter);
-               if (isupper(LastLetter))
-                       desc = "REMOTE" + desc;
-               std::string mesg = "(last message repeated "+ConvToStr(Count)+" times)";
-
-               ServerInstance->Logs->Log("snomask", DEFAULT, "%s: %s", desc.c_str(), mesg.c_str());
-
-               FOREACH_MOD(I_OnSendSnotice, OnSendSnotice(LastLetter, desc, mesg));
-
-               if (!LastBlocked)
-               {
-                       /* Only opers can receive snotices, so we iterate the oper list */
-                       std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin();
-
-                       while (i != ServerInstance->Users->all_opers.end())
-                       {
-                               User* a = *i;
-                               if (IS_LOCAL(a) && a->IsModeSet('s') && a->IsNoticeMaskSet(LastLetter) && !a->quitting)
-                               {
-                                       a->WriteServ("NOTICE %s :*** %s: %s", a->nick.c_str(), desc.c_str(), mesg.c_str());
-                               }
-
-                               i++;
-                       }
-               }
+               std::string desc = GetDescription(LastLetter);
+               std::string msg = "(last message repeated " + ConvToStr(Count) + " times)";
 
+               FOREACH_MOD(OnSendSnotice, (LastLetter, desc, msg));
+               Snomask::Send(LastLetter, desc, msg);
        }
+
        LastMessage.clear();
-       LastBlocked = false;
        Count = 0;
 }
+
+void Snomask::Send(char letter, const std::string& desc, const std::string& msg)
+{
+       ServerInstance->Logs->Log(desc, LOG_DEFAULT, msg);
+       const std::string finalmsg = InspIRCd::Format("*** %s: %s", desc.c_str(), msg.c_str());
+
+       /* Only opers can receive snotices, so we iterate the oper list */
+       const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+       for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
+       {
+               User* user = *i;
+               // IsNoticeMaskSet() returns false for opers who aren't +s, no need to check for it seperately
+               if (IS_LOCAL(user) && user->IsNoticeMaskSet(letter))
+                       user->WriteNotice(finalmsg);
+       }
+}
+
+std::string Snomask::GetDescription(char letter) const
+{
+       std::string ret;
+       if (isupper(letter))
+               ret = "REMOTE";
+       if (!Description.empty())
+               ret += Description;
+       else
+               ret += std::string("SNO-") + (char)tolower(letter);
+       return ret;
+}
index e73d01af9c37ed37fbdd1909b95dd83dbba0e5af..abccd04084c3d58a0908898c27805bef9f337296 100644 (file)
 
 
 #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)
+bool InspIRCd::BindPort(ConfigTag* tag, const irc::sockets::sockaddrs& sa, std::vector<ListenSocket*>& old_ports)
 {
-       sockaddrs servaddr;
-       int ret;
-
-       if ((*addr == '*' || *addr == '\0') && port == -1)
+       for (std::vector<ListenSocket*>::iterator n = old_ports.begin(); n != old_ports.end(); ++n)
        {
-               /* 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;
+               if ((**n).bind_sa == sa)
+               {
+                       // Replace tag, we know addr and port match, but other info (type, ssl) may not.
+                       ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Replacing listener on %s from old tag at %s with new tag from %s",
+                               sa.str().c_str(), (*n)->bind_tag->getTagLocation().c_str(), tag->getTagLocation().c_str());
+                       (*n)->bind_tag = tag;
+                       (*n)->ResetIOHookProvider();
 
-       ret = SE->Bind(sockfd, servaddr);
+                       old_ports.erase(n);
+                       return true;
+               }
+       }
 
-       if (ret < 0)
+       ListenSocket* ll = new ListenSocket(tag, sa);
+       if (ll->GetFd() < 0)
        {
+               ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to listen on %s from tag at %s: %s",
+                       sa.str().c_str(), tag->getTagLocation().c_str(), strerror(errno));
+               delete ll;
                return false;
        }
-       else
-       {
-               if (dolisten)
-               {
-                       if (SE->Listen(sockfd, Config->MaxConn) == -1)
-                       {
-                               this->Logs->Log("SOCKET",DEFAULT,"ERROR in listen(): %s",strerror(errno));
-                               return false;
-                       }
-                       else
-                       {
-                               this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port);
-                               SE->NonBlocking(sockfd);
-                               return true;
-                       }
-               }
-               else
-               {
-                       this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port);
-                       return true;
-               }
-       }
+
+       ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Added a listener on %s from tag at %s", sa.str().c_str(), tag->getTagLocation().c_str());
+       ports.push_back(ll);
+       return true;
 }
 
-int InspIRCd::BindPorts(FailedPortList &failed_ports)
+int InspIRCd::BindPorts(FailedPortListfailed_ports)
 {
        int bound = 0;
        std::vector<ListenSocket*> old_ports(ports.begin(), ports.end());
 
        ConfigTagList tags = ServerInstance->Config->ConfTags("bind");
-       for(ConfigIter i = tags.first; i != tags.second; ++i)
+       for (ConfigIter i = tags.first; i != tags.second; ++i)
        {
                ConfigTag* tag = i->second;
-               std::string porttag = tag->getString("port");
-               std::string Addr = tag->getString("address");
 
-               if (strncasecmp(Addr.c_str(), "::ffff:", 7) == 0)
-                       this->Logs->Log("SOCKET",DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
+               // Are we creating a TCP/IP listener?
+               const std::string address = tag->getString("address");
+               const std::string portlist = tag->getString("port");
+               if (!address.empty() || !portlist.empty())
+               {
+                       // InspIRCd supports IPv4 and IPv6 natively; no 4in6 required.
+                       if (strncasecmp(address.c_str(), "::ffff:", 7) == 0)
+                               this->Logs->Log("SOCKET", LOG_DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
+
+                       // A TCP listener with no ports is not very useful.
+                       if (portlist.empty())
+                               this->Logs->Log("SOCKET", LOG_DEFAULT, "TCP listener on %s at %s has no ports specified!",
+                                       address.empty() ? "*" : address.c_str(), tag->getTagLocation().c_str());
 
-               irc::portparser portrange(porttag, false);
-               int portno = -1;
-               while (0 != (portno = portrange.GetToken()))
+                       irc::portparser portrange(portlist, false);
+                       for (int port; (port = portrange.GetToken()); )
+                       {
+                               irc::sockets::sockaddrs bindspec;
+                               if (!irc::sockets::aptosa(address, port, bindspec))
+                                       continue;
+
+                               if (!BindPort(tag, bindspec, old_ports))
+                                       failed_ports.push_back(std::make_pair(bindspec, errno));
+                               else
+                                       bound++;
+                       }
+                       continue;
+               }
+
+#ifndef _WIN32
+               // Are we creating a UNIX listener?
+               const std::string path = tag->getString("path");
+               if (!path.empty())
                {
+                       // UNIX socket paths are length limited to less than PATH_MAX.
                        irc::sockets::sockaddrs bindspec;
-                       if (!irc::sockets::aptosa(Addr, portno, bindspec))
+                       if (path.length() > std::min(ServerInstance->Config->Limits.MaxHost, sizeof(bindspec.un.sun_path) - 1))
+                       {
+                               this->Logs->Log("SOCKET", LOG_DEFAULT, "UNIX listener on %s at %s specified a path that is too long!",
+                                       path.c_str(), tag->getTagLocation().c_str());
                                continue;
-                       std::string bind_readable = bindspec.str();
+                       }
 
-                       bool skip = false;
-                       for (std::vector<ListenSocket*>::iterator n = old_ports.begin(); n != old_ports.end(); ++n)
+                       // Check for characters which are problematic in the IRC message format.
+                       if (path.find_first_of("\n\r\t!@: ") != std::string::npos)
                        {
-                               if ((**n).bind_desc == bind_readable)
-                               {
-                                       (*n)->bind_tag = tag; // Replace tag, we know addr and port match, but other info (type, ssl) may not
-                                       skip = true;
-                                       old_ports.erase(n);
-                                       break;
-                               }
+                               this->Logs->Log("SOCKET", LOG_DEFAULT, "UNIX listener on %s at %s specified a path containing invalid characters!",
+                                       path.c_str(), tag->getTagLocation().c_str());
+                               continue;
                        }
-                       if (!skip)
-                       {
-                               ListenSocket* ll = new ListenSocket(tag, bindspec);
 
-                               if (ll->GetFd() > -1)
-                               {
-                                       bound++;
-                                       ports.push_back(ll);
-                               }
-                               else
-                               {
-                                       failed_ports.push_back(std::make_pair(bind_readable, strerror(errno)));
-                                       delete ll;
-                               }
-                       }
+                       irc::sockets::untosa(path, bindspec);
+                       if (!BindPort(tag, bindspec, old_ports))
+                               failed_ports.push_back(std::make_pair(bindspec, errno));
+                       else
+                               bound++;
                }
+#endif
        }
 
        std::vector<ListenSocket*>::iterator n = ports.begin();
@@ -136,12 +131,12 @@ int InspIRCd::BindPorts(FailedPortList &failed_ports)
                        n++;
                if (n == ports.end())
                {
-                       this->Logs->Log("SOCKET",DEFAULT,"Port bindings slipped out of vector, aborting close!");
+                       this->Logs->Log("SOCKET", LOG_DEFAULT, "Port bindings slipped out of vector, aborting close!");
                        break;
                }
 
-               this->Logs->Log("SOCKET",DEFAULT, "Port binding %s was removed from the config file, closing.",
-                       (**n).bind_desc.c_str());
+               this->Logs->Log("SOCKET", LOG_DEFAULT, "Port binding %s was removed from the config file, closing.",
+                       (**n).bind_sa.str().c_str());
                delete *n;
 
                // this keeps the iterator valid, pointing to the next element
@@ -183,111 +178,168 @@ bool irc::sockets::aptosa(const std::string& addr, int port, irc::sockets::socka
        return false;
 }
 
-int irc::sockets::sockaddrs::port() const
+bool irc::sockets::untosa(const std::string& path, irc::sockets::sockaddrs& sa)
 {
-       if (sa.sa_family == AF_INET)
-               return ntohs(in4.sin_port);
-       if (sa.sa_family == AF_INET6)
-               return ntohs(in6.sin6_port);
-       return -1;
+       memset(&sa, 0, sizeof(sa));
+       if (path.length() >= sizeof(sa.un.sun_path))
+               return false;
+
+       sa.un.sun_family = AF_UNIX;
+       memcpy(&sa.un.sun_path, path.c_str(), path.length() + 1);
+       return true;
 }
 
-std::string irc::sockets::sockaddrs::addr() const
+int irc::sockets::sockaddrs::family() const
 {
-       char addrv[INET6_ADDRSTRLEN+1];
-       if (sa.sa_family == AF_INET)
-       {
-               if (!inet_ntop(AF_INET, &in4.sin_addr, addrv, sizeof(addrv)))
-                       return "";
-               return addrv;
-       }
-       else if (sa.sa_family == AF_INET6)
+       return sa.sa_family;
+}
+
+int irc::sockets::sockaddrs::port() const
+{
+       switch (family())
        {
-               if (!inet_ntop(AF_INET6, &in6.sin6_addr, addrv, sizeof(addrv)))
-                       return "";
-               return addrv;
+               case AF_INET:
+                       return ntohs(in4.sin_port);
+
+               case AF_INET6:
+                       return ntohs(in6.sin6_port);
+
+               case AF_UNIX:
+                       return 0;
        }
-       return "";
+
+       // If we have reached this point then we have encountered a bug.
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::port(): socket type %d is unknown!", family());
+       return 0;
 }
 
-bool irc::sockets::satoap(const irc::sockets::sockaddrs& sa, std::string& addr, int &port)
+std::string irc::sockets::sockaddrs::addr() const
 {
-       port = sa.port();
-       addr = sa.addr();
-       return !addr.empty();
+       switch (family())
+       {
+               case AF_INET:
+                       char ip4addr[INET_ADDRSTRLEN];
+                       if (!inet_ntop(AF_INET, (void*)&in4.sin_addr, ip4addr, sizeof(ip4addr)))
+                               return "0.0.0.0";
+                       return ip4addr;
+
+               case AF_INET6:
+                       char ip6addr[INET6_ADDRSTRLEN];
+                       if (!inet_ntop(AF_INET6, (void*)&in6.sin6_addr, ip6addr, sizeof(ip6addr)))
+                               return "0:0:0:0:0:0:0:0";
+                       return ip6addr;
+
+               case AF_UNIX:
+                       return un.sun_path;
+       }
+
+       // If we have reached this point then we have encountered a bug.
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::addr(): socket type %d is unknown!", family());
+       return "<unknown>";
 }
 
 std::string irc::sockets::sockaddrs::str() const
 {
-       char buffer[MAXBUF];
-       if (sa.sa_family == AF_INET)
+       switch (family())
        {
-               const uint8_t* bits = reinterpret_cast<const uint8_t*>(&in4.sin_addr);
-               sprintf(buffer, "%d.%d.%d.%d:%u", bits[0], bits[1], bits[2], bits[3], ntohs(in4.sin_port));
+               case AF_INET:
+                       char ip4addr[INET_ADDRSTRLEN];
+                       if (!inet_ntop(AF_INET, (void*)&in4.sin_addr, ip4addr, sizeof(ip4addr)))
+                               strcpy(ip4addr, "0.0.0.0");
+                       return InspIRCd::Format("%s:%u", ip4addr, ntohs(in4.sin_port));
+
+               case AF_INET6:
+                       char ip6addr[INET6_ADDRSTRLEN];
+                       if (!inet_ntop(AF_INET6, (void*)&in6.sin6_addr, ip6addr, sizeof(ip6addr)))
+                               strcpy(ip6addr, "0:0:0:0:0:0:0:0");
+                       return InspIRCd::Format("[%s]:%u", ip6addr, ntohs(in6.sin6_port));
+
+               case AF_UNIX:
+                       return un.sun_path;
        }
-       else if (sa.sa_family == AF_INET6)
-       {
-               buffer[0] = '[';
-               if (!inet_ntop(AF_INET6, &in6.sin6_addr, buffer+1, MAXBUF - 10))
-                       return "<unknown>"; // should never happen, buffer is large enough
-               int len = strlen(buffer);
-               // no need for snprintf, buffer has at least 9 chars left, max short len = 5
-               sprintf(buffer + len, "]:%u", ntohs(in6.sin6_port));
-       }
-       else
-               return "<unknown>";
-       return std::string(buffer);
+
+       // If we have reached this point then we have encountered a bug.
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::str(): socket type %d is unknown!", family());
+       return "<unknown>";
 }
 
-int irc::sockets::sockaddrs::sa_size() const
+socklen_t irc::sockets::sockaddrs::sa_size() const
 {
-       if (sa.sa_family == AF_INET)
-               return sizeof(in4);
-       if (sa.sa_family == AF_INET6)
-               return sizeof(in6);
+       switch (family())
+       {
+               case AF_INET:
+                       return sizeof(in4);
+
+               case AF_INET6:
+                       return sizeof(in6);
+
+               case AF_UNIX:
+                       return sizeof(un);
+       }
+
+       // If we have reached this point then we have encountered a bug.
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::sa_size(): socket type %d is unknown!", family());
        return 0;
 }
 
 bool irc::sockets::sockaddrs::operator==(const irc::sockets::sockaddrs& other) const
 {
-       if (sa.sa_family != other.sa.sa_family)
+       if (family() != other.family())
                return false;
-       if (sa.sa_family == AF_INET)
-               return (in4.sin_port == other.in4.sin_port) && (in4.sin_addr.s_addr == other.in4.sin_addr.s_addr);
-       if (sa.sa_family == AF_INET6)
-               return (in6.sin6_port == other.in6.sin6_port) && !memcmp(in6.sin6_addr.s6_addr, other.in6.sin6_addr.s6_addr, 16);
+
+       switch (family())
+       {
+               case AF_INET:
+                       return (in4.sin_port == other.in4.sin_port) && (in4.sin_addr.s_addr == other.in4.sin_addr.s_addr);
+
+               case AF_INET6:
+                       return (in6.sin6_port == other.in6.sin6_port) && !memcmp(in6.sin6_addr.s6_addr, other.in6.sin6_addr.s6_addr, 16);
+
+               case AF_UNIX:
+                       return !strcmp(un.sun_path, other.un.sun_path);
+       }
+
+       // If we have reached this point then we have encountered a bug.
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::operator==(): socket type %d is unknown!", family());
        return !memcmp(this, &other, sizeof(*this));
 }
 
-static void sa2cidr(irc::sockets::cidr_mask& cidr, const irc::sockets::sockaddrs& sa, int range)
+static void sa2cidr(irc::sockets::cidr_mask& cidr, const irc::sockets::sockaddrs& sa, unsigned char range)
 {
        const unsigned char* base;
        unsigned char target_byte;
-       cidr.type = sa.sa.sa_family;
 
        memset(cidr.bits, 0, sizeof(cidr.bits));
 
-       if (cidr.type == AF_INET)
+       cidr.type = sa.family();
+       switch (cidr.type)
        {
-               target_byte = sizeof(sa.in4.sin_addr);
-               base = (unsigned char*)&sa.in4.sin_addr;
-               if (range > 32)
-                       range = 32;
-       }
-       else if (cidr.type == AF_INET6)
-       {
-               target_byte = sizeof(sa.in6.sin6_addr);
-               base = (unsigned char*)&sa.in6.sin6_addr;
-               if (range > 128)
-                       range = 128;
-       }
-       else
-       {
-               cidr.length = 0;
-               return;
+               case AF_UNIX:
+                       // XXX: UNIX sockets don't support CIDR. This fix is non-ideal but I can't
+                       // really think of another way to handle it.
+                       cidr.length = 0;
+                       return;
+
+               case AF_INET:
+                       cidr.length = range > 32 ? 32 : range;
+                       target_byte = sizeof(sa.in4.sin_addr);
+                       base = (unsigned char*)&sa.in4.sin_addr;
+                       break;
+
+               case AF_INET6:
+                       cidr.length = range > 128 ? 128 : range;
+                       target_byte = sizeof(sa.in6.sin6_addr);
+                       base = (unsigned char*)&sa.in6.sin6_addr;
+                       break;
+
+               default:
+                       // If we have reached this point then we have encountered a bug.
+                       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: sa2cidr(): socket type %d is unknown!", cidr.type);
+                       cidr.length = 0;
+                       return;
        }
-       cidr.length = range;
-       unsigned int border = range / 8;
+
+       unsigned int border = cidr.length / 8;
        unsigned int bitmask = (0xFF00 >> (range & 7)) & 0xFF;
        for(unsigned int i=0; i < target_byte; i++)
        {
@@ -300,7 +352,7 @@ static void sa2cidr(irc::sockets::cidr_mask& cidr, const irc::sockets::sockaddrs
        }
 }
 
-irc::sockets::cidr_mask::cidr_mask(const irc::sockets::sockaddrs& sa, int range)
+irc::sockets::cidr_mask::cidr_mask(const irc::sockets::sockaddrs& sa, unsigned char range)
 {
        sa2cidr(*this, sa, range);
 }
@@ -317,7 +369,7 @@ irc::sockets::cidr_mask::cidr_mask(const std::string& mask)
        }
        else
        {
-               int range = ConvToInt(mask.substr(bits_chars + 1));
+               unsigned char range = ConvToNum<unsigned char>(mask.substr(bits_chars + 1));
                irc::sockets::aptosa(mask.substr(0, bits_chars), 0, sa);
                sa2cidr(*this, sa, range);
        }
@@ -327,20 +379,30 @@ std::string irc::sockets::cidr_mask::str() const
 {
        irc::sockets::sockaddrs sa;
        sa.sa.sa_family = type;
+
        unsigned char* base;
-       int len;
-       if (type == AF_INET)
-       {
-               base = (unsigned char*)&sa.in4.sin_addr;
-               len = 4;
-       }
-       else if (type == AF_INET6)
+       size_t len;
+       switch (type)
        {
-               base = (unsigned char*)&sa.in6.sin6_addr;
-               len = 16;
+               case AF_INET:
+                       base = (unsigned char*)&sa.in4.sin_addr;
+                       len = 4;
+                       break;
+
+               case AF_INET6:
+                       base = (unsigned char*)&sa.in6.sin6_addr;
+                       len = 16;
+                       break;
+
+               case AF_UNIX:
+                       return sa.un.sun_path;
+
+               default:
+                       // If we have reached this point then we have encountered a bug.
+                       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::cidr_mask::str(): socket type %d is unknown!", type);
+                       return "<unknown>";
        }
-       else
-               return "";
+
        memcpy(base, bits, len);
        return sa.addr() + "/" + ConvToStr((int)length);
 }
@@ -362,9 +424,8 @@ bool irc::sockets::cidr_mask::operator<(const cidr_mask& other) const
 
 bool irc::sockets::cidr_mask::match(const irc::sockets::sockaddrs& addr) const
 {
-       if (addr.sa.sa_family != type)
+       if (addr.family() != type)
                return false;
        irc::sockets::cidr_mask tmp(addr, length);
        return tmp == *this;
 }
-
index 4a9a2ef10539e1b0c50d5c77fbc02e6aa6b49227..df6ff5a0206d0471b41d962ffe115a9add91ab1f 100644 (file)
  */
 
 
+#include "exitcodes.h"
 #include "inspircd.h"
 
+#include <iostream>
+
+/** Reference table, contains all current handlers
+ **/
+std::vector<EventHandler*> SocketEngine::ref;
+
+/** Current number of descriptors in the engine
+ */
+size_t SocketEngine::CurrentSetSize = 0;
+
+/** List of handlers that want a trial read/write
+ */
+std::set<int> SocketEngine::trials;
+
+size_t SocketEngine::MaxSetSize = 0;
+
+/** Socket engine statistics: count of various events, bandwidth usage
+ */
+SocketEngine::Statistics SocketEngine::stats;
+
 EventHandler::EventHandler()
 {
        fd = -1;
@@ -34,20 +55,37 @@ void EventHandler::SetFd(int FD)
        this->fd = FD;
 }
 
-SocketEngine::SocketEngine()
+void EventHandler::OnEventHandlerWrite()
 {
-       TotalEvents = WriteEvents = ReadEvents = ErrorEvents = 0;
-       lastempty = ServerInstance->Time();
-       indata = outdata = 0;
 }
 
-SocketEngine::~SocketEngine()
+void EventHandler::OnEventHandlerError(int errornum)
 {
 }
 
-void SocketEngine::SetEventMask(EventHandler* eh, int mask)
+void SocketEngine::InitError()
 {
-       eh->event_mask = mask;
+       std::cerr << con_red << "FATAL ERROR!" << con_reset << " Socket engine initialization failed. " << strerror(errno) << '.' << std::endl;
+       exit(EXIT_STATUS_SOCKETENGINE);
+}
+
+void SocketEngine::LookupMaxFds()
+{
+#if defined _WIN32
+       MaxSetSize = FD_SETSIZE;
+#else
+       struct rlimit limits;
+       if (!getrlimit(RLIMIT_NOFILE, &limits))
+               MaxSetSize = limits.rlim_cur;
+
+#if defined __APPLE__
+       limits.rlim_cur = limits.rlim_max == RLIM_INFINITY ? OPEN_MAX : limits.rlim_max;
+#else
+       limits.rlim_cur = limits.rlim_max;
+#endif
+       if (!setrlimit(RLIMIT_NOFILE, &limits))
+               MaxSetSize = limits.rlim_cur;
+#endif
 }
 
 void SocketEngine::ChangeEventMask(EventHandler* eh, int change)
@@ -60,7 +98,7 @@ void SocketEngine::ChangeEventMask(EventHandler* eh, int change)
                new_m &= ~FD_WANT_READ_MASK;
        if (change & FD_WANT_WRITE_MASK)
                new_m &= ~FD_WANT_WRITE_MASK;
-       
+
        // if adding a trial read/write, insert it into the set
        if (change & FD_TRIAL_NOTE_MASK && !(old_m & FD_TRIAL_NOTE_MASK))
                trials.insert(eh->GetFd());
@@ -88,23 +126,44 @@ 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();
        }
 }
 
-bool SocketEngine::HasFd(int fd)
+bool SocketEngine::AddFdRef(EventHandler* eh)
 {
-       if ((fd < 0) || (fd > GetMaxFds()))
+       int fd = eh->GetFd();
+       if (HasFd(fd))
                return false;
-       return (ref[fd] != NULL);
+
+       while (static_cast<unsigned int>(fd) >= ref.size())
+               ref.resize(ref.empty() ? 1 : (ref.size() * 2));
+       ref[fd] = eh;
+       CurrentSetSize++;
+       return true;
+}
+
+void SocketEngine::DelFdRef(EventHandler *eh)
+{
+       int fd = eh->GetFd();
+       if (GetRef(fd) == eh)
+       {
+               ref[fd] = NULL;
+               CurrentSetSize--;
+       }
+}
+
+bool SocketEngine::HasFd(int fd)
+{
+       return GetRef(fd) != NULL;
 }
 
 EventHandler* SocketEngine::GetRef(int fd)
 {
-       if ((fd < 0) || (fd > GetMaxFds()))
-               return 0;
+       if (fd < 0 || static_cast<unsigned int>(fd) >= ref.size())
+               return NULL;
        return ref[fd];
 }
 
@@ -112,7 +171,7 @@ bool SocketEngine::BoundsCheckFd(EventHandler* eh)
 {
        if (!eh)
                return false;
-       if ((eh->GetFd() < 0) || (eh->GetFd() > GetMaxFds()))
+       if (eh->GetFd() < 0)
                return false;
        return true;
 }
@@ -123,13 +182,12 @@ int SocketEngine::Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen)
        return accept(fd->GetFd(), addr, addrlen);
 }
 
-int SocketEngine::Close(EventHandler* fd)
+int SocketEngine::Close(EventHandler* eh)
 {
-#ifdef _WIN32
-       return closesocket(fd->GetFd());
-#else
-       return close(fd->GetFd());
-#endif
+       DelFd(eh);
+       int ret = Close(eh->GetFd());
+       eh->SetFd(-1);
+       return ret;
 }
 
 int SocketEngine::Close(int fd)
@@ -172,38 +230,60 @@ 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)
-               this->UpdateStats(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)
-               this->UpdateStats(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)
-               this->UpdateStats(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 SocketEngine::SendTo(EventHandler* fd, const void* buf, size_t len, int flags, const irc::sockets::sockaddrs& address)
 {
-       int nbSent = sendto(fd->GetFd(), (const char*)buf, len, flags, to, tolen);
-       if (nbSent > 0)
-               this->UpdateStats(0, nbSent);
+       int nbSent = sendto(fd->GetFd(), (const char*)buf, len, flags, &address.sa, address.sa_size());
+       stats.UpdateWriteCounters(nbSent);
        return nbSent;
 }
 
-int SocketEngine::Connect(EventHandler* fd, const sockaddr *serv_addr, socklen_t addrlen)
+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 irc::sockets::sockaddrs& address)
 {
-       int ret = connect(fd->GetFd(), serv_addr, addrlen);
+       int ret = connect(fd->GetFd(), &address.sa, address.sa_size());
 #ifdef _WIN32
        if ((ret == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK))
                errno = EINPROGRESS;
@@ -231,24 +311,42 @@ int SocketEngine::Shutdown(int fd, int how)
        return shutdown(fd, how);
 }
 
-void SocketEngine::RecoverFromFork()
+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();
+
+       WriteEvents++;
+       if (len_out > 0)
+               outdata += len_out;
+       else if (len_out < 0)
+               ErrorEvents++;
 }
 
-void SocketEngine::UpdateStats(size_t len_in, size_t len_out)
+void SocketEngine::Statistics::CheckFlush() const
 {
-       if (lastempty != ServerInstance->Time())
+       // Reset the in/out byte counters if it has been more than a second
+       time_t now = ServerInstance->Time();
+       if (lastempty != now)
        {
-               lastempty = ServerInstance->Time();
+               lastempty = now;
                indata = outdata = 0;
        }
-       indata += len_in;
-       outdata += len_out;
 }
 
-void SocketEngine::GetStats(float &kbitpersec_in, float &kbitpersec_out, float &kbitpersec_total)
+void SocketEngine::Statistics::GetBandwidth(float& kbitpersec_in, float& kbitpersec_out, float& kbitpersec_total) const
 {
-       UpdateStats(0, 0); /* Forces emptying of the values if its been more than a second */
+       CheckFlush();
        float in_kbit = indata * 8;
        float out_kbit = outdata * 8;
        kbitpersec_total = ((in_kbit + out_kbit) / 1024);
index d5f01734782e568d29fbf69457ed7224d1fb25bd..60b365ee139027a8cd75482157c7e7a8db9536f4 100644 (file)
  */
 
 
-#include <vector>
-#include <string>
-#include <map>
 #include "inspircd.h"
-#include "exitcodes.h"
-#include "socketengine.h"
+
 #include <sys/epoll.h>
 #include <sys/resource.h>
-#include <iostream>
-#define EP_DELAY 5
 
 /** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
  */
-class EPollEngine : public SocketEngine
+namespace
 {
-private:
-       /** These are used by epoll() to hold socket events
-        */
-       struct epoll_event* events;
        int EngineHandle;
-public:
-       /** Create a new EPollEngine
-        */
-       EPollEngine();
-       /** Delete an EPollEngine
+
+       /** These are used by epoll() to hold socket events
         */
-       virtual ~EPollEngine();
-       virtual bool AddFd(EventHandler* eh, int event_mask);
-       virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
-       virtual void DelFd(EventHandler* eh);
-       virtual int DispatchEvents();
-       virtual std::string GetName();
-};
+       std::vector<struct epoll_event> events(1);
+}
 
-EPollEngine::EPollEngine()
+void SocketEngine::Init()
 {
-       CurrentSetSize = 0;
-
-       struct rlimit limit;
-       if (!getrlimit(RLIMIT_NOFILE, &limit))
-       {
-               MAX_DESCRIPTORS = limit.rlim_cur;
-       }
-       else
-       {
-               ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
-               std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
-               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
-       }
-
-       // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
-       EngineHandle = epoll_create(GetMaxFds() / 4);
+       LookupMaxFds();
 
+       // 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.
+       EngineHandle = epoll_create(128);
        if (EngineHandle == -1)
-       {
-               ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
-               ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
-               std::cout << "ERROR: Could not initialize epoll socket engine: " << strerror(errno) << std::endl;
-               std::cout << "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now." << std::endl;
-               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
-       }
-
-       ref = new EventHandler* [GetMaxFds()];
-       events = new struct epoll_event[GetMaxFds()];
+               InitError();
+}
 
-       memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+void SocketEngine::RecoverFromFork()
+{
 }
 
-EPollEngine::~EPollEngine()
+void SocketEngine::Deinit()
 {
-       this->Close(EngineHandle);
-       delete[] ref;
-       delete[] events;
+       Close(EngineHandle);
 }
 
 static unsigned mask_to_epoll(int event_mask)
@@ -116,41 +77,41 @@ static unsigned mask_to_epoll(int event_mask)
        return rv;
 }
 
-bool EPollEngine::AddFd(EventHandler* eh, int event_mask)
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
 {
        int fd = eh->GetFd();
-       if ((fd < 0) || (fd > GetMaxFds() - 1))
+       if (fd < 0)
        {
-               ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd);
                return false;
        }
 
-       if (ref[fd])
+       if (!SocketEngine::AddFdRef(eh))
        {
-               ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
                return false;
        }
 
        struct epoll_event ev;
-       memset(&ev,0,sizeof(ev));
+       memset(&ev, 0, sizeof(ev));
        ev.events = mask_to_epoll(event_mask);
-       ev.data.fd = fd;
+       ev.data.ptr = static_cast<void*>(eh);
        int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
        if (i < 0)
        {
-               ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno));
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error adding fd: %d to socketengine: %s", fd, strerror(errno));
                return false;
        }
 
-       ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
+
+       eh->SetEventMask(event_mask);
+       ResizeDouble(events);
 
-       ref[fd] = eh;
-       SocketEngine::SetEventMask(eh, event_mask);
-       CurrentSetSize++;
        return true;
 }
 
-void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
 {
        unsigned old_events = mask_to_epoll(old_mask);
        unsigned new_events = mask_to_epoll(new_mask);
@@ -158,75 +119,78 @@ void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
        {
                // ok, we actually have something to tell the kernel about
                struct epoll_event ev;
-               memset(&ev,0,sizeof(ev));
+               memset(&ev, 0, sizeof(ev));
                ev.events = new_events;
-               ev.data.fd = eh->GetFd();
+               ev.data.ptr = static_cast<void*>(eh);
                epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
        }
 }
 
-void EPollEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
 {
        int fd = eh->GetFd();
-       if ((fd < 0) || (fd > GetMaxFds() - 1))
+       if (fd < 0)
        {
-               ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd);
                return;
        }
 
+       // Do not initialize epoll_event because for EPOLL_CTL_DEL operations the event is ignored and can be NULL.
+       // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required a non-NULL pointer in event,
+       // even though this argument is ignored. Since Linux 2.6.9, event can be specified as NULL when using EPOLL_CTL_DEL.
        struct epoll_event ev;
-       memset(&ev,0,sizeof(ev));
-       ev.data.fd = fd;
        int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
 
        if (i < 0)
        {
-               ServerInstance->Logs->Log("SOCKET",DEBUG,"epoll_ctl can't remove socket: %s", strerror(errno));
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "epoll_ctl can't remove socket: %s", strerror(errno));
        }
 
-       ref[fd] = NULL;
+       SocketEngine::DelFdRef(eh);
 
-       ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
-       CurrentSetSize--;
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
 }
 
-int EPollEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
 {
-       socklen_t codesize = sizeof(int);
-       int errcode;
-       int i = epoll_wait(EngineHandle, events, GetMaxFds() - 1, 1000);
+       int i = epoll_wait(EngineHandle, &events[0], events.size(), 1000);
        ServerInstance->UpdateTime();
 
-       TotalEvents += i;
+       stats.TotalEvents += i;
 
        for (int j = 0; j < i; j++)
        {
-               EventHandler* eh = ref[events[j].data.fd];
-               if (!eh)
-               {
-                       ServerInstance->Logs->Log("SOCKET",DEBUG,"Got event on unknown fd: %d", events[j].data.fd);
-                       epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]);
+               // Copy these in case the vector gets resized and ev invalidated
+               const epoll_event ev = events[j];
+
+               EventHandler* const eh = static_cast<EventHandler*>(ev.data.ptr);
+               const int fd = eh->GetFd();
+               if (fd < 0)
                        continue;
-               }
-               if (events[j].events & EPOLLHUP)
+
+               if (ev.events & EPOLLHUP)
                {
-                       ErrorEvents++;
-                       eh->HandleEvent(EVENT_ERROR, 0);
+                       stats.ErrorEvents++;
+                       eh->OnEventHandlerError(0);
                        continue;
                }
-               if (events[j].events & EPOLLERR)
+
+               if (ev.events & EPOLLERR)
                {
-                       ErrorEvents++;
+                       stats.ErrorEvents++;
                        /* Get error number */
-                       if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
+                       socklen_t codesize = sizeof(int);
+                       int errcode;
+                       if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
                                errcode = errno;
-                       eh->HandleEvent(EVENT_ERROR, errcode);
+                       eh->OnEventHandlerError(errcode);
                        continue;
                }
+
                int mask = eh->GetEventMask();
-               if (events[j].events & EPOLLIN)
+               if (ev.events & EPOLLIN)
                        mask &= ~FD_READ_WILL_BLOCK;
-               if (events[j].events & EPOLLOUT)
+               if (ev.events & EPOLLOUT)
                {
                        mask &= ~FD_WRITE_WILL_BLOCK;
                        if (mask & FD_WANT_SINGLE_WRITE)
@@ -236,31 +200,19 @@ int EPollEngine::DispatchEvents()
                                mask = nm;
                        }
                }
-               SetEventMask(eh, mask);
-               if (events[j].events & EPOLLIN)
+               eh->SetEventMask(mask);
+               if (ev.events & EPOLLIN)
                {
-                       ReadEvents++;
-                       eh->HandleEvent(EVENT_READ);
-                       if (eh != ref[events[j].data.fd])
+                       eh->OnEventHandlerRead();
+                       if (eh != GetRef(fd))
                                // whoa! we got deleted, better not give out the write event
                                continue;
                }
-               if (events[j].events & EPOLLOUT)
+               if (ev.events & EPOLLOUT)
                {
-                       WriteEvents++;
-                       eh->HandleEvent(EVENT_WRITE);
+                       eh->OnEventHandlerWrite();
                }
        }
 
        return i;
 }
-
-std::string EPollEngine::GetName()
-{
-       return "epoll";
-}
-
-SocketEngine* CreateSocketEngine()
-{
-       return new EPollEngine;
-}
index 8694a0bdd84d4cc2e5249aba5fa4d3f3ed6f5732..b23cfbd9dcf2b6af86a415bac1c9132d7c4ac92a 100644 (file)
 
 
 #include "inspircd.h"
-#include "exitcodes.h"
+
 #include <sys/types.h>
 #include <sys/event.h>
 #include <sys/time.h>
-#include "socketengine.h"
-#include <iostream>
+#include <sys/sysctl.h>
 
 /** A specialisation of the SocketEngine class, designed to use BSD kqueue().
  */
-class KQueueEngine : public SocketEngine
+namespace
 {
-private:
        int EngineHandle;
+       unsigned int ChangePos = 0;
        /** These are used by kqueue() to hold socket events
         */
-       struct kevent* ke_list;
-       /** This is a specialised time value used by kqueue()
-        */
-       struct timespec ts;
-public:
-       /** Create a new KQueueEngine
-        */
-       KQueueEngine();
-       /** Delete a KQueueEngine
-        */
-       virtual ~KQueueEngine();
-       bool AddFd(EventHandler* eh, int event_mask);
-       void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
-       virtual void DelFd(EventHandler* eh);
-       virtual int DispatchEvents();
-       virtual std::string GetName();
-       virtual void RecoverFromFork();
-};
+       std::vector<struct kevent> ke_list(16);
 
-#include <sys/sysctl.h>
+       /** Pending changes
+        */
+       std::vector<struct kevent> changelist(8);
+}
 
-KQueueEngine::KQueueEngine()
+/** Initialize the kqueue engine
+ */
+void SocketEngine::Init()
 {
-       MAX_DESCRIPTORS = 0;
-       int mib[2];
-       size_t len;
-
-       mib[0] = CTL_KERN;
-#ifdef KERN_MAXFILESPERPROC
-       mib[1] = KERN_MAXFILESPERPROC;
-#else
-       mib[1] = KERN_MAXFILES;
-#endif
-       len = sizeof(MAX_DESCRIPTORS);
-       sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
-       if (MAX_DESCRIPTORS <= 0)
-       {
-               ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
-               std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
-               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
-       }
-
-       this->RecoverFromFork();
-       ke_list = new struct kevent[GetMaxFds()];
-       ref = new EventHandler* [GetMaxFds()];
-       memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+       LookupMaxFds();
+       RecoverFromFork();
 }
 
-void KQueueEngine::RecoverFromFork()
+void SocketEngine::RecoverFromFork()
 {
        /*
         * The only bad thing about kqueue is that its fd cant survive a fork and is not inherited.
@@ -92,177 +58,141 @@ void KQueueEngine::RecoverFromFork()
         */
        EngineHandle = kqueue();
        if (EngineHandle == -1)
-       {
-               ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
-               ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: this is a fatal error, exiting now.");
-               std::cout << "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features." << std::endl;
-               std::cout << "ERROR: this is a fatal error, exiting now." << std::endl;
-               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
-       }
-       CurrentSetSize = 0;
+               InitError();
 }
 
-KQueueEngine::~KQueueEngine()
+/** Shutdown the kqueue engine
+ */
+void SocketEngine::Deinit()
 {
-       this->Close(EngineHandle);
-       delete[] ref;
-       delete[] ke_list;
+       Close(EngineHandle);
 }
 
-bool KQueueEngine::AddFd(EventHandler* eh, int event_mask)
+static struct kevent* GetChangeKE()
+{
+       if (ChangePos >= changelist.size())
+               changelist.resize(changelist.size() * 2);
+       return &changelist[ChangePos++];
+}
+
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
 {
        int fd = eh->GetFd();
 
-       if ((fd < 0) || (fd > GetMaxFds() - 1))
+       if (fd < 0)
                return false;
 
-       if (ref[fd])
+       if (!SocketEngine::AddFdRef(eh))
                return false;
 
        // We always want to read from the socket...
-       struct kevent ke;
-       EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
+       struct kevent* ke = GetChangeKE();
+       EV_SET(ke, fd, EVFILT_READ, EV_ADD, 0, 0, static_cast<void*>(eh));
 
-       int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
-       if (i == -1)
-       {
-               ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to add fd: %d %s",
-                                         fd, strerror(errno));
-               return false;
-       }
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
 
-       ref[fd] = eh;
-       SocketEngine::SetEventMask(eh, event_mask);
+       eh->SetEventMask(event_mask);
        OnSetEvent(eh, 0, event_mask);
-       CurrentSetSize++;
+       ResizeDouble(ke_list);
 
-       ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
        return true;
 }
 
-void KQueueEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
 {
        int fd = eh->GetFd();
 
-       if ((fd < 0) || (fd > GetMaxFds() - 1))
+       if (fd < 0)
        {
-               ServerInstance->Logs->Log("SOCKET",DEFAULT,"DelFd() on invalid fd: %d", fd);
+               ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "DelFd() on invalid fd: %d", fd);
                return;
        }
 
-       struct kevent ke;
-
        // First remove the write filter ignoring errors, since we can't be
        // sure if there are actually any write filters registered.
-       EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
-       kevent(EngineHandle, &ke, 1, 0, 0, NULL);
+       struct kevent* ke = GetChangeKE();
+       EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
 
        // Then remove the read filter.
-       EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
-       int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
-
-       if (j < 0)
-       {
-               ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to remove fd: %d %s",
-                                         fd, strerror(errno));
-       }
+       ke = GetChangeKE();
+       EV_SET(ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
 
-       CurrentSetSize--;
-       ref[fd] = NULL;
+       SocketEngine::DelFdRef(eh);
 
-       ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
 }
 
-void KQueueEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
 {
        if ((new_mask & FD_WANT_POLL_WRITE) && !(old_mask & FD_WANT_POLL_WRITE))
        {
                // new poll-style write
-               struct kevent ke;
-               EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, NULL);
-               int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
-               if (i < 0) {
-                       ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
-                                                 eh->GetFd(), strerror(errno));
-               }
+               struct kevent* ke = GetChangeKE();
+               EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, static_cast<void*>(eh));
        }
        else if ((old_mask & FD_WANT_POLL_WRITE) && !(new_mask & FD_WANT_POLL_WRITE))
        {
                // removing poll-style write
-               struct kevent ke;
-               EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
-               int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
-               if (i < 0) {
-                       ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
-                                                 eh->GetFd(), strerror(errno));
-               }
+               struct kevent* ke = GetChangeKE();
+               EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
        }
        if ((new_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)) && !(old_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)))
        {
-               // new one-shot write
-               struct kevent ke;
-               EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
-               int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
-               if (i < 0) {
-                       ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
-                                                 eh->GetFd(), strerror(errno));
-               }
+               struct kevent* ke = GetChangeKE();
+               EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, static_cast<void*>(eh));
        }
 }
 
-int KQueueEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
 {
+       struct timespec ts;
        ts.tv_nsec = 0;
        ts.tv_sec = 1;
 
-       int i = kevent(EngineHandle, NULL, 0, &ke_list[0], GetMaxFds(), &ts);
+       int i = kevent(EngineHandle, &changelist.front(), ChangePos, &ke_list.front(), ke_list.size(), &ts);
+       ChangePos = 0;
        ServerInstance->UpdateTime();
 
-       TotalEvents += i;
+       if (i < 0)
+               return i;
+
+       stats.TotalEvents += i;
 
        for (int j = 0; j < i; j++)
        {
-               EventHandler* eh = ref[ke_list[j].ident];
+               struct kevent& kev = ke_list[j];
+               EventHandler* eh = static_cast<EventHandler*>(kev.udata);
                if (!eh)
                        continue;
-               if (ke_list[j].flags & EV_EOF)
+
+               // Copy these in case the vector gets resized and kev invalidated
+               const int fd = eh->GetFd();
+               const short filter = kev.filter;
+               if (fd < 0)
+                       continue;
+
+               if (kev.flags & EV_EOF)
                {
-                       ErrorEvents++;
-                       eh->HandleEvent(EVENT_ERROR, ke_list[j].fflags);
+                       stats.ErrorEvents++;
+                       eh->OnEventHandlerError(kev.fflags);
                        continue;
                }
-               if (ke_list[j].filter == EVFILT_WRITE)
+               if (filter == EVFILT_WRITE)
                {
-                       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;
-                       SetEventMask(eh, eh->GetEventMask() & ~bits_to_clr);
-                       eh->HandleEvent(EVENT_WRITE);
-
-                       if (eh != ref[ke_list[j].ident])
-                               // whoops, deleted out from under us
-                               continue;
+                       eh->SetEventMask(eh->GetEventMask() & ~bits_to_clr);
+                       eh->OnEventHandlerWrite();
                }
-               if (ke_list[j].filter == EVFILT_READ)
+               else if (filter == EVFILT_READ)
                {
-                       ReadEvents++;
-                       SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
-                       eh->HandleEvent(EVENT_READ);
+                       eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
+                       eh->OnEventHandlerRead();
                }
        }
 
        return i;
 }
-
-std::string KQueueEngine::GetName()
-{
-       return "kqueue";
-}
-
-SocketEngine* CreateSocketEngine()
-{
-       return new KQueueEngine;
-}
index e38e0fac1c4f67f537a921f428fcb48cb7063aa0..339045a8c301c5ca75196cb06c90b4d4eb027278 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2014 Adam <Adam@anope.org>
  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
  *   Copyright (C) 2009 Uli Schlachter <psychon@znc.in>
  *   Copyright (C) 2009 Craig Edwards <craigedwards@brainbox.cc>
 
 
 #include "inspircd.h"
-#include "exitcodes.h"
 
-#ifndef SOCKETENGINE_POLL
-#define SOCKETENGINE_POLL
-
-#include <iostream>
-#include <vector>
-#include <string>
-#include <map>
-#include "inspircd_config.h"
-#include "inspircd.h"
-#include "socketengine.h"
-
-#ifndef _WIN32
-# ifndef __USE_XOPEN
-#  define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
-# endif
-# include <poll.h>
-# include <sys/poll.h>
-# include <sys/resource.h>
-#else
-# define struct pollfd WSAPOLLFD
-# define poll WSAPoll
-#endif
-
-class InspIRCd;
+#include <sys/poll.h>
+#include <sys/resource.h>
 
 /** A specialisation of the SocketEngine class, designed to use poll().
  */
-class PollEngine : public SocketEngine
+namespace
 {
-private:
        /** These are used by poll() to hold socket events
         */
-       struct pollfd *events;
-       /** This map maps fds to an index in the events array.
-        */
-       std::map<int, unsigned int> fd_mappings;
-public:
-       /** Create a new PollEngine
+       std::vector<struct pollfd> events(16);
+       /** This vector maps fds to an index in the events array.
         */
-       PollEngine();
-       /** Delete a PollEngine
-        */
-       virtual ~PollEngine();
-       virtual bool AddFd(EventHandler* eh, int event_mask);
-       virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
-       virtual EventHandler* GetRef(int fd);
-       virtual void DelFd(EventHandler* eh);
-       virtual int DispatchEvents();
-       virtual std::string GetName();
-};
-
-#endif
-
-PollEngine::PollEngine()
-{
-       CurrentSetSize = 0;
-       struct rlimit limits;
-       if (!getrlimit(RLIMIT_NOFILE, &limits))
-       {
-               MAX_DESCRIPTORS = limits.rlim_cur;
-       }
-       else
-       {
-               ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
-               std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
-               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
-       }
+       std::vector<int> fd_mappings(16, -1);
+}
 
-       ref = new EventHandler* [GetMaxFds()];
-       events = new struct pollfd[GetMaxFds()];
+void SocketEngine::Init()
+{
+       LookupMaxFds();
+}
 
-       memset(events, 0, GetMaxFds() * sizeof(struct pollfd));
-       memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+void SocketEngine::Deinit()
+{
 }
 
-PollEngine::~PollEngine()
+void SocketEngine::RecoverFromFork()
 {
-       // No destruction required, either.
-       delete[] ref;
-       delete[] events;
 }
 
 static int mask_to_poll(int event_mask)
@@ -115,71 +61,70 @@ static int mask_to_poll(int event_mask)
        return rv;
 }
 
-bool PollEngine::AddFd(EventHandler* eh, int event_mask)
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
 {
        int fd = eh->GetFd();
-       if ((fd < 0) || (fd > GetMaxFds() - 1))
+       if (fd < 0)
        {
-               ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd);
                return false;
        }
 
-       if (fd_mappings.find(fd) != fd_mappings.end())
+       if (static_cast<unsigned int>(fd) < fd_mappings.size() && fd_mappings[fd] != -1)
        {
-               ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
                return false;
        }
 
        unsigned int index = CurrentSetSize;
 
+       if (!SocketEngine::AddFdRef(eh))
+       {
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
+               return false;
+       }
+
+       while (static_cast<unsigned int>(fd) >= fd_mappings.size())
+               fd_mappings.resize(fd_mappings.size() * 2, -1);
        fd_mappings[fd] = index;
-       ref[index] = eh;
+
+       ResizeDouble(events);
        events[index].fd = fd;
        events[index].events = mask_to_poll(event_mask);
 
-       ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
-       SocketEngine::SetEventMask(eh, event_mask);
-       CurrentSetSize++;
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
+       eh->SetEventMask(event_mask);
        return true;
 }
 
-EventHandler* PollEngine::GetRef(int fd)
-{
-       std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
-       if (it == fd_mappings.end())
-               return NULL;
-       return ref[it->second];
-}
-
-void PollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
 {
-       std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
-       if (it == fd_mappings.end())
+       int fd = eh->GetFd();
+       if (fd < 0 || static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
        {
-               ServerInstance->Logs->Log("SOCKET",DEBUG,"SetEvents() on unknown fd: %d", eh->GetFd());
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
                return;
        }
 
-       events[it->second].events = mask_to_poll(new_mask);
+       events[fd_mappings[fd]].events = mask_to_poll(new_mask);
 }
 
-void PollEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
 {
        int fd = eh->GetFd();
-       if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+       if (fd < 0)
        {
-               ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd);
                return;
        }
 
-       std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
-       if (it == fd_mappings.end())
+       if (static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
        {
-               ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd() on unknown fd: %d", fd);
+               ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
                return;
        }
 
-       unsigned int index = it->second;
+       unsigned int index = fd_mappings[fd];
        unsigned int last_index = CurrentSetSize - 1;
        int last_fd = events[last_index].fd;
 
@@ -193,89 +138,78 @@ void PollEngine::DelFd(EventHandler* eh)
                // move last_fd from last_index into index
                events[index].fd = last_fd;
                events[index].events = events[last_index].events;
-
-               ref[index] = ref[last_index];
        }
 
        // Now remove all data for the last fd we got into out list.
        // Above code made sure this always is right
-       fd_mappings.erase(it);
+       fd_mappings[fd] = -1;
        events[last_index].fd = 0;
        events[last_index].events = 0;
-       ref[last_index] = NULL;
 
-       CurrentSetSize--;
+       SocketEngine::DelFdRef(eh);
 
-       ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d (index: %d) "
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
                        "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
 }
 
-int PollEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
 {
-       int i = poll(events, CurrentSetSize, 1000);
-       int index;
-       socklen_t codesize = sizeof(int);
-       int errcode;
+       int i = poll(&events[0], CurrentSetSize, 1000);
        int processed = 0;
        ServerInstance->UpdateTime();
 
-       if (i > 0)
+       for (size_t index = 0; index < CurrentSetSize && processed < i; index++)
        {
-               for (index = 0; index < CurrentSetSize && processed != i; index++)
+               struct pollfd& pfd = events[index];
+
+               // Copy these in case the vector gets resized and pfd invalidated
+               const int fd = pfd.fd;
+               const short revents = pfd.revents;
+
+               if (revents)
+                       processed++;
+
+               EventHandler* eh = GetRef(fd);
+               if (!eh)
+                       continue;
+
+               if (revents & POLLHUP)
                {
-                       if (events[index].revents)
-                               processed++;
-                       EventHandler* eh = ref[index];
-                       if (!eh)
-                               continue;
+                       eh->OnEventHandlerError(0);
+                       continue;
+               }
+
+               if (revents & POLLERR)
+               {
+                       // Get error number
+                       socklen_t codesize = sizeof(int);
+                       int errcode;
+                       if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
+                               errcode = errno;
+                       eh->OnEventHandlerError(errcode);
+                       continue;
+               }
 
-                       if (events[index].revents & POLLHUP)
-                       {
-                               eh->HandleEvent(EVENT_ERROR, 0);
+               if (revents & POLLIN)
+               {
+                       eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
+                       eh->OnEventHandlerRead();
+                       if (eh != GetRef(fd))
+                               // whoops, deleted out from under us
                                continue;
-                       }
+               }
 
-                       if (events[index].revents & POLLERR)
-                       {
-                               // Get fd
-                               int fd = events[index].fd;
+               if (revents & POLLOUT)
+               {
+                       int mask = eh->GetEventMask();
+                       mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
+                       eh->SetEventMask(mask);
 
-                               // Get error number
-                               if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
-                                       errcode = errno;
-                               eh->HandleEvent(EVENT_ERROR, errcode);
-                               continue;
-                       }
-
-                       if (events[index].revents & POLLIN)
-                       {
-                               SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
-                               eh->HandleEvent(EVENT_READ);
-                               if (eh != ref[index])
-                                       // whoops, deleted out from under us
-                                       continue;
-                       }
-                       
-                       if (events[index].revents & POLLOUT)
-                       {
-                               int mask = eh->GetEventMask();
-                               mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
-                               SetEventMask(eh, mask);
-                               events[index].events = mask_to_poll(mask);
-                               eh->HandleEvent(EVENT_WRITE);
-                       }
+                       // The vector could've been resized, reference can be invalid by now; don't use it
+                       events[index].events = mask_to_poll(mask);
+                       eh->OnEventHandlerWrite();
                }
        }
 
        return i;
 }
-
-std::string PollEngine::GetName()
-{
-       return "poll";
-}
-
-SocketEngine* CreateSocketEngine()
-{
-       return new PollEngine;
-}
diff --git a/src/socketengines/socketengine_ports.cpp b/src/socketengines/socketengine_ports.cpp
deleted file mode 100644 (file)
index f7c547d..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007-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 "exitcodes.h"
-#include <port.h>
-
-#ifndef SOCKETENGINE_PORTS
-#define SOCKETENGINE_PORTS
-
-#ifndef __sun
-# error You need Solaris 10 or later to make use of this code.
-#endif
-
-#include <vector>
-#include <string>
-#include <map>
-#include "inspircd_config.h"
-#include "inspircd.h"
-#include "socketengine.h"
-#include <port.h>
-#include <iostream>
-
-/** A specialisation of the SocketEngine class, designed to use solaris 10 I/O completion ports
- */
-class PortsEngine : public SocketEngine
-{
-private:
-       /** These are used by epoll() to hold socket events
-        */
-       port_event_t* events;
-       int EngineHandle;
-public:
-       /** Create a new PortsEngine
-        */
-       PortsEngine();
-       /** Delete a PortsEngine
-        */
-       virtual ~PortsEngine();
-       virtual bool AddFd(EventHandler* eh, int event_mask);
-       virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
-       virtual void DelFd(EventHandler* eh);
-       virtual int DispatchEvents();
-       virtual std::string GetName();
-};
-
-#endif
-
-
-#include <ulimit.h>
-
-PortsEngine::PortsEngine()
-{
-       int max = ulimit(4, 0);
-       if (max > 0)
-       {
-               MAX_DESCRIPTORS = max;
-       }
-       else
-       {
-               ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
-               std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
-               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
-       }
-       EngineHandle = port_create();
-
-       if (EngineHandle == -1)
-       {
-               ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno));
-               ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: This is a fatal error, exiting now.");
-               std::cout << "ERROR: Could not initialize socket engine: " << strerror(errno) << std::endl;
-               std::cout << "ERROR: This is a fatal error, exiting now." << std::endl;
-               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
-       }
-       CurrentSetSize = 0;
-
-       ref = new EventHandler* [GetMaxFds()];
-       events = new port_event_t[GetMaxFds()];
-       memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
-}
-
-PortsEngine::~PortsEngine()
-{
-       this->Close(EngineHandle);
-       delete[] ref;
-       delete[] events;
-}
-
-static int mask_to_events(int event_mask)
-{
-       int rv = 0;
-       if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
-               rv |= POLLRDNORM;
-       if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
-               rv |= POLLWRNORM;
-       return rv;
-}
-
-bool PortsEngine::AddFd(EventHandler* eh, int event_mask)
-{
-       int fd = eh->GetFd();
-       if ((fd < 0) || (fd > GetMaxFds() - 1))
-               return false;
-
-       if (ref[fd])
-               return false;
-
-       ref[fd] = eh;
-       SocketEngine::SetEventMask(eh, event_mask);
-       port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(event_mask), eh);
-
-       ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
-       CurrentSetSize++;
-       return true;
-}
-
-void PortsEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
-{
-       if (mask_to_events(new_mask) != mask_to_events(old_mask))
-               port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), mask_to_events(new_mask), eh);
-}
-
-void PortsEngine::DelFd(EventHandler* eh)
-{
-       int fd = eh->GetFd();
-       if ((fd < 0) || (fd > GetMaxFds() - 1))
-               return;
-
-       port_dissociate(EngineHandle, PORT_SOURCE_FD, fd);
-
-       CurrentSetSize--;
-       ref[fd] = NULL;
-
-       ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
-}
-
-int PortsEngine::DispatchEvents()
-{
-       struct timespec poll_time;
-
-       poll_time.tv_sec = 1;
-       poll_time.tv_nsec = 0;
-
-       unsigned int nget = 1; // used to denote a retrieve request.
-       int ret = port_getn(EngineHandle, this->events, GetMaxFds() - 1, &nget, &poll_time);
-       ServerInstance->UpdateTime();
-
-       // first handle an error condition
-       if (ret == -1)
-               return -1;
-
-       TotalEvents += nget;
-
-       unsigned int i;
-       for (i = 0; i < nget; i++)
-       {
-               switch (this->events[i].portev_source)
-               {
-                       case PORT_SOURCE_FD:
-                       {
-                               int fd = this->events[i].portev_object;
-                               EventHandler* eh = ref[fd];
-                               if (eh)
-                               {
-                                       int mask = eh->GetEventMask();
-                                       if (events[i].portev_events & POLLWRNORM)
-                                               mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE);
-                                       if (events[i].portev_events & POLLRDNORM)
-                                               mask &= ~FD_READ_WILL_BLOCK;
-                                       // reinsert port for next time around, pretending to be one-shot for writes
-                                       SetEventMask(eh, mask);
-                                       port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(mask), eh);
-                                       if (events[i].portev_events & POLLRDNORM)
-                                       {
-                                               ReadEvents++;
-                                               eh->HandleEvent(EVENT_READ);
-                                               if (eh != ref[fd])
-                                                       continue;
-                                       }
-                                       if (events[i].portev_events & POLLWRNORM)
-                                       {
-                                               WriteEvents++;
-                                               eh->HandleEvent(EVENT_WRITE);
-                                       }
-                               }
-                       }
-                       default:
-                       break;
-               }
-       }
-
-       return (int)i;
-}
-
-std::string PortsEngine::GetName()
-{
-       return "ports";
-}
-
-SocketEngine* CreateSocketEngine()
-{
-       return new PortsEngine;
-}
index 0b5abaf304792b6d14978992ec1aa53d1cb77b86..03f0aca62a04de54ab73137d0439dccc0f88888a 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2014 Adam <Adam@anope.org>
  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
  *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
  *
  */
 
 
-#include "inspircd_config.h"
-
 #include "inspircd.h"
-#include "socketengine.h"
 
 #ifndef _WIN32
 #include <sys/select.h>
 
 /** A specialisation of the SocketEngine class, designed to use traditional select().
  */
-class SelectEngine : public SocketEngine
+namespace
 {
        fd_set ReadSet, WriteSet, ErrSet;
-       int MaxFD;
-
-public:
-       /** Create a new SelectEngine
-        */
-       SelectEngine();
-       /** Delete a SelectEngine
-        */
-       virtual ~SelectEngine();
-       virtual bool AddFd(EventHandler* eh, int event_mask);
-       virtual void DelFd(EventHandler* eh);
-       void OnSetEvent(EventHandler* eh, int, int);
-       virtual int DispatchEvents();
-       virtual std::string GetName();
-};
-
-SelectEngine::SelectEngine()
-{
-       MAX_DESCRIPTORS = FD_SETSIZE;
-       CurrentSetSize = 0;
+       int MaxFD = 0;
+}
 
-       ref = new EventHandler* [GetMaxFds()];
-       memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+void SocketEngine::Init()
+{
+       MaxSetSize = FD_SETSIZE;
 
        FD_ZERO(&ReadSet);
        FD_ZERO(&WriteSet);
        FD_ZERO(&ErrSet);
-       MaxFD = 0;
 }
 
-SelectEngine::~SelectEngine()
+void SocketEngine::Deinit()
 {
-       delete[] ref;
 }
 
-bool SelectEngine::AddFd(EventHandler* eh, int event_mask)
+void SocketEngine::RecoverFromFork()
+{
+}
+
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
 {
        int fd = eh->GetFd();
-       if ((fd < 0) || (fd > GetMaxFds() - 1))
+
+       if (fd < 0)
                return false;
 
-       if (ref[fd])
+       if (static_cast<size_t>(fd) >= GetMaxFds())
                return false;
 
-       ref[fd] = eh;
+       if (!SocketEngine::AddFdRef(eh))
+               return false;
 
-       SocketEngine::SetEventMask(eh, event_mask);
+       eh->SetEventMask(event_mask);
        OnSetEvent(eh, 0, event_mask);
        FD_SET(fd, &ErrSet);
        if (fd > MaxFD)
                MaxFD = fd;
 
-       CurrentSetSize++;
-
-       ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
        return true;
 }
 
-void SelectEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
 {
        int fd = eh->GetFd();
 
-       if ((fd < 0) || (fd > GetMaxFds() - 1))
+       if (fd < 0)
+               return;
+
+       if (static_cast<size_t>(fd) >= GetMaxFds())
                return;
 
-       CurrentSetSize--;
-       ref[fd] = NULL;
+       SocketEngine::DelFdRef(eh);
 
        FD_CLR(fd, &ReadSet);
        FD_CLR(fd, &WriteSet);
@@ -106,10 +91,10 @@ void SelectEngine::DelFd(EventHandler* eh)
        if (fd == MaxFD)
                --MaxFD;
 
-       ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+       ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
 }
 
-void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
 {
        int fd = eh->GetFd();
        int diff = old_mask ^ new_mask;
@@ -130,7 +115,7 @@ void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
        }
 }
 
-int SelectEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
 {
        timeval tval;
        tval.tv_sec = 1;
@@ -141,63 +126,48 @@ int SelectEngine::DispatchEvents()
        int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval);
        ServerInstance->UpdateTime();
 
-       /* Nothing to process this time around */
-       if (sresult < 1)
-               return 0;
-
        for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++)
        {
                int has_read = FD_ISSET(i, &rfdset), has_write = FD_ISSET(i, &wfdset), has_error = FD_ISSET(i, &errfdset);
 
-               if (has_read || has_write || has_error)
-               {
-                       --j;
+               if (!(has_read || has_write || has_error))
+                       continue;
 
-                       EventHandler* ev = ref[i];
-                       if (!ev)
-                               continue;
+               --j;
 
-                       if (has_error)
-                       {
-                               ErrorEvents++;
+               EventHandler* ev = GetRef(i);
+               if (!ev)
+                       continue;
 
-                               socklen_t codesize = sizeof(int);
-                               int errcode = 0;
-                               if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
-                                       errcode = errno;
+               if (has_error)
+               {
+                       stats.ErrorEvents++;
+
+                       socklen_t codesize = sizeof(int);
+                       int errcode = 0;
+                       if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
+                               errcode = errno;
+
+                       ev->OnEventHandlerError(errcode);
+                       continue;
+               }
 
-                               ev->HandleEvent(EVENT_ERROR, errcode);
+               if (has_read)
+               {
+                       ev->SetEventMask(ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
+                       ev->OnEventHandlerRead();
+                       if (ev != GetRef(i))
                                continue;
-                       }
-
-                       if (has_read)
-                       {
-                               ReadEvents++;
-                               SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
-                               ev->HandleEvent(EVENT_READ);
-                               if (ev != ref[i])
-                                       continue;
-                       }
-                       if (has_write)
-                       {
-                               WriteEvents++;
-                               int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
-                               this->OnSetEvent(ev, ev->GetEventMask(), newmask);
-                               SetEventMask(ev, newmask);
-                               ev->HandleEvent(EVENT_WRITE);
-                       }
+               }
+
+               if (has_write)
+               {
+                       int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
+                       SocketEngine::OnSetEvent(ev, ev->GetEventMask(), newmask);
+                       ev->SetEventMask(newmask);
+                       ev->OnEventHandlerWrite();
                }
        }
 
        return sresult;
 }
-
-std::string SelectEngine::GetName()
-{
-       return "select";
-}
-
-SocketEngine* CreateSocketEngine()
-{
-       return new SelectEngine;
-}
diff --git a/src/testsuite.cpp b/src/testsuite.cpp
deleted file mode 100644 (file)
index 58b72ee..0000000
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2008 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/>.
- */
-
-
-/* $Core */
-
-#include "inspircd.h"
-#include "testsuite.h"
-#include "threadengine.h"
-#include <iostream>
-
-class TestSuiteThread : public Thread
-{
- public:
-       TestSuiteThread() : Thread()
-       {
-       }
-
-       virtual ~TestSuiteThread()
-       {
-       }
-
-       virtual void Run()
-       {
-               while (GetExitFlag() == false)
-               {
-                       std::cout << "Test suite thread run...\n";
-                       sleep(5);
-               }
-       }
-};
-
-TestSuite::TestSuite()
-{
-       std::cout << "\n\n*** STARTING TESTSUITE ***\n";
-
-       std::string modname;
-       char choice;
-
-       while (1)
-       {
-               std::cout << "(1) Call all module OnRunTestSuite() methods\n";
-               std::cout << "(2) Load a module\n";
-               std::cout << "(3) Unload a module\n";
-               std::cout << "(4) Threading tests\n";
-               std::cout << "(5) Wildcard and CIDR tests\n";
-               std::cout << "(6) Comma sepstream tests\n";
-               std::cout << "(7) Space sepstream tests\n";
-               std::cout << "(8) UID generation tests\n";
-
-               std::cout << std::endl << "(X) Exit test suite\n";
-
-               std::cout << "\nChoices (Enter one or more options as a list then press enter, e.g. 15X): ";
-               std::cin >> choice;
-
-               if (!choice)
-                       continue;
-
-               switch (choice)
-               {
-                       case '1':
-                               FOREACH_MOD(I_OnRunTestSuite, OnRunTestSuite());
-                               break;
-                       case '2':
-                               std::cout << "Enter module filename to load: ";
-                               std::cin >> modname;
-                               std::cout << (ServerInstance->Modules->Load(modname.c_str()) ? "\nSUCCESS!\n" : "\nFAILURE\n");
-                               break;
-                       case '3':
-                               std::cout << "Enter module filename to unload: ";
-                               std::cin >> modname;
-                               {
-                                       Module* m = ServerInstance->Modules->Find(modname);
-                                       std::cout << (ServerInstance->Modules->Unload(m) ? "\nSUCCESS!\n" : "\nFAILURE\n");
-                                       ServerInstance->AtomicActions.Run();
-                               }
-                               break;
-                       case '4':
-                               std::cout << (DoThreadTests() ? "\nSUCCESS!\n" : "\nFAILURE\n");
-                               break;
-                       case '5':
-                               std::cout << (DoWildTests() ? "\nSUCCESS!\n" : "\nFAILURE\n");
-                               break;
-                       case '6':
-                               std::cout << (DoCommaSepStreamTests() ? "\nSUCCESS!\n" : "\nFAILURE\n");
-                               break;
-                       case '7':
-                               std::cout << (DoSpaceSepStreamTests() ? "\nSUCCESS!\n" : "\nFAILURE\n");
-                               break;
-                       case '8':
-                               std::cout << (DoGenerateUIDTests() ? "\nSUCCESS!\n" : "\nFAILURE\n");
-                               break;
-                       case 'X':
-                               return;
-                               break;
-                       default:
-                               std::cout << "Invalid option\n";
-                               break;
-               }
-               std::cout << std::endl;
-       }
-}
-
-/* Test that x matches y with match() */
-#define WCTEST(x, y) std::cout << "match(\"" << x << "\",\"" << y "\") " << ((passed = (InspIRCd::Match(x, y, NULL))) ? " SUCCESS!\n" : " FAILURE\n")
-/* Test that x does not match y with match() */
-#define WCTESTNOT(x, y) std::cout << "!match(\"" << x << "\",\"" << y "\") " << ((passed = ((!InspIRCd::Match(x, y, NULL)))) ? " SUCCESS!\n" : " FAILURE\n")
-
-/* Test that x matches y with match() and cidr enabled */
-#define CIDRTEST(x, y) std::cout << "match(\"" << x << "\",\"" << y "\", true) " << ((passed = (InspIRCd::MatchCIDR(x, y, NULL))) ? " SUCCESS!\n" : " FAILURE\n")
-/* Test that x does not match y with match() and cidr enabled */
-#define CIDRTESTNOT(x, y) std::cout << "!match(\"" << x << "\",\"" << y "\", true) " << ((passed = ((!InspIRCd::MatchCIDR(x, y, NULL)))) ? " SUCCESS!\n" : " FAILURE\n")
-
-bool TestSuite::DoWildTests()
-{
-       std::cout << "\n\nWildcard and CIDR tests\n\n";
-       bool passed = false;
-
-       WCTEST("foobar", "*");
-       WCTEST("foobar", "foo*");
-       WCTEST("foobar", "*bar");
-       WCTEST("foobar", "foo??r");
-       WCTEST("foobar.test", "fo?bar.*t");
-       WCTEST("foobar.test", "fo?bar.t*t");
-       WCTEST("foobar.tttt", "fo?bar.**t");
-       WCTEST("foobar", "foobar");
-       WCTEST("foobar", "foo***bar");
-       WCTEST("foobar", "*foo***bar");
-       WCTEST("foobar", "**foo***bar");
-       WCTEST("foobar", "**foobar*");
-       WCTEST("foobar", "**foobar**");
-       WCTEST("foobar", "**foobar");
-       WCTEST("foobar", "**f?*?ar");
-       WCTEST("foobar", "**f?*b?r");
-       WCTEST("foofar", "**f?*f*r");
-       WCTEST("foofar", "**f?*f*?");
-       WCTEST("r", "*");
-       WCTEST("", "");
-       WCTEST("test@foo.bar.test", "*@*.bar.test");
-       WCTEST("test@foo.bar.test", "*test*@*.bar.test");
-       WCTEST("test@foo.bar.test", "*@*test");
-
-       WCTEST("a", "*a");
-       WCTEST("aa", "*a");
-       WCTEST("aaa", "*a");
-       WCTEST("aaaa", "*a");
-       WCTEST("aaaaa", "*a");
-       WCTEST("aaaaaa", "*a");
-       WCTEST("aaaaaaa", "*a");
-       WCTEST("aaaaaaaa", "*a");
-       WCTEST("aaaaaaaaa", "*a");
-       WCTEST("aaaaaaaaaa", "*a");
-       WCTEST("aaaaaaaaaaa", "*a");
-
-       WCTESTNOT("foobar", "bazqux");
-       WCTESTNOT("foobar", "*qux");
-       WCTESTNOT("foobar", "foo*x");
-       WCTESTNOT("foobar", "baz*");
-       WCTESTNOT("foobar", "foo???r");
-       WCTESTNOT("foobar", "foobars");
-       WCTESTNOT("foobar", "**foobar**h");
-       WCTESTNOT("foobar", "**foobar**h*");
-       WCTESTNOT("foobar", "**f??*bar?");
-       WCTESTNOT("foobar", "");
-       WCTESTNOT("", "foobar");
-       WCTESTNOT("OperServ", "O");
-       WCTESTNOT("O", "OperServ");
-       WCTESTNOT("foobar.tst", "fo?bar.*g");
-       WCTESTNOT("foobar.test", "fo?bar.*tt");
-
-       CIDRTEST("brain@1.2.3.4", "*@1.2.0.0/16");
-       CIDRTEST("brain@1.2.3.4", "*@1.2.3.0/24");
-       CIDRTEST("192.168.3.97", "192.168.3.0/24");
-
-       CIDRTESTNOT("brain@1.2.3.4", "x*@1.2.0.0/16");
-       CIDRTESTNOT("brain@1.2.3.4", "*@1.3.4.0/24");
-       CIDRTESTNOT("1.2.3.4", "1.2.4.0/24");
-       CIDRTESTNOT("brain@1.2.3.4", "*@/24");
-       CIDRTESTNOT("brain@1.2.3.4", "@1.2.3.4/9");
-       CIDRTESTNOT("brain@1.2.3.4", "@");
-       CIDRTESTNOT("brain@1.2.3.4", "");
-
-       return true;
-}
-
-
-#define STREQUALTEST(x, y) std::cout << "==(\"" << x << ",\"" << y "\") " << ((passed = (x == y)) ? "SUCCESS\n" : "FAILURE\n")
-
-bool TestSuite::DoCommaSepStreamTests()
-{
-       bool passed = false;
-       irc::commasepstream items("this,is,a,comma,stream");
-       std::string item;
-       int idx = 0;
-
-       while (items.GetToken(item))
-       {
-               idx++;
-
-               switch (idx)
-               {
-                       case 1:
-                               STREQUALTEST(item, "this");
-                               break;
-                       case 2:
-                               STREQUALTEST(item, "is");
-                               break;
-                       case 3:
-                               STREQUALTEST(item, "a");
-                               break;
-                       case 4:
-                               STREQUALTEST(item, "comma");
-                               break;
-                       case 5:
-                               STREQUALTEST(item, "stream");
-                               break;
-                       default:
-                               std::cout << "COMMASEPSTREAM: FAILURE: Got an index too many! " << idx << " items\n";
-                               break;
-               }
-       }
-
-       return true;
-}
-
-bool TestSuite::DoSpaceSepStreamTests()
-{
-       bool passed = false;
-
-       irc::spacesepstream list("this is a space stream");
-       std::string item;
-       int idx = 0;
-
-       while (list.GetToken(item))
-       {
-               idx++;
-
-               switch (idx)
-               {
-                       case 1:
-                               STREQUALTEST(item, "this");
-                               break;
-                       case 2:
-                               STREQUALTEST(item, "is");
-                               break;
-                       case 3:
-                               STREQUALTEST(item, "a");
-                               break;
-                       case 4:
-                               STREQUALTEST(item, "space");
-                               break;
-                       case 5:
-                               STREQUALTEST(item, "stream");
-                               break;
-                       default:
-                               std::cout << "SPACESEPSTREAM: FAILURE: Got an index too many! " << idx << " items\n";
-                               break;
-               }
-       }
-       return true;
-}
-
-bool TestSuite::DoThreadTests()
-{
-       std::string anything;
-       ThreadEngine* te = NULL;
-
-       std::cout << "Creating new ThreadEngine class...\n";
-       try
-       {
-               te = new ThreadEngine;
-       }
-       catch (...)
-       {
-               std::cout << "Creation failed, test failure.\n";
-               return false;
-       }
-       std::cout << "Creation success, type " << te->GetName() << "\n";
-
-       std::cout << "Allocate: new TestSuiteThread...\n";
-       TestSuiteThread* tst = new TestSuiteThread();
-
-       std::cout << "ThreadEngine::Create on TestSuiteThread...\n";
-       try
-       {
-               try
-               {
-                       te->Start(tst);
-               }
-               catch (CoreException &ce)
-               {
-                       std::cout << "Failure: " << ce.GetReason() << std::endl;
-               }
-       }
-       catch (...)
-       {
-               std::cout << "Failure, unhandled exception\n";
-       }
-
-       std::cout << "Type any line and press enter to end test.\n";
-       std::cin >> anything;
-
-       /* Thread engine auto frees thread on delete */
-       std::cout << "Waiting for thread to exit... " << std::flush;
-       delete tst;
-       std::cout << "Done!\n";
-
-       std::cout << "Delete ThreadEngine... ";
-       delete te;
-       std::cout << "Done!\n";
-
-       return true;
-}
-
-bool TestSuite::DoGenerateUIDTests()
-{
-       bool success = RealGenerateUIDTests();
-
-       // Reset the UID generation state so running the tests multiple times won't mess things up
-       for (unsigned int i = 0; i < 3; i++)
-               ServerInstance->current_uid[i] = ServerInstance->Config->sid[i];
-       for (unsigned int i = 3; i < UUID_LENGTH-1; i++)
-               ServerInstance->current_uid[i] = '9';
-
-       ServerInstance->current_uid[UUID_LENGTH-1] = '\0';
-
-       return success;
-}
-
-bool TestSuite::RealGenerateUIDTests()
-{
-       std::string first_uid = ServerInstance->GetUID();
-       if (first_uid.length() != UUID_LENGTH-1)
-       {
-               std::cout << "GENERATEUID: Generated UID is " << first_uid.length() << " characters long instead of " << UUID_LENGTH-1 << std::endl;
-               return false;
-       }
-
-       if (ServerInstance->current_uid[UUID_LENGTH-1] != '\0')
-       {
-               std::cout << "GENERATEUID: The null terminator is missing from the end of current_uid" << std::endl;
-               return false;
-       }
-
-       // The correct UID when generating one for the first time is ...AAAAAA
-       std::string correct_uid = ServerInstance->Config->sid + std::string(UUID_LENGTH - 4, 'A');
-       if (first_uid != correct_uid)
-       {
-               std::cout << "GENERATEUID: Generated an invalid first UID: " << first_uid << " instead of " << correct_uid << std::endl;
-               return false;
-       }
-
-       // Set current_uid to be ...Z99999
-       ServerInstance->current_uid[3] = 'Z';
-       for (unsigned int i = 4; i < UUID_LENGTH-1; i++)
-               ServerInstance->current_uid[i] = '9';
-
-       // Store the UID we'll be incrementing so we can display what's wrong later if necessary
-       std::string before_increment(ServerInstance->current_uid);
-       std::string generated_uid = ServerInstance->GetUID();
-
-       // Correct UID after incrementing ...Z99999 is ...0AAAAA
-       correct_uid = ServerInstance->Config->sid + "0" + std::string(UUID_LENGTH - 5, 'A');
-
-       if (generated_uid != correct_uid)
-       {
-               std::cout << "GENERATEUID: Generated an invalid UID after incrementing " << before_increment << ": " << generated_uid << " instead of " << correct_uid << std::endl;
-               return false;
-       }
-
-       // Set current_uid to be ...999999 to see if it rolls over correctly
-       for (unsigned int i = 3; i < UUID_LENGTH-1; i++)
-               ServerInstance->current_uid[i] = '9';
-
-       before_increment.assign(ServerInstance->current_uid);
-       generated_uid = ServerInstance->GetUID();
-
-       // Correct UID after rolling over is the first UID we've generated (...AAAAAA)
-       if (generated_uid != first_uid)
-       {
-               std::cout << "GENERATEUID: Generated an invalid UID after incrementing " << before_increment << ": " << generated_uid << " instead of " << first_uid << std::endl;
-               return false;
-       }
-
-       return true;
-}
-
-TestSuite::~TestSuite()
-{
-       std::cout << "\n\n*** END OF TEST SUITE ***\n";
-}
-
index 8f1895c0fb74619bb94c1efeab430f2b846c6208..f757aa56c7d1509f332306b7d2bace777c9912e0 100644 (file)
  */
 
 
-/* $Core */
-
-/*********        DEFAULTS       **********/
-/* $ExtraSources: threadengines/threadengine_pthread.cpp */
-/* $ExtraObjects: threadengine_pthread.o */
-
 #include "inspircd.h"
-#include "threadengine.h"
 
 void Thread::SetExitFlag()
 {
@@ -33,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);
 }
index 40205da3118533479382f8f7f8dba2b5e182ffe1..3249f442bfb31e5662cfed2d8853eea7b2730f20 100644 (file)
 #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
@@ -75,13 +59,12 @@ class ThreadSignalSocket : public EventHandler
        ThreadSignalSocket(SocketThread* p, int newfd) : parent(p)
        {
                SetFd(newfd);
-               ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
+               SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
        }
 
        ~ThreadSignalSocket()
        {
-               ServerInstance->SE->DelFd(this);
-               ServerInstance->SE->Close(GetFd());
+               SocketEngine::Close(this);
        }
 
        void Notify()
@@ -89,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();
        }
 };
 
@@ -123,15 +109,14 @@ class ThreadSignalSocket : public EventHandler
                parent(p), send_fd(sendfd)
        {
                SetFd(recvfd);
-               ServerInstance->SE->NonBlocking(fd);
-               ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
+               SocketEngine::NonBlocking(fd);
+               SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
        }
 
        ~ThreadSignalSocket()
        {
                close(send_fd);
-               ServerInstance->SE->DelFd(this);
-               ServerInstance->SE->Close(GetFd());
+               SocketEngine::Close(this);
        }
 
        void Notify()
@@ -140,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();
        }
 };
 
index ea37892f8d9c0d239ca1b1aa485f590a230b4fcf..0f0d1f277442578a259c7eab14fed2063e6022cd 100644 (file)
 #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,22 +97,22 @@ 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");
-       ServerInstance->SE->NonBlocking(connFD);
+       SocketEngine::NonBlocking(connFD);
 
        struct sockaddr_in addr;
        socklen_t sz = sizeof(addr);
        getsockname(listenFD, reinterpret_cast<struct sockaddr*>(&addr), &sz);
        connect(connFD, reinterpret_cast<struct sockaddr*>(&addr), sz);
-       ServerInstance->SE->Blocking(listenFD);
+       SocketEngine::Blocking(listenFD);
        int nfd = accept(listenFD, reinterpret_cast<struct sockaddr*>(&addr), &sz);
        if (nfd < 0)
                throw CoreException("Could not create ITC pipe");
        new ThreadSignalSocket(this, nfd);
        closesocket(listenFD);
 
-       ServerInstance->SE->Blocking(connFD);
+       SocketEngine::Blocking(connFD);
        this->signal.connFD = connFD;
 }
 
index e04a186cffbbe30eaa55fadbd8584239ad6c0a7e..48ad79df7207e246bddb84a69df1cf9859ac487d 100644 (file)
  */
 
 
-/* $Core */
-
 #include "inspircd.h"
-#include "timer.h"
 
-TimerManager::TimerManager()
+void Timer::SetInterval(unsigned int newinterval)
 {
+       ServerInstance->Timers.DelTimer(this);
+       secs = newinterval;
+       SetTrigger(ServerInstance->Time() + newinterval);
+       ServerInstance->Timers.AddTimer(this);
 }
 
-TimerManager::~TimerManager()
+Timer::Timer(unsigned int secs_from_now, bool repeating)
+       : trigger(ServerInstance->Time() + secs_from_now)
+       , secs(secs_from_now)
+       , repeat(repeating)
 {
-       for(std::vector<Timer *>::iterator i = Timers.begin(); i != Timers.end(); i++)
-               delete *i;
+}
+
+Timer::~Timer()
+{
+       ServerInstance->Timers.DelTimer(this);
 }
 
 void TimerManager::TickTimers(time_t TIME)
 {
-       while ((Timers.size()) && (TIME > (*Timers.begin())->GetTimer()))
+       for (TimerMap::iterator i = Timers.begin(); i != Timers.end(); )
        {
-               std::vector<Timer *>::iterator i = Timers.begin();
-               Timer *t = (*i);
+               Timer* t = i->second;
+               if (t->GetTrigger() > TIME)
+                       break;
+
+               Timers.erase(i++);
 
-               // Probable fix: move vector manipulation to *before* we modify the vector.
-               Timers.erase(i);
+               if (!t->Tick(TIME))
+                       continue;
 
-               t->Tick(TIME);
                if (t->GetRepeat())
                {
-                       t->SetTimer(TIME + t->GetSecs());
+                       t->SetTrigger(TIME + t->GetInterval());
                        AddTimer(t);
                }
-               else
-                       delete t;
        }
 }
 
-void TimerManager::DelTimer(Timer* T)
+void TimerManager::DelTimer(Timer* t)
 {
-       std::vector<Timer *>::iterator i = std::find(Timers.begin(), Timers.end(), T);
+       std::pair<TimerMap::iterator, TimerMap::iterator> itpair = Timers.equal_range(t->GetTrigger());
 
-       if (i != Timers.end())
+       for (TimerMap::iterator i = itpair.first; i != itpair.second; ++i)
        {
-               delete (*i);
-               Timers.erase(i);
+               if (i->second == t)
+               {
+                       Timers.erase(i);
+                       break;
+               }
        }
 }
 
-void TimerManager::AddTimer(Timer* T)
-{
-       Timers.push_back(T);
-       std::sort(Timers.begin(), Timers.end(), TimerManager::TimerComparison);
-}
-
-bool TimerManager::TimerComparison( Timer *one, Timer *two)
+void TimerManager::AddTimer(Timer* t)
 {
-       return (one->GetTimer()) < (two->GetTimer());
+       Timers.insert(std::make_pair(t->GetTrigger(), t));
 }
diff --git a/src/user_resolver.cpp b/src/user_resolver.cpp
deleted file mode 100644 (file)
index f18fc9a..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * 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"
-UserResolver::UserResolver(LocalUser* user, std::string to_resolve, QueryType qt, bool &cache) :
-       Resolver(to_resolve, qt, cache, NULL), uuid(user->uuid)
-{
-       this->fwd = (qt == DNS_QUERY_A || qt == DNS_QUERY_AAAA);
-}
-
-void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
-{
-       UserResolver *res_forward; // for forward-resolution
-       LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
-       if (!bound_user)
-       {
-               ServerInstance->Logs->Log("RESOLVER", DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
-               return;
-       }
-
-       ServerInstance->Logs->Log("RESOLVER", DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), input.c_str(), result.c_str());
-
-       if (!fwd)
-       {
-               // first half of resolution is done. We now need to verify that the host matches.
-               bound_user->stored_host = result;
-               try
-               {
-                       /* Check we didnt time out */
-                       if (bound_user->registered != REG_ALL)
-                       {
-                               bool lcached = false;
-                               if (bound_user->client_sa.sa.sa_family == AF_INET6)
-                               {
-                                       /* IPV6 forward lookup */
-                                       res_forward = new UserResolver(bound_user, result, DNS_QUERY_AAAA, lcached);
-                               }
-                               else
-                               {
-                                       /* IPV4 lookup */
-                                       res_forward = new UserResolver(bound_user, result, DNS_QUERY_A, lcached);
-                               }
-                               ServerInstance->AddResolver(res_forward, lcached);
-                       }
-               }
-               catch (CoreException& e)
-               {
-                       ServerInstance->Logs->Log("RESOLVER", DEBUG,"Error in resolver: %s",e.GetReason());
-               }
-       }
-       else
-       {
-               /* Both lookups completed */
-
-               irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
-               bool rev_match = false;
-               if (user_ip->sa.sa_family == AF_INET6)
-               {
-                       struct in6_addr res_bin;
-                       if (inet_pton(AF_INET6, result.c_str(), &res_bin))
-                       {
-                               rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
-                       }
-               }
-               else
-               {
-                       struct in_addr res_bin;
-                       if (inet_pton(AF_INET, result.c_str(), &res_bin))
-                       {
-                               rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
-                       }
-               }
-               
-               if (rev_match)
-               {
-                       std::string hostname = bound_user->stored_host;
-                       if (hostname.length() < 65)
-                       {
-                               /* Check we didnt time out */
-                               if ((bound_user->registered != REG_ALL) && (!bound_user->dns_done))
-                               {
-                                       /* Hostnames starting with : are not a good thing (tm) */
-                                       if (hostname[0] == ':')
-                                               hostname.insert(0, "0");
-
-                                       bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname.c_str(), (cached ? " -- cached" : ""));
-                                       bound_user->dns_done = true;
-                                       bound_user->dhost.assign(hostname, 0, 64);
-                                       bound_user->host.assign(hostname, 0, 64);
-                                       /* Invalidate cache */
-                                       bound_user->InvalidateCache();
-                               }
-                       }
-                       else
-                       {
-                               if (!bound_user->dns_done)
-                               {
-                                       bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", bound_user->GetIPString());
-                                       bound_user->dns_done = true;
-                               }
-                       }
-               }
-               else
-               {
-                       if (!bound_user->dns_done)
-                       {
-                               bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", bound_user->GetIPString());
-                               bound_user->dns_done = true;
-                       }
-               }
-
-               // Save some memory by freeing this up; it's never used again in the user's lifetime.
-               bound_user->stored_host.resize(0);
-       }
-}
-
-void UserResolver::OnError(ResolverError e, const std::string &errormessage)
-{
-       LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
-       if (bound_user)
-       {
-               bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname: %s; using your IP address (%s) instead.", errormessage.c_str(), bound_user->GetIPString());
-               bound_user->dns_done = true;
-               bound_user->stored_host.resize(0);
-               ServerInstance->stats->statsDnsBad++;
-       }
-}
index f62d28faab3d83c8dad12a88454d5e73590ee5c6..fafeffb42bce7e55355ea2d59f1b7d0914c43561 100644 (file)
 
 #include "inspircd.h"
 #include "xline.h"
-#include "bancache.h"
+#include "iohook.h"
 
-UserManager::UserManager()
-       : unregistered_count(0), local_count(0)
-{
-}
-
-/* add a client connection to the sockets list */
-void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+namespace
 {
-       /* NOTE: Calling this one parameter constructor for User automatically
-        * allocates a new UUID and places it in the hash_map.
-        */
-       LocalUser* New = NULL;
-       try
+       class WriteCommonQuit : public User::ForEachNeighborHandler
        {
-               New = new LocalUser(socket, client, server);
-       }
-       catch (...)
+               ClientProtocol::Messages::Quit quitmsg;
+               ClientProtocol::Event quitevent;
+               ClientProtocol::Messages::Quit operquitmsg;
+               ClientProtocol::Event operquitevent;
+
+               void Execute(LocalUser* user) CXX11_OVERRIDE
+               {
+                       user->Send(user->IsOper() ? operquitevent : quitevent);
+               }
+
+        public:
+               WriteCommonQuit(User* user, const std::string& msg, const std::string& opermsg)
+                       : quitmsg(user, msg)
+                       , quitevent(ServerInstance->GetRFCEvents().quit, quitmsg)
+                       , operquitmsg(user, opermsg)
+                       , operquitevent(ServerInstance->GetRFCEvents().quit, operquitmsg)
+               {
+                       user->ForEachNeighbor(*this, false);
+               }
+       };
+
+       void CheckPingTimeout(LocalUser* user)
        {
-               ServerInstance->Logs->Log("USERS", DEFAULT,"*** WTF *** Duplicated UUID! -- Crack smoking monkeys have been unleashed.");
-               ServerInstance->SNO->WriteToSnoMask('a', "WARNING *** Duplicate UUID allocated!");
-               return;
-       }
-       UserIOHandler* eh = &New->eh;
+               // Check if it is time to ping the user yet.
+               if (ServerInstance->Time() < user->nextping)
+                       return;
 
-       /* Give each of the modules an attempt to hook the user for I/O */
-       FOREACH_MOD(I_OnHookIO, OnHookIO(eh, via));
+               // This user didn't answer the last ping, remove them.
+               if (!user->lastping)
+               {
+                       time_t secs = ServerInstance->Time() - (user->nextping - user->MyClass->GetPingTime());
+                       const std::string message = "Ping timeout: " + ConvToStr(secs) + (secs != 1 ? " seconds" : " second");
+                       ServerInstance->Users.QuitUser(user, message);
+                       return;
+               }
 
-       if (eh->GetIOHook())
+               // Send a ping to the client.
+               ClientProtocol::Messages::Ping ping;
+               user->Send(ServerInstance->GetRFCEvents().ping, ping);
+               user->lastping = 0;
+               user->nextping = ServerInstance->Time() + user->MyClass->GetPingTime();
+       }
+
+       void CheckRegistrationTimeout(LocalUser* user)
        {
-               try
+               if (user->GetClass() && (ServerInstance->Time() > (user->signon + user->GetClass()->GetRegTimeout())))
                {
-                       eh->GetIOHook()->OnStreamSocketAccept(eh, client, server);
+                       // Either the user did not send NICK/USER or a module blocked registration in
+                       // OnCheckReady until the client timed out.
+                       ServerInstance->Users.QuitUser(user, "Registration timeout");
                }
-               catch (CoreException& modexcept)
+       }
+
+       void CheckModulesReady(LocalUser* user)
+       {
+               ModResult res;
+               FIRST_MOD_RESULT(OnCheckReady, res, (user));
+               if (res == MOD_RES_PASSTHRU)
                {
-                       ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+                       // User has sent NICK/USER and modules are ready.
+                       user->FullConnect();
+                       return;
                }
+
+               // If the user has been quit in OnCheckReady then we shouldn't quit
+               // them again for having a registration timeout.
+               if (!user->quitting)
+                       CheckRegistrationTimeout(user);
        }
+}
 
-       ServerInstance->Logs->Log("USERS", DEBUG,"New user fd: %d", socket);
+UserManager::UserManager()
+       : already_sent_id(0)
+       , unregistered_count(0)
+{
+}
 
-       this->unregistered_count++;
+UserManager::~UserManager()
+{
+       for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); ++i)
+       {
+               delete i->second;
+       }
+}
 
-       /* The users default nick is their UUID */
-       New->nick = New->uuid;
-       (*(this->clientlist))[New->nick] = New;
+void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+{
+       // 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;
 
-       New->registered = REG_NONE;
-       New->signon = ServerInstance->Time();
-       New->lastping = 1;
+       ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New user fd: %d", socket);
 
-       ServerInstance->Users->AddLocalClone(New);
-       ServerInstance->Users->AddGlobalClone(New);
+       this->unregistered_count++;
+       this->clientlist[New->nick] = New;
+       this->AddClone(New);
+       this->local_users.push_front(New);
+       FOREACH_MOD(OnUserInit, (New));
 
-       New->localuseriter = this->local_users.insert(local_users.end(), New);
-       local_count++;
+       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;
+       }
 
-       if ((this->local_users.size() > ServerInstance->Config->SoftLimit) || (this->local_users.size() >= (unsigned int)ServerInstance->SE->GetMaxFds()))
+       // 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;
+
+               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)
        {
                ServerInstance->SNO->WriteToSnoMask('a', "Warning: softlimit value has been reached: %d clients", ServerInstance->Config->SoftLimit);
                this->QuitUser(New,"No more connections allowed");
                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
-        */
-       New->CheckClass();
+       // If the user doesn't have an acceptable connect class CheckClass() quits them
+       New->CheckClass(ServerInstance->Config->CCOnConnect);
        if (New->quitting)
                return;
 
@@ -109,24 +171,25 @@ 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", DEBUG, std::string("BanCache: Positive hit for ") + New->GetIPString());
-                       if (!ServerInstance->Config->MoronBanner.empty())
-                               New->WriteServ("NOTICE %s :*** %s", New->nick.c_str(), ServerInstance->Config->MoronBanner.c_str());
+                       ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Positive hit for " + New->GetIPString());
+                       if (!ServerInstance->Config->XLineMessage.empty())
+                               New->WriteNumeric(ERR_YOUREBANNEDCREEP, ServerInstance->Config->XLineMessage);
 
                        if (ServerInstance->Config->HideBans)
-                               this->QuitUser(New, b->Type + "-Lined", b->Reason.c_str());
+                               this->QuitUser(New, b->Type + "-lined", &b->Reason);
                        else
                                this->QuitUser(New, b->Reason);
                        return;
                }
                else
                {
-                       ServerInstance->Logs->Log("BANCACHE", DEBUG, std::string("BanCache: Negative hit for ") + New->GetIPString());
+                       ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Negative hit for " + New->GetIPString());
                }
        }
        else
@@ -143,277 +206,185 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs
                }
        }
 
-       if (!ServerInstance->SE->AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
-       {
-               ServerInstance->Logs->Log("USERS", DEBUG,"Internal error on new connection");
-               this->QuitUser(New, "Internal error handling connection");
-               return;
-       }
-
-       /* NOTE: even if dns lookups are *off*, we still need to display this.
-        * BOPM and other stuff requires it.
-        */
-       New->WriteServ("NOTICE Auth :*** Looking up your hostname...");
        if (ServerInstance->Config->RawLog)
-               New->WriteServ("NOTICE Auth :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
-
-       FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(New));
-       if (New->quitting)
-               return;
-
-       FOREACH_MOD(I_OnUserInit,OnUserInit(New));
+               New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
 
-       if (ServerInstance->Config->NoUserDns)
-       {
-               New->WriteServ("NOTICE %s :*** Skipping host resolution (disabled by server administrator)", New->nick.c_str());
-               New->dns_done = true;
-       }
-       else
-       {
-               New->StartDNSLookup();
-       }
+       FOREACH_MOD(OnSetUserIP, (New));
+       if (!New->quitting)
+               FOREACH_MOD(OnUserPostInit, (New));
 }
 
-void UserManager::QuitUser(User *user, const std::string &quitreason, const char* operreason)
+void UserManager::QuitUser(User* user, const std::string& quitreason, const std::string* operreason)
 {
        if (user->quitting)
        {
-               ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
+               ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
                return;
        }
 
        if (IS_SERVER(user))
        {
-               ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
+               ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
                return;
        }
 
        user->quitting = true;
 
-       ServerInstance->Logs->Log("USERS", DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitreason.c_str());
-       user->Write("ERROR :Closing link: (%s@%s) [%s]", user->ident.c_str(), user->host.c_str(), *operreason ? operreason : quitreason.c_str());
+       ServerInstance->Logs->Log("USERS", LOG_DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitreason.c_str());
+       LocalUser* const localuser = IS_LOCAL(user);
+       if (localuser)
+       {
+               ClientProtocol::Messages::Error errormsg(InspIRCd::Format("Closing link: (%s@%s) [%s]", user->ident.c_str(), user->GetRealHost().c_str(), operreason ? operreason->c_str() : quitreason.c_str()));
+               localuser->Send(ServerInstance->GetRFCEvents().error, errormsg);
+       }
 
        std::string reason;
-       std::string oper_reason;
        reason.assign(quitreason, 0, ServerInstance->Config->Limits.MaxQuit);
-       if (operreason && *operreason)
-               oper_reason.assign(operreason, 0, ServerInstance->Config->Limits.MaxQuit);
-       else
-               oper_reason = quitreason;
+       if (!operreason)
+               operreason = &reason;
 
        ServerInstance->GlobalCulls.AddItem(user);
 
        if (user->registered == REG_ALL)
        {
-               FOREACH_MOD(I_OnUserQuit,OnUserQuit(user, reason, oper_reason));
-               user->WriteCommonQuit(reason, oper_reason);
+               FOREACH_MOD(OnUserQuit, (user, reason, *operreason));
+               WriteCommonQuit(user, reason, *operreason);
        }
-
-       if (user->registered != REG_ALL)
-               if (ServerInstance->Users->unregistered_count)
-                       ServerInstance->Users->unregistered_count--;
+       else
+               unregistered_count--;
 
        if (IS_LOCAL(user))
        {
                LocalUser* lu = IS_LOCAL(user);
-               FOREACH_MOD(I_OnUserDisconnect,OnUserDisconnect(lu));
+               FOREACH_MOD(OnUserDisconnect, (lu));
                lu->eh.Close();
-       }
 
-       /*
-        * this must come before the ServerInstance->SNO->WriteToSnoMaskso that it doesnt try to fill their buffer with anything
-        * if they were an oper with +s +qQ.
-        */
-       if (user->registered == REG_ALL)
-       {
-               if (IS_LOCAL(user))
-               {
-                       if (!user->quietquit)
-                       {
-                               ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]",
-                                       user->GetFullRealHost().c_str(), user->GetIPString(), oper_reason.c_str());
-                       }
-               }
-               else
-               {
-                       if ((!ServerInstance->SilentULine(user->server)) && (!user->quietquit))
-                       {
-                               ServerInstance->SNO->WriteToSnoMask('Q',"Client exiting on server %s: %s (%s) [%s]",
-                                       user->server.c_str(), user->GetFullRealHost().c_str(), user->GetIPString(), oper_reason.c_str());
-                       }
-               }
-               user->AddToWhoWas();
+               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);
        }
 
-       user_hash::iterator iter = this->clientlist->find(user->nick);
+       if (!clientlist.erase(user->nick))
+               ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
 
-       if (iter != this->clientlist->end())
-               this->clientlist->erase(iter);
-       else
-               ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
-
-       ServerInstance->Users->uuidlist->erase(user->uuid);
-}
-
-
-void UserManager::AddLocalClone(User *user)
-{
-       local_clones[user->GetCIDRMask()]++;
+       uuidlist.erase(user->uuid);
+       user->PurgeEmptyChannels();
+       user->UnOper();
 }
 
-void UserManager::AddGlobalClone(User *user)
+void UserManager::AddClone(User* user)
 {
-       global_clones[user->GetCIDRMask()]++;
+       CloneCounts& counts = clonemap[user->GetCIDRMask()];
+       counts.global++;
+       if (IS_LOCAL(user))
+               counts.local++;
 }
 
 void UserManager::RemoveCloneCounts(User *user)
 {
-       if (IS_LOCAL(user))
+       CloneMap::iterator it = clonemap.find(user->GetCIDRMask());
+       if (it != clonemap.end())
        {
-               clonemap::iterator x = local_clones.find(user->GetCIDRMask());
-               if (x != local_clones.end())
+               CloneCounts& counts = it->second;
+               counts.global--;
+               if (counts.global == 0)
                {
-                       x->second--;
-                       if (!x->second)
-                       {
-                               local_clones.erase(x);
-                       }
+                       // No more users from this IP, remove entry from the map
+                       clonemap.erase(it);
+                       return;
                }
-       }
 
-       clonemap::iterator y = global_clones.find(user->GetCIDRMask());
-       if (y != global_clones.end())
-       {
-               y->second--;
-               if (!y->second)
-               {
-                       global_clones.erase(y);
-               }
+               if (IS_LOCAL(user))
+                       counts.local--;
        }
 }
 
 void UserManager::RehashCloneCounts()
 {
-       local_clones.clear();
-       global_clones.clear();
+       clonemap.clear();
 
-       const user_hash& hash = *ServerInstance->Users->clientlist;
+       const user_hash& hash = ServerInstance->Users.GetUsers();
        for (user_hash::const_iterator i = hash.begin(); i != hash.end(); ++i)
        {
                User* u = i->second;
-
-               if (IS_LOCAL(u))
-                       AddLocalClone(u);
-               AddGlobalClone(u);
+               AddClone(u);
        }
 }
 
-unsigned long UserManager::GlobalCloneCount(User *user)
+const UserManager::CloneCounts& UserManager::GetCloneCounts(User* user) const
 {
-       clonemap::iterator x = global_clones.find(user->GetCIDRMask());
-       if (x != global_clones.end())
-               return x->second;
+       CloneMap::const_iterator it = clonemap.find(user->GetCIDRMask());
+       if (it != clonemap.end())
+               return it->second;
        else
-               return 0;
-}
-
-unsigned long UserManager::LocalCloneCount(User *user)
-{
-       clonemap::iterator x = local_clones.find(user->GetCIDRMask());
-       if (x != local_clones.end())
-               return x->second;
-       else
-               return 0;
-}
-
-/* this function counts all users connected, wether they are registered or NOT. */
-unsigned int UserManager::UserCount()
-{
-       /*
-        * XXX: Todo:
-        *  As part of this restructuring, move clientlist/etc fields into usermanager.
-        *      -- w00t
-        */
-       return this->clientlist->size();
-}
-
-/* this counts only registered users, so that the percentages in /MAP don't mess up */
-unsigned int UserManager::RegisteredUserCount()
-{
-       return this->clientlist->size() - this->UnregisteredUserCount();
-}
-
-/* return how many users are opered */
-unsigned int UserManager::OperCount()
-{
-       return this->all_opers.size();
-}
-
-/* return how many users are unregistered */
-unsigned int UserManager::UnregisteredUserCount()
-{
-       return this->unregistered_count;
-}
-
-/* return how many local registered users there are */
-unsigned int UserManager::LocalUserCount()
-{
-       /* Doesnt count unregistered clients */
-       return (this->local_count - this->UnregisteredUserCount());
+               return zeroclonecounts;
 }
 
 void UserManager::ServerNoticeAll(const char* text, ...)
 {
-       if (!text)
-               return;
-
-       char textbuffer[MAXBUF];
-       char formatbuffer[MAXBUF];
-       va_list argsPtr;
-       va_start (argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
+       std::string message;
+       VAFORMAT(message, text, text);
+       ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, ServerInstance->Config->ServerName, message, MSG_NOTICE);
+       ClientProtocol::Event msgevent(ServerInstance->GetRFCEvents().privmsg, msg);
 
-       snprintf(formatbuffer,MAXBUF,"NOTICE $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
-
-       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(std::string(formatbuffer));
+               LocalUser* user = *i;
+               user->Send(msgevent);
        }
 }
 
-void UserManager::ServerPrivmsgAll(const char* text, ...)
+/**
+ * This function is called once a second from the mainloop.
+ * It is intended to do background checking on all the users, e.g. do
+ * ping checks, registration timeouts, etc.
+ */
+void UserManager::DoBackgroundUserStuff()
 {
-       if (!text)
-               return;
+       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;
+               ++i;
+
+               if (curr->CommandFloodPenalty || curr->eh.getSendQSize())
+               {
+                       unsigned int rate = curr->MyClass->GetCommandRate();
+                       if (curr->CommandFloodPenalty > rate)
+                               curr->CommandFloodPenalty -= rate;
+                       else
+                               curr->CommandFloodPenalty = 0;
+                       curr->eh.OnDataReady();
+               }
 
-       char textbuffer[MAXBUF];
-       char formatbuffer[MAXBUF];
-       va_list argsPtr;
-       va_start (argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
+               switch (curr->registered)
+               {
+                       case REG_ALL:
+                               CheckPingTimeout(curr);
+                               break;
 
-       snprintf(formatbuffer,MAXBUF,"PRIVMSG $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
+                       case REG_NICKUSER:
+                               CheckModulesReady(curr);
+                               break;
 
-       for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
-       {
-               User* t = *i;
-               t->WriteServ(std::string(formatbuffer));
+                       default:
+                               CheckRegistrationTimeout(curr);
+                               break;
+               }
        }
 }
 
-
-/* return how many users have a given mode e.g. 'a' */
-int UserManager::ModeCount(const char mode)
+already_sent_t UserManager::NextAlreadySentId()
 {
-       int c = 0;
-       for(user_hash::iterator i = clientlist->begin(); i != clientlist->end(); ++i)
+       if (++already_sent_id == 0)
        {
-               User* u = i->second;
-               if (u->modes[mode-65])
-                       c++;
+               // 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 c;
+       return already_sent_id;
 }
diff --git a/src/userprocess.cpp b/src/userprocess.cpp
deleted file mode 100644 (file)
index 69c31f8..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
- *   Copyright (C) 2005-2007 Craig Edwards <craigedwards@brainbox.cc>
- *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *   Copyright (C) 2006 Craig McLure <craig@chatspike.net>
- *
- * 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/>.
- */
-
-
-/* $Core */
-
-#include "inspircd.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "command_parse.h"
-
-void FloodQuitUserHandler::Call(User* current)
-{
-       ServerInstance->Logs->Log("USERS",DEFAULT,"Excess flood from: %s@%s", current->ident.c_str(), current->host.c_str());
-       ServerInstance->SNO->WriteToSnoMask('f',"Excess flood from: %s%s%s@%s",
-                       current->registered == REG_ALL ? current->nick.c_str() : "",
-                       current->registered == REG_ALL ? "!" : "", current->ident.c_str(), current->host.c_str());
-       ServerInstance->Users->QuitUser(current, "Excess flood");
-
-       if (current->registered != REG_ALL)
-       {
-               ZLine* zl = new ZLine(ServerInstance->Time(), 0, ServerInstance->Config->ServerName, "Flood from unregistered connection", current->GetIPString());
-               if (ServerInstance->XLines->AddLine(zl,NULL))
-                       ServerInstance->XLines->ApplyLines();
-               else
-                       delete zl;
-       }
-}
-
-/**
- * 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.
- */
-void InspIRCd::DoBackgroundUserStuff()
-{
-       /*
-        * loop over all local users..
-        */
-       LocalUserList::reverse_iterator count2 = this->Users->local_users.rbegin();
-       while (count2 != this->Users->local_users.rend())
-       {
-               LocalUser *curr = *count2;
-               count2++;
-
-               if (curr->quitting)
-                       continue;
-
-               if (curr->CommandFloodPenalty || curr->eh.getSendQSize())
-               {
-                       unsigned int rate = curr->MyClass->GetCommandRate();
-                       if (curr->CommandFloodPenalty > rate)
-                               curr->CommandFloodPenalty -= rate;
-                       else
-                               curr->CommandFloodPenalty = 0;
-                       curr->eh.OnDataReady();
-               }
-
-               switch (curr->registered)
-               {
-                       case REG_ALL:
-                               if (Time() > curr->nping)
-                               {
-                                       // This user didn't answer the last ping, remove them
-                                       if (!curr->lastping)
-                                       {
-                                               time_t time = this->Time() - (curr->nping - curr->MyClass->GetPingTime());
-                                               char message[MAXBUF];
-                                               snprintf(message, MAXBUF, "Ping timeout: %ld second%s", (long)time, time > 1 ? "s" : "");
-                                               curr->lastping = 1;
-                                               curr->nping = Time() + curr->MyClass->GetPingTime();
-                                               this->Users->QuitUser(curr, message);
-                                               continue;
-                                       }
-
-                                       curr->Write("PING :%s",this->Config->ServerName.c_str());
-                                       curr->lastping = 0;
-                                       curr->nping = Time()  +curr->MyClass->GetPingTime();
-                               }
-                               break;
-                       case REG_NICKUSER:
-                               if (AllModulesReportReady(curr) && curr->dns_done)
-                               {
-                                       /* User has sent NICK/USER, modules are okay, DNS finished. */
-                                       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 && curr->MyClass && (Time() > (curr->signon + curr->MyClass->GetRegTimeout())))
-               {
-                       /*
-                        * registration timeout -- didnt send USER/NICK/HOST
-                        * in the time specified in their connection class.
-                        */
-                       this->Users->QuitUser(curr, "Registration timeout");
-                       continue;
-               }
-       }
-}
-
index ac87f1187992cc943d1dab44cc9de778473ffcdb..aaaa06f19293bf989469a88836ff029e43e61156 100644 (file)
 
 
 #include "inspircd.h"
-#include <stdarg.h>
-#include "socketengine.h"
 #include "xline.h"
-#include "bancache.h"
-#include "commands/cmd_whowas.h"
 
-already_sent_t LocalUser::already_sent_id = 0;
-
-std::string User::ProcessNoticeMasks(const char *sm)
-{
-       bool adding = true, oldadding = false;
-       const char *c = sm;
-       std::string output;
-
-       while (c && *c)
-       {
-               switch (*c)
-               {
-                       case '+':
-                               adding = true;
-                       break;
-                       case '-':
-                               adding = false;
-                       break;
-                       case '*':
-                               for (unsigned char d = 'a'; d <= 'z'; d++)
-                               {
-                                       if (!ServerInstance->SNO->masks[d - 'a'].Description.empty())
-                                       {
-                                               if ((!IsNoticeMaskSet(d) && adding) || (IsNoticeMaskSet(d) && !adding))
-                                               {
-                                                       if ((oldadding != adding) || (!output.length()))
-                                                               output += (adding ? '+' : '-');
-
-                                                       this->SetNoticeMask(d, adding);
-
-                                                       output += d;
-                                               }
-                                               oldadding = adding;
-                                               char u = toupper(d);
-                                               if ((!IsNoticeMaskSet(u) && adding) || (IsNoticeMaskSet(u) && !adding))
-                                               {
-                                                       if ((oldadding != adding) || (!output.length()))
-                                                               output += (adding ? '+' : '-');
-
-                                                       this->SetNoticeMask(u, adding);
-
-                                                       output += u;
-                                               }
-                                               oldadding = adding;
-                                       }
-                               }
-                       break;
-                       default:
-                               if (isalpha(*c))
-                               {
-                                       if ((!IsNoticeMaskSet(*c) && adding) || (IsNoticeMaskSet(*c) && !adding))
-                                       {
-                                               if ((oldadding != adding) || (!output.length()))
-                                                       output += (adding ? '+' : '-');
-
-                                               this->SetNoticeMask(*c, adding);
-
-                                               output += *c;
-                                               oldadding = adding;
-                                       }
-                               }
-                               else
-                                       this->WriteNumeric(ERR_UNKNOWNSNOMASK, "%s %c :is unknown snomask char to me", this->nick.c_str(), *c);
-
-                       break;
-               }
-
-               c++;
-       }
-
-       std::string s = this->FormatNoticeMasks();
-       if (s.length() == 0)
-       {
-               this->modes[UM_SNOMASK] = false;
-       }
-
-       return output;
-}
-
-void LocalUser::StartDNSLookup()
-{
-       try
-       {
-               bool cached = false;
-               const char* sip = this->GetIPString();
-               UserResolver *res_reverse;
-
-               QueryType resolvtype = this->client_sa.sa.sa_family == AF_INET6 ? DNS_QUERY_PTR6 : DNS_QUERY_PTR4;
-               res_reverse = new UserResolver(this, sip, resolvtype, cached);
-
-               ServerInstance->AddResolver(res_reverse, cached);
-       }
-       catch (CoreException& e)
-       {
-               ServerInstance->Logs->Log("USERS", DEBUG,"Error in resolver: %s",e.GetReason());
-               dns_done = true;
-               ServerInstance->stats->statsDnsBad++;
-       }
-}
+ClientProtocol::MessageList LocalUser::sendmsglist;
 
 bool User::IsNoticeMaskSet(unsigned char sm)
 {
@@ -137,104 +35,85 @@ bool User::IsNoticeMaskSet(unsigned char sm)
        return (snomasks[sm-65]);
 }
 
-void User::SetNoticeMask(unsigned char sm, bool value)
-{
-       if (!isalpha(sm))
-               return;
-       snomasks[sm-65] = value;
-}
-
-const char* User::FormatNoticeMasks()
+bool User::IsModeSet(unsigned char m) const
 {
-       static char data[MAXBUF];
-       int offset = 0;
-
-       for (int n = 0; n < 64; n++)
-       {
-               if (snomasks[n])
-                       data[offset++] = n+65;
-       }
-
-       data[offset] = 0;
-       return data;
+       ModeHandler* mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
+       return (mh && modes[mh->GetId()]);
 }
 
-bool User::IsModeSet(unsigned char m)
+std::string User::GetModeLetters(bool includeparams) const
 {
-       if (!isalpha(m))
-               return false;
-       return (modes[m-65]);
-}
-
-void User::SetMode(unsigned char m, bool value)
-{
-       if (!isalpha(m))
-               return;
-       modes[m-65] = value;
-}
-
-const char* User::FormatModes(bool showparameters)
-{
-       static char data[MAXBUF];
+       std::string ret(1, '+');
        std::string params;
-       int offset = 0;
 
-       for (unsigned char n = 0; n < 64; n++)
+       for (unsigned char i = 'A'; i <= 'z'; i++)
        {
-               if (modes[n])
+               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[offset++] = n + 65;
-                       ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_USER);
-                       if (showparameters && mh && 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[offset] = 0;
-       strlcat(data, params.c_str(), MAXBUF);
-       return data;
+
+       ret += params;
+       return ret;
 }
 
-User::User(const std::string &uid, const std::string& sid, int type)
-       : uuid(uid), server(sid), usertype(type)
+User::User(const std::string& 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 = idle_lastmsg = 0;
-       registered = 0;
-       quietquit = quitting = exempt = dns_done = false;
-       quitting_sendq = false;
        client_sa.sa.sa_family = AF_UNSPEC;
 
-       ServerInstance->Logs->Log("USERS", DEBUG, "New UUID for user: %s", uuid.c_str());
+       ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New UUID for user: %s", uuid.c_str());
 
-       user_hash::iterator finduuid = ServerInstance->Users->uuidlist->find(uuid);
-       if (finduuid == ServerInstance->Users->uuidlist->end())
-               (*ServerInstance->Users->uuidlist)[uuid] = this;
-       else
-               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->GetUID(), ServerInstance->Config->ServerName, USERTYPE_LOCAL), eh(this),
-       localuseriter(ServerInstance->Users->local_users.end()),
-       bytes_in(0), bytes_out(0), cmds_in(0), cmds_out(0), nping(0), CommandFloodPenalty(0),
-       already_sent(0)
-{
+       : User(ServerInstance->UIDGen.GetUID(), ServerInstance->FakeClient->server, USERTYPE_LOCAL)
+       , eh(this)
+       , serializer(NULL)
+       , bytes_in(0)
+       , bytes_out(0)
+       , cmds_in(0)
+       , cmds_out(0)
+       , quitting_sendq(false)
+       , lastping(true)
+       , exempt(false)
+       , nextping(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));
-       dhost = host = GetIPString();
+       ChangeRealHost(GetIPString(), true);
 }
 
 User::~User()
 {
-       if (ServerInstance->Users->uuidlist->find(uuid) != ServerInstance->Users->uuidlist->end())
-               ServerInstance->Logs->Log("USERS", DEFAULT, "User destructor for %s called without cull", uuid.c_str());
 }
 
 const std::string& User::MakeHost()
@@ -242,18 +121,8 @@ const std::string& User::MakeHost()
        if (!this->cached_makehost.empty())
                return this->cached_makehost;
 
-       char nhost[MAXBUF];
-       /* This is much faster than snprintf */
-       char* t = nhost;
-       for(const char* n = ident.c_str(); *n; n++)
-               *t++ = *n;
-       *t++ = '@';
-       for(const char* n = host.c_str(); *n; n++)
-               *t++ = *n;
-       *t = 0;
-
-       this->cached_makehost.assign(nhost);
-
+       // XXX: Is there really a need to cache this?
+       this->cached_makehost = ident + "@" + GetRealHost();
        return this->cached_makehost;
 }
 
@@ -262,18 +131,8 @@ const std::string& User::MakeHostIP()
        if (!this->cached_hostip.empty())
                return this->cached_hostip;
 
-       char ihost[MAXBUF];
-       /* This is much faster than snprintf */
-       char* t = ihost;
-       for(const char* n = ident.c_str(); *n; n++)
-               *t++ = *n;
-       *t++ = '@';
-       for(const char* n = this->GetIPString(); *n; n++)
-               *t++ = *n;
-       *t = 0;
-
-       this->cached_hostip = ihost;
-
+       // XXX: Is there really a need to cache this?
+       this->cached_hostip = ident + "@" + this->GetIPString();
        return this->cached_hostip;
 }
 
@@ -282,111 +141,36 @@ const std::string& User::GetFullHost()
        if (!this->cached_fullhost.empty())
                return this->cached_fullhost;
 
-       char result[MAXBUF];
-       char* t = result;
-       for(const char* n = nick.c_str(); *n; n++)
-               *t++ = *n;
-       *t++ = '!';
-       for(const char* n = ident.c_str(); *n; n++)
-               *t++ = *n;
-       *t++ = '@';
-       for(const char* n = dhost.c_str(); *n; n++)
-               *t++ = *n;
-       *t = 0;
-
-       this->cached_fullhost = result;
-
+       // XXX: Is there really a need to cache this?
+       this->cached_fullhost = nick + "!" + ident + "@" + GetDisplayedHost();
        return this->cached_fullhost;
 }
 
-char* User::MakeWildHost()
-{
-       static char nresult[MAXBUF];
-       char* t = nresult;
-       *t++ = '*';     *t++ = '!';
-       *t++ = '*';     *t++ = '@';
-       for(const char* n = dhost.c_str(); *n; n++)
-               *t++ = *n;
-       *t = 0;
-       return nresult;
-}
-
 const std::string& User::GetFullRealHost()
 {
        if (!this->cached_fullrealhost.empty())
                return this->cached_fullrealhost;
 
-       char fresult[MAXBUF];
-       char* t = fresult;
-       for(const char* n = nick.c_str(); *n; n++)
-               *t++ = *n;
-       *t++ = '!';
-       for(const char* n = ident.c_str(); *n; n++)
-               *t++ = *n;
-       *t++ = '@';
-       for(const char* n = host.c_str(); *n; n++)
-               *t++ = *n;
-       *t = 0;
-
-       this->cached_fullrealhost = fresult;
-
+       // XXX: Is there really a need to cache this?
+       this->cached_fullrealhost = nick + "!" + ident + "@" + GetRealHost();
        return this->cached_fullrealhost;
 }
 
-bool LocalUser::IsInvited(const irc::string &channel)
-{
-       Channel* chan = ServerInstance->FindChan(channel.c_str());
-       if (!chan)
-               return false;
-
-       return (Invitation::Find(chan, this) != NULL);
-}
-
-InviteList& LocalUser::GetInviteList()
-{
-       RemoveExpiredInvites();
-       return invites;
-}
-
-void LocalUser::InviteTo(const irc::string &channel, time_t invtimeout)
-{
-       Channel* chan = ServerInstance->FindChan(channel.c_str());
-       if (chan)
-               Invitation::Create(chan, this, invtimeout);
-}
-
-void LocalUser::RemoveInvite(const irc::string &channel)
-{
-       Channel* chan = ServerInstance->FindChan(channel.c_str());
-       if (chan)
-       {
-               Invitation* inv = Invitation::Find(chan, this);
-               if (inv)
-               {
-                       inv->cull();
-                       delete inv;
-               }
-       }
-}
-
-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 (!IS_OPER(this))
+       if (!this->IsOper())
                return false;
 
-       if (mode < 'A' || mode > ('A' + 64)) return false;
+       const unsigned char mode = mh->GetModeChar();
+       if (!ModeParser::IsModeChar(mode))
+               return false;
 
-       return ((type == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
+       return ((mh->GetModeType() == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
 
 }
 /*
@@ -396,53 +180,33 @@ bool LocalUser::HasModePermission(unsigned char mode, ModeType type)
  * allowing remote kills, etc - but if they have access to the src, they most likely have
  * access to the conf - so it's an end to a means either way.
  */
-bool User::HasPermission(const std::string&)
+bool User::HasCommandPermission(const std::string&)
 {
        return true;
 }
 
-bool LocalUser::HasPermission(const std::string &command)
+bool LocalUser::HasCommandPermission(const std::string& command)
 {
        // are they even an oper at all?
-       if (!IS_OPER(this))
+       if (!this->IsOper())
        {
                return false;
        }
 
-       if (oper->AllowedOperCommands.find(command) != oper->AllowedOperCommands.end())
-               return true;
-       else if (oper->AllowedOperCommands.find("*") != oper->AllowedOperCommands.end())
-               return true;
-
-       return false;
+       return oper->AllowedOperCommands.Contains(command);
 }
 
-bool User::HasPrivPermission(const std::string &privstr, bool noisy)
+bool User::HasPrivPermission(const std::string& privstr)
 {
        return true;
 }
 
-bool LocalUser::HasPrivPermission(const std::string &privstr, bool noisy)
+bool LocalUser::HasPrivPermission(const std::string& privstr)
 {
-       if (!IS_OPER(this))
-       {
-               if (noisy)
-                       this->WriteServ("NOTICE %s :You are not an oper", this->nick.c_str());
+       if (!this->IsOper())
                return false;
-       }
 
-       if (oper->AllowedPrivs.find(privstr) != oper->AllowedPrivs.end())
-       {
-               return true;
-       }
-       else if (oper->AllowedPrivs.find("*") != oper->AllowedPrivs.end())
-       {
-               return true;
-       }
-
-       if (noisy)
-               this->WriteServ("NOTICE %s :Oper type %s does not have access to priv %s", this->nick.c_str(), oper->NameStr(), privstr.c_str());
-       return false;
+       return oper->AllowedPrivs.Contains(privstr);
 }
 
 void UserIOHandler::OnDataReady()
@@ -457,49 +221,68 @@ void UserIOHandler::OnDataReady()
                        user->nick.c_str(), (unsigned long)recvq.length(), user->MyClass->GetRecvqMax());
                return;
        }
+
        unsigned long sendqmax = ULONG_MAX;
        if (!user->HasPrivPermission("users/flood/increased-buffers"))
                sendqmax = user->MyClass->GetSendqSoftMax();
+
        unsigned long penaltymax = ULONG_MAX;
        if (!user->HasPrivPermission("users/flood/no-fakelag"))
                penaltymax = user->MyClass->GetPenaltyThreshold() * 1000;
 
+       // The cleaned message sent by the user or empty if not found yet.
+       std::string line;
+
+       // The position of the most \n character or npos if not found yet.
+       std::string::size_type eolpos;
+
+       // The position within the recvq of the current character.
+       std::string::size_type qpos;
+
        while (user->CommandFloodPenalty < penaltymax && getSendQSize() < sendqmax)
        {
-               std::string line;
-               line.reserve(MAXBUF);
-               std::string::size_type qpos = 0;
-               while (qpos < recvq.length())
+               // Check the newly received data for an EOL.
+               eolpos = recvq.find('\n', checked_until);
+               if (eolpos == std::string::npos)
                {
-                       char c = recvq[qpos++];
+                       checked_until = recvq.length();
+                       return;
+               }
+
+               // We've found a line! Clean it up and move it to the line buffer.
+               line.reserve(eolpos);
+               for (qpos = 0; qpos < eolpos; ++qpos)
+               {
+                       char c = recvq[qpos];
                        switch (c)
                        {
-                       case '\0':
-                               c = ' ';
-                               break;
-                       case '\r':
-                               continue;
-                       case '\n':
-                               goto eol_found;
+                               case '\0':
+                                       c = ' ';
+                                       break;
+                               case '\r':
+                                       continue;
                        }
-                       if (line.length() < MAXBUF - 2)
-                               line.push_back(c);
+
+                       line.push_back(c);
                }
-               // if we got here, the recvq ran out before we found a newline
-               return;
-eol_found:
+
                // just found a newline. Terminate the string, and pull it out of recvq
-               recvq = recvq.substr(qpos);
+               recvq.erase(0, eolpos + 1);
+               checked_until = 0;
 
                // 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(user, line);
                if (user->quitting)
                        return;
+
+               // clear() does not reclaim memory associated with the string, so our .reserve() call is safe
+               line.clear();
        }
+
        if (user->CommandFloodPenalty >= penaltymax && !user->MyClass->fakelag)
                ServerInstance->Users->QuitUser(user, "Excess Flood");
 }
@@ -522,6 +305,13 @@ void UserIOHandler::AddWriteBuf(const std::string &data)
        WriteData(data);
 }
 
+bool UserIOHandler::OnSetEndPoint(const irc::sockets::sockaddrs& server, const irc::sockets::sockaddrs& client)
+{
+       memcpy(&user->server_sa, &server, sizeof(irc::sockets::sockaddrs));
+       user->SetClientIP(client);
+       return !user->quitting;
+}
+
 void UserIOHandler::OnError(BufferedSocketError)
 {
        ServerInstance->Users->QuitUser(user, getError());
@@ -531,9 +321,8 @@ CullResult User::cull()
 {
        if (!quitting)
                ServerInstance->Users->QuitUser(this, "Culled without QuitUser");
-       PurgeEmptyChannels();
 
-       if (client_sa.sa.sa_family != AF_UNSPEC)
+       if (client_sa.family() != AF_UNSPEC)
                ServerInstance->Users->RemoveCloneCounts(this);
 
        return Extensible::cull();
@@ -541,18 +330,6 @@ CullResult User::cull()
 
 CullResult LocalUser::cull()
 {
-       // The iterator is initialized to local_users.end() in the constructor. It is
-       // overwritten in UserManager::AddUser() with the real iterator so this check
-       // is only a precaution currently.
-       if (localuseriter != ServerInstance->Users->local_users.end())
-       {
-               ServerInstance->Users->local_count--;
-               ServerInstance->Users->local_users.erase(localuseriter);
-       }
-       else
-               ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: LocalUserIter does not point to a valid entry for " + this->nick);
-
-       ClearInvites();
        eh.cull();
        return User::cull();
 }
@@ -561,54 +338,53 @@ CullResult FakeUser::cull()
 {
        // Fake users don't quit, they just get culled.
        quitting = true;
-       ServerInstance->Users->clientlist->erase(nick);
-       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();
 }
 
 void User::Oper(OperInfo* info)
 {
-       if (this->IsModeSet('o'))
-               this->UnOper();
-
-       this->modes[UM_OPERATOR] = 1;
+       ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
+       if (opermh)
+       {
+               if (this->IsModeSet(opermh))
+                       this->UnOper();
+               this->SetMode(opermh, true);
+       }
        this->oper = info;
-       this->WriteServ("MODE %s :+o", this->nick.c_str());
-       FOREACH_MOD(I_OnOper, OnOper(this, info->name));
+
+       LocalUser* localuser = IS_LOCAL(this);
+       if (localuser)
+       {
+               Modes::ChangeList changelist;
+               changelist.push_add(opermh);
+               ClientProtocol::Events::Mode modemsg(ServerInstance->FakeClient, NULL, localuser, changelist);
+               localuser->Send(modemsg);
+       }
+
+       FOREACH_MOD(OnOper, (this, info->name));
 
        std::string opername;
        if (info->oper_block)
                opername = info->oper_block->getString("name");
 
-       if (IS_LOCAL(this))
-       {
-               LocalUser* l = IS_LOCAL(this);
-               std::string vhost = oper->getConfig("vhost");
-               if (!vhost.empty())
-                       l->ChangeDisplayedHost(vhost.c_str());
-               std::string opClass = oper->getConfig("class");
-               if (!opClass.empty())
-                       l->SetClass(opClass);
-       }
+       ServerInstance->SNO->WriteToSnoMask('o', "%s (%s@%s) is now a server operator of type %s (using oper '%s')",
+               nick.c_str(), ident.c_str(), GetRealHost().c_str(), oper->name.c_str(), opername.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->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->NameStr(), opername.c_str());
-       this->WriteNumeric(381, "%s :You are now %s %s", nick.c_str(), strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->NameStr());
-
-       ServerInstance->Logs->Log("OPER", DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->NameStr());
        ServerInstance->Users->all_opers.push_back(this);
 
        // Expand permissions from config for faster lookup
-       if (IS_LOCAL(this))
+       if (localuser)
                oper->init();
 
-       FOREACH_MOD(I_OnPostOper,OnPostOper(this, oper->name, opername));
+       FOREACH_MOD(OnPostOper, (this, oper->name, opername));
 }
 
 void OperInfo::init()
 {
-       AllowedOperCommands.clear();
-       AllowedPrivs.clear();
+       AllowedOperCommands.Clear();
+       AllowedPrivs.Clear();
        AllowedUserModes.reset();
        AllowedChanModes.reset();
        AllowedUserModes['o' - 'A'] = true; // Call me paranoid if you want.
@@ -616,19 +392,9 @@ void OperInfo::init()
        for(std::vector<reference<ConfigTag> >::iterator iter = class_blocks.begin(); iter != class_blocks.end(); ++iter)
        {
                ConfigTag* tag = *iter;
-               std::string mycmd, mypriv;
-               /* Process commands */
-               irc::spacesepstream CommandList(tag->getString("commands"));
-               while (CommandList.GetToken(mycmd))
-               {
-                       AllowedOperCommands.insert(mycmd);
-               }
 
-               irc::spacesepstream PrivList(tag->getString("privs"));
-               while (PrivList.GetToken(mypriv))
-               {
-                       AllowedPrivs.insert(mypriv);
-               }
+               AllowedOperCommands.AddList(tag->getString("commands"));
+               AllowedPrivs.AddList(tag->getString("privs"));
 
                std::string modes = tag->getString("usermodes");
                for (std::string::const_iterator c = modes.begin(); c != modes.end(); ++c)
@@ -660,7 +426,7 @@ void OperInfo::init()
 
 void User::UnOper()
 {
-       if (!IS_OPER(this))
+       if (!this->IsOper())
                return;
 
        /*
@@ -672,45 +438,30 @@ 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);
        }
 
+       ServerInstance->Modes->Process(this, NULL, this, changelist);
 
-       std::vector<std::string> parameters;
-       parameters.push_back(this->nick);
-       parameters.push_back(moderemove);
-
-       ServerInstance->Parser->CallHandler("MODE", parameters, this);
-
-       /* remove the user from the oper list. Will remove multiple entries as a safeguard against bug #404 */
-       ServerInstance->Users->all_opers.remove(this);
+       // Remove the user from the oper list
+       stdalgo::vector::swaperase(ServerInstance->Users->all_opers, this);
 
-       this->modes[UM_OPERATOR] = 0;
-       FOREACH_MOD(I_OnPostDeoper, OnPostDeoper(this));
-}
-
-/* adds or updates an entry in the whowas list */
-void User::AddToWhoWas()
-{
-       Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
-       if (whowas)
-       {
-               WhowasRequest req(NULL, whowas, WhowasRequest::WHOWAS_ADD);
-               req.user = this;
-               req.Send();
-       }
+       ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
+       if (opermh)
+               this->SetMode(opermh, false);
+       FOREACH_MOD(OnPostDeoper, (this));
 }
 
 /*
  * Check class restrictions
  */
-void LocalUser::CheckClass()
+void LocalUser::CheckClass(bool clone_count)
 {
        ConnectClass* a = this->MyClass;
 
@@ -724,25 +475,29 @@ void LocalUser::CheckClass()
                ServerInstance->Users->QuitUser(this, a->config->getString("reason", "Unauthorised connection"));
                return;
        }
-       else if ((a->GetMaxLocal()) && (ServerInstance->Users->LocalCloneCount(this) > a->GetMaxLocal()))
+       else if (clone_count)
        {
-               ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)");
-               if (a->maxconnwarn)
-                       ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString());
-               return;
-       }
-       else if ((a->GetMaxGlobal()) && (ServerInstance->Users->GlobalCloneCount(this) > a->GetMaxGlobal()))
-       {
-               ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)");
-               if (a->maxconnwarn)
-                       ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString());
-               return;
+               const UserManager::CloneCounts& clonecounts = ServerInstance->Users->GetCloneCounts(this);
+               if ((a->GetMaxLocal()) && (clonecounts.local > a->GetMaxLocal()))
+               {
+                       ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)");
+                       if (a->maxconnwarn)
+                               ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString().c_str());
+                       return;
+               }
+               else if ((a->GetMaxGlobal()) && (clonecounts.global > a->GetMaxGlobal()))
+               {
+                       ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)");
+                       if (a->maxconnwarn)
+                               ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString().c_str());
+                       return;
+               }
        }
 
-       this->nping = ServerInstance->Time() + a->GetPingTime() + ServerInstance->Config->dns_timeout;
+       this->nextping = ServerInstance->Time() + a->GetPingTime();
 }
 
-bool User::CheckLines(bool doZline)
+bool LocalUser::CheckLines(bool doZline)
 {
        const char* check[] = { "G" , "K", (doZline) ? "Z" : NULL, NULL };
 
@@ -765,7 +520,7 @@ bool User::CheckLines(bool doZline)
 
 void LocalUser::FullConnect()
 {
-       ServerInstance->stats->statsConnects++;
+       ServerInstance->stats.Connects++;
        this->idle_lastmsg = ServerInstance->Time();
 
        /*
@@ -782,55 +537,23 @@ void LocalUser::FullConnect()
        if (quitting)
                return;
 
-       if (ServerInstance->Config->WelcomeNotice)
-               this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network.c_str());
-       this->WriteNumeric(RPL_WELCOME, "%s :Welcome to the %s IRC Network %s",this->nick.c_str(), ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str());
-       this->WriteNumeric(RPL_YOURHOSTIS, "%s :Your host is %s, running version %s",this->nick.c_str(),ServerInstance->Config->ServerName.c_str(),BRANCH);
-       this->WriteNumeric(RPL_SERVERCREATED, "%s :This server was created %s %s", this->nick.c_str(), __TIME__, __DATE__);
-
-       std::string umlist = ServerInstance->Modes->UserModeList();
-       std::string cmlist = ServerInstance->Modes->ChannelModeList();
-       std::string pmlist = ServerInstance->Modes->ParaModeList();
-       this->WriteNumeric(RPL_SERVERVERSION, "%s %s %s %s %s %s", this->nick.c_str(), ServerInstance->Config->ServerName.c_str(), BRANCH, umlist.c_str(), cmlist.c_str(), pmlist.c_str());
-
-       ServerInstance->Config->Send005(this);
-       this->WriteNumeric(RPL_YOURUUID, "%s %s :your unique ID", this->nick.c_str(), this->uuid.c_str());
-
-       /* Now registered */
-       if (ServerInstance->Users->unregistered_count)
-               ServerInstance->Users->unregistered_count--;
-
-       /* Trigger MOTD and LUSERS output, give modules a chance too */
-       ModResult MOD_RESULT;
-       std::string command("MOTD");
-       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);
-
-       MOD_RESULT = MOD_RES_PASSTHRU;
-       command = "LUSERS";
-       FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
-       if (!MOD_RESULT)
-               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());
-
        /*
         * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
         * for a user that doesn't exist yet.
         */
-       FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));
+       FOREACH_MOD(OnUserConnect, (this));
 
+       /* Now registered */
+       if (ServerInstance->Users->unregistered_count)
+               ServerInstance->Users->unregistered_count--;
        this->registered = REG_ALL;
 
-       FOREACH_MOD(I_OnPostConnect,OnPostConnect(this));
+       FOREACH_MOD(OnPostConnect, (this));
 
        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(), this->fullname.c_str());
-       ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCache: Adding NEGATIVE hit for %s", this->GetIPString());
-       ServerInstance->BanCache->AddHit(this->GetIPString(), "", "");
+               this->server_sa.port(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->GetRealName().c_str());
+       ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding NEGATIVE hit for " + this->GetIPString());
+       ServerInstance->BanCache.AddHit(this->GetIPString(), "", "");
        // reset the flood penalty (which could have been raised due to things like auto +x)
        CommandFloodPenalty = 0;
 }
@@ -845,72 +568,25 @@ void User::InvalidateCache()
        cached_fullrealhost.clear();
 }
 
-bool User::ChangeNick(const std::string& newnick, bool force)
+bool User::ChangeNick(const std::string& newnick, time_t newts)
 {
        if (quitting)
        {
-               ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
+               ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
                return false;
        }
 
-       ModResult MOD_RESULT;
-
-       if (force)
-               ServerInstance->NICKForced.set(this, 1);
-       FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (this, newnick));
-       ServerInstance->NICKForced.set(this, 0);
-
-       if (MOD_RESULT == MOD_RES_DENY)
-       {
-               ServerInstance->stats->statsCollisions++;
-               return false;
-       }
-
-       if (assign(newnick) == assign(nick))
+       User* const InUse = ServerInstance->FindNickOnly(newnick);
+       if (InUse == this)
        {
-               // 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)
                        return true;
        }
        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(432, "%s %s :Invalid nickname: %s",this->nick.c_str(), 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;
-                                       if (chan->GetPrefixValue(this) < VOICE_VALUE && chan->IsBanned(this))
-                                       {
-                                               this->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", this->nick.c_str(), 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
@@ -920,76 +596,94 @@ bool User::ChangeNick(const std::string& newnick, bool force)
                 * 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->WriteTo(InUse, "NICK %s", InUse->uuid.c_str());
-                               InUse->WriteNumeric(433, "%s %s :Nickname overruled.", InUse->nick.c_str(), 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(433, "%s %s :Nickname is already in use.", this->registered >= REG_NICK ? this->nick.c_str() : "*", newnick.c_str());
+                               this->WriteNumeric(ERR_NICKNAMEINUSE, newnick, "Nickname is already in use.");
                                return false;
                        }
                }
+
+               age = newts ? newts : ServerInstance->Time();
        }
 
        if (this->registered == REG_ALL)
-               this->WriteCommon("NICK %s",newnick.c_str());
-       std::string oldnick = nick;
+       {
+               ClientProtocol::Messages::Nick nickmsg(this, newnick);
+               ClientProtocol::Event nickevent(ServerInstance->GetRFCEvents().nick, nickmsg);
+               this->WriteCommonRaw(nickevent, true);
+       }
+       const std::string oldnick = nick;
        nick = newnick;
 
        InvalidateCache();
-       ServerInstance->Users->clientlist->erase(oldnick);
-       (*(ServerInstance->Users->clientlist))[newnick] = this;
+       ServerInstance->Users->clientlist.erase(oldnick);
+       ServerInstance->Users->clientlist[newnick] = this;
 
        if (registered == REG_ALL)
-               FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(this,oldnick));
+               FOREACH_MOD(OnUserPostNick, (this,oldnick));
 
        return true;
 }
 
-int LocalUser::GetServerPort()
+void LocalUser::OverruleNick()
 {
-       switch (this->server_sa.sa.sa_family)
        {
-               case AF_INET6:
-                       return htons(this->server_sa.in6.sin6_port);
-               case AF_INET:
-                       return htons(this->server_sa.in4.sin_port);
+               ClientProtocol::Messages::Nick nickmsg(this, this->uuid);
+               this->Send(ServerInstance->GetRFCEvents().nick, nickmsg);
        }
-       return 0;
+       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);
 }
 
-const char* User::GetIPString()
+const std::string& User::GetIPString()
 {
-       int port;
        if (cachedip.empty())
        {
-               irc::sockets::satoap(client_sa, cachedip, port);
+               cachedip = client_sa.addr();
                /* IP addresses starting with a : on irc are a Bad Thing (tm) */
-               if (cachedip.c_str()[0] == ':')
+               if (cachedip[0] == ':')
                        cachedip.insert(cachedip.begin(),1,'0');
        }
 
-       return cachedip.c_str();
+       return cachedip;
+}
+
+const std::string& User::GetHost(bool uncloak) const
+{
+       return uncloak ? GetRealHost() : GetDisplayedHost();
+}
+
+const std::string& User::GetDisplayedHost() const
+{
+       return displayhost.empty() ? realhost : displayhost;
+}
+
+const std::string& User::GetRealHost() const
+{
+       return realhost;
+}
+
+const std::string& User::GetRealName() const
+{
+       return realname;
 }
 
 irc::sockets::cidr_mask User::GetCIDRMask()
 {
-       int range = 0;
-       switch (client_sa.sa.sa_family)
+       unsigned char range = 0;
+       switch (client_sa.family())
        {
                case AF_INET6:
                        range = ServerInstance->Config->c_ipv6_range;
@@ -1001,339 +695,230 @@ irc::sockets::cidr_mask User::GetCIDRMask()
        return irc::sockets::cidr_mask(client_sa, range);
 }
 
-bool User::SetClientIP(const char* sip, bool recheck_eline)
+bool User::SetClientIP(const std::string& address)
 {
-       this->InvalidateCache();
-       return irc::sockets::aptosa(sip, 0, client_sa);
+       irc::sockets::sockaddrs sa;
+       if (!irc::sockets::aptosa(address, client_sa.port(), sa))
+               return false;
+
+       User::SetClientIP(sa);
+       return true;
 }
 
-void User::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline)
+void User::SetClientIP(const irc::sockets::sockaddrs& sa)
 {
-       this->InvalidateCache();
+       const std::string oldip(GetIPString());
        memcpy(&client_sa, &sa, sizeof(irc::sockets::sockaddrs));
+       this->InvalidateCache();
+
+       // If the users hostname was their IP then update it.
+       if (GetRealHost() == oldip)
+               ChangeRealHost(GetIPString(), false);
+       if (GetDisplayedHost() == oldip)
+               ChangeDisplayedHost(GetIPString());
 }
 
-bool LocalUser::SetClientIP(const char* sip, bool recheck_eline)
+bool LocalUser::SetClientIP(const std::string& address)
 {
        irc::sockets::sockaddrs sa;
-       if (!irc::sockets::aptosa(sip, 0, sa))
-               // Invalid
+       if (!irc::sockets::aptosa(address, client_sa.port(), sa))
                return false;
 
-       LocalUser::SetClientIP(sa, recheck_eline);
+       LocalUser::SetClientIP(sa);
        return true;
 }
 
-void LocalUser::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline)
+void LocalUser::SetClientIP(const irc::sockets::sockaddrs& sa)
 {
-       if (sa != client_sa)
-       {
-               User::SetClientIP(sa);
-               if (recheck_eline)
-                       this->exempt = (ServerInstance->XLines->MatchesLine("E", this) != NULL);
+       if (sa == client_sa)
+               return;
 
-               FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(this));
-       }
-}
+       ServerInstance->Users->RemoveCloneCounts(this);
 
-static std::string wide_newline("\r\n");
+       User::SetClientIP(sa);
 
-void User::Write(const std::string& text)
-{
-}
+       FOREACH_MOD(OnSetUserIP, (this));
 
-void User::Write(const char *text, ...)
-{
+       ServerInstance->Users->AddClone(this);
+
+       // Recheck the connect class.
+       this->MyClass = NULL;
+       this->SetClass();
+       this->CheckClass();
 }
 
-void LocalUser::Write(const std::string& text)
+void LocalUser::Write(const ClientProtocol::SerializedMessage& text)
 {
-       if (!ServerInstance->SE->BoundsCheckFd(&eh))
+       if (!SocketEngine::BoundsCheckFd(&eh))
                return;
 
-       if (text.length() > MAXBUF - 2)
+       if (ServerInstance->Config->RawLog)
        {
-               // this should happen rarely or never. Crop the string at 512 and try again.
-               std::string try_again = text.substr(0, MAXBUF - 2);
-               Write(try_again);
-               return;
-       }
+               if (text.empty())
+                       return;
 
-       ServerInstance->Logs->Log("USEROUTPUT", RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
+               std::string::size_type nlpos = text.find_first_of("\r\n", 0, 2);
+               if (nlpos == std::string::npos)
+                       nlpos = text.length(); // TODO is this ok, test it
+
+               ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %.*s", uuid.c_str(), (int) nlpos, text.c_str());
+       }
 
        eh.AddWriteBuf(text);
-       eh.AddWriteBuf(wide_newline);
 
-       ServerInstance->stats->statsSent += text.length() + 2;
-       this->bytes_out += text.length() + 2;
+       const size_t bytessent = text.length() + 2;
+       ServerInstance->stats.Sent += bytessent;
+       this->bytes_out += bytessent;
        this->cmds_out++;
 }
 
-/** Write()
- */
-void LocalUser::Write(const char *text, ...)
+void LocalUser::Send(ClientProtocol::Event& protoev)
 {
-       va_list argsPtr;
-       char textbuffer[MAXBUF];
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->Write(std::string(textbuffer));
-}
+       if (!serializer)
+       {
+               ServerInstance->Logs->Log("USERS", LOG_DEBUG, "BUG: LocalUser::Send() called on %s who does not have a serializer!",
+                       GetFullRealHost().c_str());
+               return;
+       }
 
-void User::WriteServ(const std::string& text)
-{
-       this->Write(":%s %s",ServerInstance->Config->ServerName.c_str(),text.c_str());
+       // In the most common case a static LocalUser field, sendmsglist, is passed to the event to be
+       // populated. The list is cleared before returning.
+       // To handle re-enters, if sendmsglist is non-empty upon entering the method then a temporary
+       // list is used instead of the static one.
+       if (sendmsglist.empty())
+       {
+               Send(protoev, sendmsglist);
+               sendmsglist.clear();
+       }
+       else
+       {
+               ClientProtocol::MessageList msglist;
+               Send(protoev, msglist);
+       }
 }
 
-/** WriteServ()
- *  Same as Write(), except `text' is prefixed with `:server.name '.
- */
-void User::WriteServ(const char* text, ...)
+void LocalUser::Send(ClientProtocol::Event& protoev, ClientProtocol::MessageList& msglist)
 {
-       va_list argsPtr;
-       char textbuffer[MAXBUF];
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteServ(std::string(textbuffer));
+       // Modules can personalize the messages sent per user for the event
+       protoev.GetMessagesForUser(this, msglist);
+       for (ClientProtocol::MessageList::const_iterator i = msglist.begin(); i != msglist.end(); ++i)
+       {
+               ClientProtocol::Message& curr = **i;
+               ModResult res;
+               FIRST_MOD_RESULT(OnUserWrite, res, (this, curr));
+               if (res != MOD_RES_DENY)
+                       Write(serializer->SerializeForUser(this, curr));
+       }
 }
 
-
-void User::WriteNumeric(unsigned int numeric, const char* text, ...)
+void User::WriteNumeric(const Numeric::Numeric& numeric)
 {
-       va_list argsPtr;
-       char textbuffer[MAXBUF];
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteNumeric(numeric, std::string(textbuffer));
-}
+       LocalUser* const localuser = IS_LOCAL(this);
+       if (!localuser)
+               return;
 
-void User::WriteNumeric(unsigned int numeric, const std::string &text)
-{
-       char textbuffer[MAXBUF];
        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;
 
-       snprintf(textbuffer,MAXBUF,":%s %03u %s",ServerInstance->Config->ServerName.c_str(), numeric, text.c_str());
-       this->Write(std::string(textbuffer));
-}
-
-void User::WriteFrom(User *user, const std::string &text)
-{
-       char tb[MAXBUF];
-
-       snprintf(tb,MAXBUF,":%s %s",user->GetFullHost().c_str(),text.c_str());
-
-       this->Write(std::string(tb));
-}
-
-
-/* write text from an originating user to originating user */
-
-void User::WriteFrom(User *user, const char* text, ...)
-{
-       va_list argsPtr;
-       char textbuffer[MAXBUF];
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteFrom(user, std::string(textbuffer));
+       ClientProtocol::Messages::Numeric numericmsg(numeric, localuser);
+       localuser->Send(ServerInstance->GetRFCEvents().numeric, numericmsg);
 }
 
-
-/* write text to an destination user from a source user (e.g. user privmsg) */
-
-void User::WriteTo(User *dest, const char *data, ...)
+void User::WriteRemoteNotice(const std::string& text)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       va_start(argsPtr, data);
-       vsnprintf(textbuffer, MAXBUF, data, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteTo(dest, std::string(textbuffer));
+       ServerInstance->PI->SendUserNotice(this, text);
 }
 
-void User::WriteTo(User *dest, const std::string &data)
+void LocalUser::WriteRemoteNotice(const std::string& text)
 {
-       dest->WriteFrom(this, data);
+       WriteNotice(text);
 }
 
-void User::WriteCommon(const char* text, ...)
+namespace
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       if (this->registered != REG_ALL || quitting)
-               return;
-
-       int len = snprintf(textbuffer,MAXBUF,":%s ",this->GetFullHost().c_str());
+       class WriteCommonRawHandler : public User::ForEachNeighborHandler
+       {
+               ClientProtocol::Event& ev;
 
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer + len, MAXBUF - len, text, argsPtr);
-       va_end(argsPtr);
+               void Execute(LocalUser* user) CXX11_OVERRIDE
+               {
+                       user->Send(ev);
+               }
 
-       this->WriteCommonRaw(std::string(textbuffer), true);
+        public:
+               WriteCommonRawHandler(ClientProtocol::Event& protoev)
+                       : ev(protoev)
+               {
+               }
+       };
 }
 
-void User::WriteCommonExcept(const char* text, ...)
+void User::WriteCommonRaw(ClientProtocol::Event& protoev, bool include_self)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       if (this->registered != REG_ALL || quitting)
-               return;
-
-       int len = snprintf(textbuffer,MAXBUF,":%s ",this->GetFullHost().c_str());
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer + len, MAXBUF - len, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteCommonRaw(std::string(textbuffer), false);
+       WriteCommonRawHandler handler(protoev);
+       ForEachNeighbor(handler, include_self);
 }
 
-void User::WriteCommonRaw(const std::string &line, bool include_self)
+void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self)
 {
-       if (this->registered != REG_ALL || quitting)
-               return;
-
-       LocalUser::already_sent_id++;
-
-       UserChanList include_c(chans);
-       std::map<User*,bool> exceptions;
+       // 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.
 
+       // 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(I_OnBuildNeighborList,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 = LocalUser::already_sent_id;
-                       if (i->second)
-                               u->Write(line);
+                       // 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 (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
-       {
-               Channel* c = *v;
-               const UserMembList* ulist = c->GetUsers();
-               for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
-               {
-                       LocalUser* u = IS_LOCAL(i->first);
-                       if (u && !u->quitting && 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)
-{
-       char tb1[MAXBUF];
-       char tb2[MAXBUF];
 
-       if (this->registered != REG_ALL)
-               return;
-
-       already_sent_t uniq_id = ++LocalUser::already_sent_id;
-
-       snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),normal_text.c_str());
-       snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),oper_text.c_str());
-       std::string out1 = tb1;
-       std::string out2 = tb2;
-
-       UserChanList include_c(chans);
-       std::map<User*,bool> exceptions;
-
-       FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
-
-       for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
+       // Now consider the real neighbors
+       for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
        {
-               LocalUser* u = IS_LOCAL(i->first);
-               if (u && !u->quitting)
+               Channel* chan = (*i)->chan;
+               const Channel::MemberMap& userlist = chan->GetUsers();
+               for (Channel::MemberMap::const_iterator j = userlist.begin(); j != userlist.end(); ++j)
                {
-                       u->already_sent = uniq_id;
-                       if (i->second)
-                               u->Write(IS_OPER(u) ? out2 : out1);
-               }
-       }
-       for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
-       {
-               const UserMembList* ulist = (*v)->GetUsers();
-               for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
-               {
-                       LocalUser* u = IS_LOCAL(i->first);
-                       if (u && !u->quitting && (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(IS_OPER(u) ? out2 : out1);
+                               // Mark as visited and execute function
+                               curr->already_sent = newid;
+                               handler.Execute(curr);
                        }
                }
        }
 }
 
-void LocalUser::SendText(const std::string& line)
+void User::WriteRemoteNumeric(const Numeric::Numeric& numeric)
 {
-       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, ...)
-{
-       va_list argsPtr;
-       char line[MAXBUF];
-
-       va_start(argsPtr, text);
-       vsnprintf(line, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       SendText(std::string(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() + 13;
-               if (lineLength > MAXBUF)
-               {
-                       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
@@ -1350,221 +935,103 @@ void User::SendText(const std::string &LinePrefix, std::stringstream &TextStream
  */
 bool User::SharesChannelWith(User *other)
 {
-       if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL))
-               return false;
-
        /* 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
                 */
-               if ((*i)->HasUser(other))
+               if ((*i)->chan->HasUser(other))
                        return true;
        }
        return false;
 }
 
-bool User::ChangeName(const char* gecos)
+bool User::ChangeRealName(const std::string& real)
 {
-       if (!this->fullname.compare(gecos))
+       if (!this->realname.compare(real))
                return true;
 
        if (IS_LOCAL(this))
        {
                ModResult MOD_RESULT;
-               FIRST_MOD_RESULT(OnChangeLocalUserGECOS, MOD_RESULT, (IS_LOCAL(this),gecos));
+               FIRST_MOD_RESULT(OnPreChangeRealName, MOD_RESULT, (IS_LOCAL(this), real));
                if (MOD_RESULT == MOD_RES_DENY)
                        return false;
-               FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
+               FOREACH_MOD(OnChangeRealName, (this, real));
        }
-       this->fullname.assign(gecos, 0, ServerInstance->Config->Limits.MaxGecos);
+       this->realname.assign(real, 0, ServerInstance->Config->Limits.MaxReal);
 
        return true;
 }
 
-void User::DoHostCycle(const std::string &quitline)
+bool User::ChangeDisplayedHost(const std::string& shost)
 {
-       char buffer[MAXBUF];
-
-       if (!ServerInstance->Config->CycleHosts)
-               return;
-
-       already_sent_t silent_id = ++LocalUser::already_sent_id;
-       already_sent_t seen_id = ++LocalUser::already_sent_id;
-
-       UserChanList include_c(chans);
-       std::map<User*,bool> exceptions;
-
-       FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
-
-       // Users shouldn't see themselves quitting when host cycling
-       exceptions.erase(this);
-       for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
-       {
-               LocalUser* u = IS_LOCAL(i->first);
-               if (u && !u->quitting)
-               {
-                       if (i->second)
-                       {
-                               u->already_sent = seen_id;
-                               u->Write(quitline);
-                       }
-                       else
-                       {
-                               u->already_sent = silent_id;
-                       }
-               }
-       }
-       for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
-       {
-               Channel* c = *v;
-               snprintf(buffer, MAXBUF, ":%s JOIN %s", GetFullHost().c_str(), c->name.c_str());
-               std::string joinline(buffer);
-               Membership* memb = c->GetUser(this);
-               std::string modeline = memb->modes;
-               if (modeline.length() > 0)
-               {
-                       for(unsigned int i=0; i < memb->modes.length(); i++)
-                               modeline.append(" ").append(nick);
-                       snprintf(buffer, MAXBUF, ":%s MODE %s +%s",
-                               ServerInstance->Config->CycleHostsFromUser ? GetFullHost().c_str() : ServerInstance->Config->ServerName.c_str(),
-                               c->name.c_str(), modeline.c_str());
-                       modeline = buffer;
-               }
-
-               const UserMembList *ulist = c->GetUsers();
-               for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
-               {
-                       LocalUser* u = IS_LOCAL(i->first);
-                       if (u == NULL || u == this)
-                               continue;
-                       if (u->already_sent == silent_id)
-                               continue;
-
-                       if (u->already_sent != seen_id)
-                       {
-                               u->Write(quitline);
-                               u->already_sent = seen_id;
-                       }
-                       u->Write(joinline);
-                       if (modeline.length() > 0)
-                               u->Write(modeline);
-               }
-       }
-}
-
-bool User::ChangeDisplayedHost(const char* shost)
-{
-       if (dhost == shost)
+       if (GetDisplayedHost() == shost)
                return true;
 
-       if (IS_LOCAL(this))
+       LocalUser* luser = IS_LOCAL(this);
+       if (luser)
        {
                ModResult MOD_RESULT;
-               FIRST_MOD_RESULT(OnChangeLocalUserHost, MOD_RESULT, (IS_LOCAL(this),shost));
+               FIRST_MOD_RESULT(OnPreChangeHost, MOD_RESULT, (luser, shost));
                if (MOD_RESULT == MOD_RES_DENY)
                        return false;
        }
 
-       FOREACH_MOD(I_OnChangeHost, OnChangeHost(this,shost));
+       FOREACH_MOD(OnChangeHost, (this,shost));
 
-       std::string quitstr = ":" + GetFullHost() + " QUIT :Changing host";
-
-       /* Fix by Om: User::dhost is 65 long, this was truncating some long hosts */
-       this->dhost.assign(shost, 0, 64);
+       if (realhost == shost)
+               this->displayhost.clear();
+       else
+               this->displayhost.assign(shost, 0, ServerInstance->Config->Limits.MaxHost);
 
        this->InvalidateCache();
 
-       this->DoHostCycle(quitstr);
-
-       if (IS_LOCAL(this))
-               this->WriteNumeric(RPL_YOURDISPLAYEDHOST, "%s %s :is now your displayed host",this->nick.c_str(),this->dhost.c_str());
+       if (IS_LOCAL(this) && this->registered != REG_NONE)
+               this->WriteNumeric(RPL_YOURDISPLAYEDHOST, this->GetDisplayedHost(), "is now your displayed host");
 
        return true;
 }
 
-bool User::ChangeIdent(const char* newident)
+void User::ChangeRealHost(const std::string& host, bool resetdisplay)
 {
-       if (this->ident == newident)
-               return true;
+       // If the real host is the new host and we are not resetting the
+       // display host then we have nothing to do.
+       const bool changehost = (realhost != host);
+       if (!changehost && !resetdisplay)
+               return;
 
-       FOREACH_MOD(I_OnChangeIdent, OnChangeIdent(this,newident));
+       // If the displayhost is not set and we are not resetting it then
+       // we need to copy it to the displayhost field.
+       if (displayhost.empty() && !resetdisplay)
+               displayhost = realhost;
 
-       std::string quitstr = ":" + GetFullHost() + " QUIT :Changing ident";
+       // If the displayhost is the new host or we are resetting it then
+       // we clear its contents to save memory.
+       else if (displayhost == host || resetdisplay)
+               displayhost.clear();
 
-       this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
+       // If we are just resetting the display host then we don't need to
+       // do anything else.
+       if (!changehost)
+               return;
 
+       realhost = host;
        this->InvalidateCache();
-
-       this->DoHostCycle(quitstr);
-
-       return true;
 }
 
-void User::SendAll(const char* command, const char* text, ...)
+bool User::ChangeIdent(const std::string& newident)
 {
-       char textbuffer[MAXBUF];
-       char formatbuffer[MAXBUF];
-       va_list argsPtr;
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       snprintf(formatbuffer,MAXBUF,":%s %s $* :%s", this->GetFullHost().c_str(), command, textbuffer);
-       std::string fmt = formatbuffer;
-
-       for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
-       {
-               if ((*i)->registered == REG_ALL)
-                       (*i)->Write(fmt);
-       }
-}
-
-
-std::string User::ChannelList(User* source, bool spy)
-{
-       std::string list;
-
-       for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
-       {
-               Channel* c = *i;
-               /* 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 == this || !(c->IsModeSet('p') || c->IsModeSet('s')) || c->HasUser(source)))
-                       list.append(c->GetPrefixChar(this)).append(c->name).append(" ");
-       }
-
-       return list;
-}
-
-void User::SplitChanList(User* dest, const std::string &cl)
-{
-       std::string line;
-       std::ostringstream prefix;
-       std::string::size_type start, pos;
+       if (this->ident == newident)
+               return true;
 
-       prefix << this->nick << " " << dest->nick << " :";
-       line = prefix.str();
-       int namelen = ServerInstance->Config->ServerName.length() + 6;
+       FOREACH_MOD(OnChangeIdent, (this,newident));
 
-       for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
-       {
-               if (line.length() + namelen + pos - start > 510)
-               {
-                       ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
-                       line = prefix.str();
-               }
-
-               line.append(cl.substr(start, pos - start + 1));
-       }
+       this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
+       this->InvalidateCache();
 
-       if (line.length() != prefix.str().length())
-       {
-               ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
-       }
+       return true;
 }
 
 /*
@@ -1578,27 +1045,27 @@ void LocalUser::SetClass(const std::string &explicit_name)
 {
        ConnectClass *found = NULL;
 
-       ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Setting connect class for UID %s", this->uuid.c_str());
+       ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Setting connect class for UID %s", this->uuid.c_str());
 
        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;
 
                        if (explicit_name == c->name)
                        {
-                               ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Explicitly set to %s", explicit_name.c_str());
+                               ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Explicitly set to %s", explicit_name.c_str());
                                found = c;
                        }
                }
        }
        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", DEBUG, "Checking %s", c->GetName().c_str());
+                       ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Checking %s", c->GetName().c_str());
 
                        ModResult MOD_RESULT;
                        FIRST_MOD_RESULT(OnSetConnectClass, MOD_RESULT, (this,c));
@@ -1606,7 +1073,7 @@ void LocalUser::SetClass(const std::string &explicit_name)
                                continue;
                        if (MOD_RESULT == MOD_RES_ALLOW)
                        {
-                               ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Class forced by module to %s", c->GetName().c_str());
+                               ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class forced by module to %s", c->GetName().c_str());
                                found = c;
                                break;
                        }
@@ -1620,9 +1087,9 @@ void LocalUser::SetClass(const std::string &explicit_name)
 
                        /* check if host matches.. */
                        if (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) &&
-                           !InspIRCd::MatchCIDR(this->host, c->GetHost(), NULL))
+                           !InspIRCd::MatchCIDR(this->GetRealHost(), c->GetHost(), NULL))
                        {
-                               ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "No host match (for %s)", c->GetHost().c_str());
+                               ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "No host match (for %s)", c->GetHost().c_str());
                                continue;
                        }
 
@@ -1632,26 +1099,26 @@ void LocalUser::SetClass(const std::string &explicit_name)
                         */
                        if (c->limit && (c->GetReferenceCount() >= c->limit))
                        {
-                               ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit);
+                               ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit);
                                continue;
                        }
 
                        /* if it requires a port ... */
-                       int port = c->config->getInt("port");
-                       if (port)
+                       if (!c->ports.empty())
                        {
-                               ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Requires port (%d)", port);
-
                                /* and our port doesn't match, fail. */
-                               if (this->GetServerPort() != port)
+                               if (!c->ports.count(this->server_sa.port()))
+                               {
+                                       ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Requires a different port, skipping");
                                        continue;
+                               }
                        }
 
                        if (regdone && !c->config->getString("password").empty())
                        {
-                               if (ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash")))
+                               if (!ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash")))
                                {
-                                       ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Bad password, skipping");
+                                       ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Bad password, skipping");
                                        continue;
                                }
                        }
@@ -1671,62 +1138,80 @@ void LocalUser::SetClass(const std::string &explicit_name)
        }
 }
 
-/* looks up a users password for their connection class (<ALLOW>/<DENY> tags)
- * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
- * then their ip will be taken as 'priority' anyway, so for example,
- * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
- */
-ConnectClass* LocalUser::GetClass()
-{
-       return MyClass;
-}
-
-ConnectClass* User::GetClass()
-{
-       return NULL;
-}
-
 void User::PurgeEmptyChannels()
 {
        // firstly decrement the count on each channel
-       for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++)
+       for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); )
        {
-               Channel* c = *f;
+               Channel* c = (*i)->chan;
+               ++i;
                c->DelUser(this);
        }
+}
 
-       this->UnOper();
+void User::WriteNotice(const std::string& text)
+{
+       LocalUser* const localuser = IS_LOCAL(this);
+       if (!localuser)
+               return;
+
+       ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, localuser, text, MSG_NOTICE);
+       localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
 }
 
 const std::string& FakeUser::GetFullHost()
 {
-       if (!ServerInstance->Config->HideWhoisServer.empty())
-               return ServerInstance->Config->HideWhoisServer;
-       return server;
+       if (!ServerInstance->Config->HideServer.empty())
+               return ServerInstance->Config->HideServer;
+       return server->GetName();
 }
 
 const std::string& FakeUser::GetFullRealHost()
 {
-       if (!ServerInstance->Config->HideWhoisServer.empty())
-               return ServerInstance->Config->HideWhoisServer;
-       return server;
+       if (!ServerInstance->Config->HideServer.empty())
+               return ServerInstance->Config->HideServer;
+       return server->GetName();
 }
 
 ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask)
        : config(tag), type(t), fakelag(true), name("unnamed"), registration_timeout(0), host(mask),
        pingtime(0), softsendqmax(0), hardsendqmax(0), recvqmax(0),
-       penaltythreshold(0), commandrate(0), maxlocal(0), maxglobal(0), maxconnwarn(true), maxchans(0), limit(0)
+       penaltythreshold(0), commandrate(0), maxlocal(0), maxglobal(0), maxconnwarn(true), maxchans(ServerInstance->Config->MaxChans),
+       limit(0), resolvehostnames(true)
 {
 }
 
 ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask, const ConnectClass& parent)
-       : config(tag), type(t), fakelag(parent.fakelag), name("unnamed"),
-       registration_timeout(parent.registration_timeout), host(mask), pingtime(parent.pingtime),
-       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)
 {
+       Update(&parent);
+       name = "unnamed";
+       type = t;
+       host = mask;
+
+       // Connect classes can inherit from each other but this is problematic for modules which can't use
+       // ConnectClass::Update so we build a hybrid tag containing all of the values set on this class as
+       // well as the parent class.
+       ConfigItems* items = NULL;
+       config = ConfigTag::create(tag->tag, tag->src_name, tag->src_line, items);
+
+       const ConfigItems& parentkeys = parent.config->getItems();
+       for (ConfigItems::const_iterator piter = parentkeys.begin(); piter != parentkeys.end(); ++piter)
+       {
+               // The class name and parent name are not inherited
+               if (stdalgo::string::equalsci(piter->first, "name") || stdalgo::string::equalsci(piter->first, "parent"))
+                       continue;
+
+               // Store the item in the config tag. If this item also
+               // exists in the child it will be overwritten.
+               (*items)[piter->first] = piter->second;
+       }
+
+       const ConfigItems& childkeys = tag->getItems();
+       for (ConfigItems::const_iterator citer = childkeys.begin(); citer != childkeys.end(); ++citer)
+       {
+               // This will overwrite the parent value if present.
+               (*items)[citer->first] = citer->second;
+       }
 }
 
 void ConnectClass::Update(const ConnectClass* src)
@@ -1748,4 +1233,6 @@ void ConnectClass::Update(const ConnectClass* src)
        maxconnwarn = src->maxconnwarn;
        maxchans = src->maxchans;
        limit = src->limit;
+       resolvehostnames = src->resolvehostnames;
+       ports = src->ports;
 }
index 0467e8f218fd18630d2fda6da49eb9922c09a503..50a7d5064ad2f7211f229153e75bbf5fbe42870c 100755 (executable)
@@ -1,2 +1,2 @@
 #!/bin/sh
-echo "InspIRCd-2.0.27"
+echo "InspIRCd-3.0.0-rc2"
diff --git a/src/whois.cpp b/src/whois.cpp
deleted file mode 100644 (file)
index bec9c7e..0000000
+++ /dev/null
@@ -1,97 +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"
-
-void InspIRCd::DoWhois(User* user, User* dest,unsigned long signon, unsigned long idle, const char* nick)
-{
-       this->SendWhoisLine(user, dest, 311, "%s %s %s %s * :%s",user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), dest->dhost.c_str(), dest->fullname.c_str());
-       if (user == dest || user->HasPrivPermission("users/auspex"))
-       {
-               this->SendWhoisLine(user, dest, 378, "%s %s :is connecting from %s@%s %s", user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), dest->host.c_str(), dest->GetIPString());
-       }
-
-       std::string cl = dest->ChannelList(user, false);
-       const ServerConfig::OperSpyWhoisState state = user->HasPrivPermission("users/auspex") ? ServerInstance->Config->OperSpyWhois : ServerConfig::SPYWHOIS_NONE;
-
-       if (state == ServerConfig::SPYWHOIS_SINGLEMSG)
-               cl.append(dest->ChannelList(user, true));
-
-       user->SplitChanList(dest,cl);
-
-       if (state == ServerConfig::SPYWHOIS_SPLITMSG)
-       {
-               std::string scl = dest->ChannelList(user, true);
-               if (scl.length())
-               {
-                       SendWhoisLine(user, dest, 336, "%s %s :is on private/secret channels:",user->nick.c_str(), dest->nick.c_str());
-                       user->SplitChanList(dest,scl);
-               }
-       }
-       if (user != dest && !this->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
-       {
-               this->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick.c_str(), dest->nick.c_str(), this->Config->HideWhoisServer.c_str(), this->Config->Network.c_str());
-       }
-       else
-       {
-               std::string serverdesc = GetServerDescription(dest->server);
-               this->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick.c_str(), dest->nick.c_str(), dest->server.c_str(), serverdesc.c_str());
-       }
-
-       if (IS_AWAY(dest))
-       {
-               this->SendWhoisLine(user, dest, 301, "%s %s :%s",user->nick.c_str(), dest->nick.c_str(), dest->awaymsg.c_str());
-       }
-
-       if (IS_OPER(dest))
-       {
-               if (this->Config->GenericOper)
-                       this->SendWhoisLine(user, dest, 313, "%s %s :is an IRC operator",user->nick.c_str(), dest->nick.c_str());
-               else
-                       this->SendWhoisLine(user, dest, 313, "%s %s :is %s %s on %s",user->nick.c_str(), dest->nick.c_str(), (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"),dest->oper->NameStr(), this->Config->Network.c_str());
-       }
-
-       if (user == dest || user->HasPrivPermission("users/auspex"))
-       {
-               if (dest->IsModeSet('s') != 0)
-               {
-                       this->SendWhoisLine(user, dest, 379, "%s %s :is using modes +%s +%s", user->nick.c_str(), dest->nick.c_str(), dest->FormatModes(), dest->FormatNoticeMasks());
-               }
-               else
-               {
-                       this->SendWhoisLine(user, dest, 379, "%s %s :is using modes +%s", user->nick.c_str(), dest->nick.c_str(), dest->FormatModes());
-               }
-       }
-
-       FOREACH_MOD(I_OnWhois,OnWhois(user,dest));
-
-       /*
-        * We only send these if we've been provided them. That is, if hidewhois is turned off, and user is local, or
-        * if remote whois is queried, too. This is to keep the user hidden, and also since you can't reliably tell remote time. -- w00t
-        */
-       if ((idle) || (signon))
-       {
-               this->SendWhoisLine(user, dest, 317, "%s %s %lu %lu :seconds idle, signon time",user->nick.c_str(), dest->nick.c_str(), idle, signon);
-       }
-
-       this->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick.c_str(), dest->nick.c_str());
-}
-
-
-
index eb9151293d8e185747de8ba4a3caaa729a5b1588..4a313af760b34e2d7d4443005f4978be990c0ac7 100644 (file)
  */
 
 
-/* $Core */
-
 #include "inspircd.h"
-#include "hashcomp.h"
-#include "inspstring.h"
 
-static bool match_internal(const unsigned char *str, const unsigned char *mask, unsigned const char *map)
+static bool MatchInternal(const unsigned char* str, const unsigned char* mask, unsigned const char* map)
 {
-       unsigned char *cp = NULL, *mp = NULL;
+       unsigned char* cp = NULL;
+       unsigned char* mp = NULL;
        unsigned char* string = (unsigned char*)str;
        unsigned char* wild = (unsigned char*)mask;
 
@@ -74,46 +71,53 @@ static bool match_internal(const unsigned char *str, const unsigned char *mask,
        return !*wild;
 }
 
-/********************************************************************
- * Below here is all wrappers around match_internal
- ********************************************************************/
+// Below here is all wrappers around MatchInternal
 
-CoreExport bool InspIRCd::Match(const std::string &str, const std::string &mask, unsigned const char *map)
+bool InspIRCd::Match(const std::string& str, const std::string& mask, unsigned const char* map)
 {
        if (!map)
                map = national_case_insensitive_map;
 
-       return match_internal((const unsigned char *)str.c_str(), (const unsigned char *)mask.c_str(), map);
+       return MatchInternal((const unsigned char*)str.c_str(), (const unsigned char*)mask.c_str(), map);
 }
 
-CoreExport bool InspIRCd::Match(const  char *str, const char *mask, unsigned const char *map)
+bool InspIRCd::Match(const char* str, const char* mask, unsigned const char* map)
 {
        if (!map)
                map = national_case_insensitive_map;
-       return match_internal((const unsigned char *)str, (const unsigned char *)mask, map);
+
+       return MatchInternal((const unsigned char*)str, (const unsigned char*)mask, map);
 }
 
-CoreExport bool InspIRCd::MatchCIDR(const std::string &str, const std::string &mask, unsigned const char *map)
+bool InspIRCd::MatchCIDR(const std::string& str, const std::string& mask, unsigned const char* map)
 {
        if (irc::sockets::MatchCIDR(str, mask, true))
                return true;
 
-       if (!map)
-               map = national_case_insensitive_map;
-
        // Fall back to regular match
        return InspIRCd::Match(str, mask, map);
 }
 
-CoreExport bool InspIRCd::MatchCIDR(const  char *str, const char *mask, unsigned const char *map)
+bool InspIRCd::MatchCIDR(const char* str, const char* mask, unsigned const char* map)
 {
        if (irc::sockets::MatchCIDR(str, mask, true))
                return true;
 
-       if (!map)
-               map = national_case_insensitive_map;
-
        // Fall back to regular match
        return InspIRCd::Match(str, mask, map);
 }
 
+bool InspIRCd::MatchMask(const std::string& masks, const std::string& hostname, const std::string& ipaddr)
+{
+       irc::spacesepstream masklist(masks);
+       std::string mask;
+       while (masklist.GetToken(mask))
+       {
+               if (InspIRCd::Match(hostname, mask, ascii_case_insensitive_map) ||
+                       InspIRCd::MatchCIDR(ipaddr, mask, ascii_case_insensitive_map))
+               {
+                       return true;
+               }
+       }
+       return false;
+}
index ba8a446e3d956b7848f079aa1a807f232bb1fd59..bf61d76ea480b63cc0689baa7ab2ad830926245e 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "inspircd.h"
 #include "xline.h"
-#include "bancache.h"
+#include "modules/stats.h"
 
 /** An XLineFactory specialized to generate GLine* pointers
  */
@@ -34,7 +34,7 @@ class GLineFactory : public XLineFactory
 
        /** Generate a GLine
         */
-       XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+       XLine* Generate(time_t set_time, unsigned long duration, const std::string& source, const std::string& reason, const std::string& xline_specific_mask) CXX11_OVERRIDE
        {
                IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
                return new GLine(set_time, duration, source, reason, ih.first, ih.second);
@@ -50,7 +50,7 @@ class ELineFactory : public XLineFactory
 
        /** Generate an ELine
         */
-       XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+       XLine* Generate(time_t set_time, unsigned long duration, const std::string& source, const std::string& reason, const std::string& xline_specific_mask) CXX11_OVERRIDE
        {
                IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
                return new ELine(set_time, duration, source, reason, ih.first, ih.second);
@@ -66,7 +66,7 @@ class KLineFactory : public XLineFactory
 
        /** Generate a KLine
         */
-        XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+        XLine* Generate(time_t set_time, unsigned long duration, const std::string& source, const std::string& reason, const std::string& xline_specific_mask) CXX11_OVERRIDE
         {
                 IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
                 return new KLine(set_time, duration, source, reason, ih.first, ih.second);
@@ -82,7 +82,7 @@ class QLineFactory : public XLineFactory
 
        /** Generate a QLine
         */
-        XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+        XLine* Generate(time_t set_time, unsigned long duration, const std::string& source, const std::string& reason, const std::string& xline_specific_mask) CXX11_OVERRIDE
         {
                 return new QLine(set_time, duration, source, reason, xline_specific_mask);
         }
@@ -97,7 +97,7 @@ class ZLineFactory : public XLineFactory
 
        /** Generate a ZLine
         */
-        XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
+        XLine* Generate(time_t set_time, unsigned long duration, const std::string& source, const std::string& reason, const std::string& xline_specific_mask) CXX11_OVERRIDE
         {
                 return new ZLine(set_time, duration, source, reason, xline_specific_mask);
         }
@@ -156,9 +156,10 @@ 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++)
        {
-               User* u = (User*)(*u2);
+               LocalUser* u = *u2;
                u->exempt = false;
 
                /* This uses safe iteration to ensure that if a line expires here, it doenst trash the iterator */
@@ -264,12 +265,31 @@ bool XLineManager::AddLine(XLine* line, User* user)
                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
-                       // it's expired then expire it and add the new one instead of returning false
-                       if ((!i->second->duration) || (ServerInstance->Time() < i->second->expiry))
-                               return false;
+                       bool silent = false;
 
-                       ExpireLine(x, i);
+                       // Allow replacing a config line for an updated config line.
+                       if (i->second->from_config && line->from_config)
+                       {
+                               // Nothing changed, skip adding this one.
+                               if (i->second->reason == line->reason)
+                                       return false;
+
+                               silent = true;
+                       }
+                       // Allow replacing a non-config line for a new config line.
+                       else if (!line->from_config)
+                       {
+                               // X-line propagation bug was here, if the line to be added already exists and
+                               // it's expired then expire it and add the new one instead of returning false
+                               if ((!i->second->duration) || (ServerInstance->Time() < i->second->expiry))
+                                       return false;
+                       }
+                       else
+                       {
+                               silent = true;
+                       }
+
+                       ExpireLine(x, i, silent);
                }
        }
 
@@ -278,7 +298,7 @@ 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);
@@ -286,14 +306,14 @@ bool XLineManager::AddLine(XLine* line, User* user)
        lookup_lines[line->type][line->Displayable()] = line;
        line->OnAdd();
 
-       FOREACH_MOD(I_OnAddLine,OnAddLine(user, line));
+       FOREACH_MOD(OnAddLine, (user, line));
 
        return true;
 }
 
 // deletes a line, returns true if the line existed and was removed
 
-bool XLineManager::DelLine(const char* hostmask, const std::string &type, User* user, bool simulate)
+bool XLineManager::DelLine(const char* hostmask, const std::string& type, std::string& reason, User* user, bool simulate)
 {
        ContainerIter x = lookup_lines.find(type);
 
@@ -305,18 +325,18 @@ bool XLineManager::DelLine(const char* hostmask, const std::string &type, User*
        if (y == x->second.end())
                return false;
 
+       reason.assign(y->second->reason);
+
        if (simulate)
                return true;
 
-       ServerInstance->BanCache->RemoveEntries(y->second->type, true);
+       ServerInstance->BanCache.RemoveEntries(y->second->type, true);
 
-       FOREACH_MOD(I_OnDelLine,OnDelLine(user, y->second));
+       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);
@@ -330,7 +350,7 @@ void ELine::Unset()
        ServerInstance->XLines->CheckELines();
 }
 
-// returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match
+// returns a pointer to the reason if a nickname matches a Q-line, NULL if it didn't match
 
 XLine* XLineManager::MatchesLine(const std::string &type, User* user)
 {
@@ -402,20 +422,20 @@ XLine* XLineManager::MatchesLine(const std::string &type, const std::string &pat
 }
 
 // removes lines that have expired
-void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
+void XLineManager::ExpireLine(ContainerIter container, LookupIter item, bool silent)
 {
-       FOREACH_MOD(I_OnExpireLine, OnExpireLine(item->second));
+       FOREACH_MOD(OnExpireLine, (item->second));
+
+       if (!silent)
+               item->second->DisplayExpiry();
 
-       item->second->DisplayExpiry();
        item->second->Unset();
 
        /* TODO: Can we skip this loop by having a 'pending' field in the XLine class, which is set when a line
         * 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);
@@ -425,10 +445,10 @@ void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
 // applies lines, removing clients and changing nicks etc as applicable
 void XLineManager::ApplyLines()
 {
-       LocalUserList::reverse_iterator u2 = ServerInstance->Users->local_users.rbegin();
-       while (u2 != ServerInstance->Users->local_users.rend())
+       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+       for (UserManager::LocalList::const_iterator j = list.begin(); j != list.end(); ++j)
        {
-               User* u = *u2++;
+               LocalUser* u = *j;
 
                // Don't ban people who are exempt.
                if (u->exempt)
@@ -445,7 +465,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);
 
@@ -466,7 +486,7 @@ void XLineManager::InvokeStats(const std::string &type, int numeric, User* user,
                                ExpireLine(n, i);
                        }
                        else
-                               results.push_back(ServerInstance->Config->ServerName+" "+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;
                }
@@ -521,40 +541,38 @@ void XLine::Apply(User* u)
 
 bool XLine::IsBurstable()
 {
-       return true;
+       return !from_config;
 }
 
 void XLine::DefaultApply(User* u, const std::string &line, bool bancache)
 {
-       char sreason[MAXBUF];
-       snprintf(sreason, MAXBUF, "%s-Lined: %s", line.c_str(), this->reason.c_str());
-       if (!ServerInstance->Config->MoronBanner.empty())
-               u->WriteServ("NOTICE %s :*** %s", u->nick.c_str(), ServerInstance->Config->MoronBanner.c_str());
+       const std::string banReason = line + "-lined: " + reason;
+
+       if (!ServerInstance->Config->XLineMessage.empty())
+               u->WriteNumeric(ERR_YOUREBANNEDCREEP, ServerInstance->Config->XLineMessage);
 
        if (ServerInstance->Config->HideBans)
-               ServerInstance->Users->QuitUser(u, line + "-Lined", sreason);
+               ServerInstance->Users->QuitUser(u, line + "-lined", &banReason);
        else
-               ServerInstance->Users->QuitUser(u, sreason);
+               ServerInstance->Users->QuitUser(u, banReason);
 
 
        if (bancache)
        {
-               ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCache: Adding positive hit (" + line + ") for " + u->GetIPString());
-               if (this->duration > 0)
-                       ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason, (this->expiry - ServerInstance->Time()));
-               else
-                       ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason);
+               ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding positive hit (" + line + ") for " + u->GetIPString());
+               ServerInstance->BanCache.AddHit(u->GetIPString(), this->type, banReason, (this->duration > 0 ? (this->expiry - ServerInstance->Time()) : 0));
        }
 }
 
 bool KLine::Matches(User *u)
 {
-       if (u->exempt)
+       LocalUser* lu = IS_LOCAL(u);
+       if (lu && lu->exempt)
                return false;
 
        if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
        {
-               if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
+               if (InspIRCd::MatchCIDR(u->GetRealHost(), this->hostmask, ascii_case_insensitive_map) ||
                    InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
                {
                        return true;
@@ -571,12 +589,13 @@ void KLine::Apply(User* u)
 
 bool GLine::Matches(User *u)
 {
-       if (u->exempt)
+       LocalUser* lu = IS_LOCAL(u);
+       if (lu && lu->exempt)
                return false;
 
        if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
        {
-               if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
+               if (InspIRCd::MatchCIDR(u->GetRealHost(), this->hostmask, ascii_case_insensitive_map) ||
                    InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
                {
                        return true;
@@ -595,7 +614,7 @@ bool ELine::Matches(User *u)
 {
        if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
        {
-               if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
+               if (InspIRCd::MatchCIDR(u->GetRealHost(), this->hostmask, ascii_case_insensitive_map) ||
                    InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
                {
                        return true;
@@ -607,7 +626,8 @@ bool ELine::Matches(User *u)
 
 bool ZLine::Matches(User *u)
 {
-       if (u->exempt)
+       LocalUser* lu = IS_LOCAL(u);
+       if (lu && lu->exempt)
                return false;
 
        if (InspIRCd::MatchCIDR(u->GetIPString(), this->ipaddr))
@@ -632,8 +652,8 @@ 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.c_str());
+       /* Force to uuid on apply of Q-line, no need to disconnect anymore :) */
+       u->ChangeNick(u->uuid);
 }
 
 
@@ -670,68 +690,46 @@ 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++)
+       /* When adding one E-line, only check the one E-line */
+       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+       for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
        {
-               User* u = (User*)(*u2);
+               LocalUser* u = *u2;
                if (this->Matches(u))
                        u->exempt = true;
        }
 }
 
-void ELine::DisplayExpiry()
+void XLine::DisplayExpiry()
 {
-       ServerInstance->SNO->WriteToSnoMask('x',"Removing expired E-Line %s@%s (set by %s %ld seconds ago)",
-               identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+       bool onechar = (type.length() == 1);
+       ServerInstance->SNO->WriteToSnoMask('x', "Removing expired %s%s %s (set by %s %s ago): %s",
+               type.c_str(), (onechar ? "-line" : ""), Displayable().c_str(), source.c_str(), InspIRCd::DurationString(ServerInstance->Time() - set_time).c_str(), reason.c_str());
 }
 
-void QLine::DisplayExpiry()
+const std::string& ELine::Displayable()
 {
-       ServerInstance->SNO->WriteToSnoMask('x',"Removing expired Q-Line %s (set by %s %ld seconds ago)",
-               nick.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+       return matchtext;
 }
 
-void ZLine::DisplayExpiry()
+const std::string& KLine::Displayable()
 {
-       ServerInstance->SNO->WriteToSnoMask('x',"Removing expired Z-Line %s (set by %s %ld seconds ago)",
-               ipaddr.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+       return matchtext;
 }
 
-void KLine::DisplayExpiry()
+const std::string& GLine::Displayable()
 {
-       ServerInstance->SNO->WriteToSnoMask('x',"Removing expired K-Line %s@%s (set by %s %ld seconds ago)",
-               identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+       return matchtext;
 }
 
-void GLine::DisplayExpiry()
+const std::string& ZLine::Displayable()
 {
-       ServerInstance->SNO->WriteToSnoMask('x',"Removing expired G-Line %s@%s (set by %s %ld seconds ago)",
-               identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+       return ipaddr;
 }
 
-const char* ELine::Displayable()
+const std::string& QLine::Displayable()
 {
-       return matchtext.c_str();
-}
-
-const char* KLine::Displayable()
-{
-       return matchtext.c_str();
-}
-
-const char* GLine::Displayable()
-{
-       return matchtext.c_str();
-}
-
-const char* ZLine::Displayable()
-{
-       return ipaddr.c_str();
-}
-
-const char* QLine::Displayable()
-{
-       return nick.c_str();
+       return nick;
 }
 
 bool KLine::IsBurstable()
@@ -772,3 +770,24 @@ XLineFactory* XLineManager::GetFactory(const std::string &type)
 
        return n->second;
 }
+
+void XLineManager::ExpireRemovedConfigLines(const std::string& type, const insp::flat_set<std::string>& configlines)
+{
+       // Nothing to do.
+       if (lookup_lines.empty())
+               return;
+
+       ContainerIter xlines = lookup_lines.find(type);
+       if (xlines == lookup_lines.end())
+               return;
+
+       for (LookupIter xline = xlines->second.begin(); xline != xlines->second.end(); )
+       {
+               LookupIter cachedxline = xline++;
+               if (!cachedxline->second->from_config)
+                       continue;
+
+               if (!configlines.count(cachedxline->second->Displayable()))
+                       ExpireLine(xlines, cachedxline);
+       }
+}
diff --git a/tools/create_templates.pl b/tools/create_templates.pl
deleted file mode 100755 (executable)
index 4154332..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/perl
-
-#
-# InspIRCd -- Internet Relay Chat Daemon
-#
-#   Copyright (C) 2007 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/>.
-#
-
-
-use strict;
-use warnings;
-
-my $maxparams = shift;
-
-die "You must supply a number of parameters to generate headers allowing for!" unless(defined $maxparams);
-die "You must request a non-negative parameter limit!" unless($maxparams >= 0);
-
-print STDERR "Generating headerfile for a maximium of $maxparams parameters\n";
-
-# First generate the HanderBase family
-
-my @templatetypes = ('ReturnType');
-for(my $i = 0; $i <= $maxparams; $i++)
-{
-       push @templatetypes, "Param" . $i if($i > 0);
-       print "template <typename " . join(', typename ', @templatetypes) . "> class CoreExport HandlerBase" . $i . "\n";
-       print "{\n";
-       print " public:\n";
-       print " virtual ReturnType Call(" . join(', ', @templatetypes[1..$#templatetypes]) . ") = 0;\n";
-       print " virtual ~HandlerBase" . $i . "() { }\n";
-       print "};\n\n";
-}
-
-# And now the caller family
-
-print "template <typename HandlerType> class caller\n";
-print "{\n";
-print " public:\n";
-print "        HandlerType* target;\n\n";
-print "        caller(HandlerType* initial)\n";
-print "        : target(initial)\n";
-print "        { }\n\n";
-print "        virtual ~caller() { }\n\n";
-print "        caller& operator=(HandlerType* newtarget)\n";
-print "        {\n";
-print "                target = newtarget;\n";
-print "                return *this;\n";
-print "        }\n";
-print "};\n\n";
-
-
-
-
-@templatetypes = ('ReturnType');
-for(my $i = 0; $i <= $maxparams; $i++)
-{
-       push @templatetypes, "Param" . $i if($i > 0);
-       
-       my $handlertype = "HandlerBase" . $i . "<" . join(', ', @templatetypes) . ">";
-       my @templatetypepairs = map { $_ . " " . lc($_) }  @templatetypes;
-       my @lctemplatetypes = map(lc, @templatetypes);
-       
-       print "template <typename " . join(', typename ', @templatetypes) . "> class caller" . $i . " : public caller< " . $handlertype . " >\n";
-       print "{\n";
-       print " public:\n";
-       print " caller" . $i . "(" . $handlertype . "* initial)\n";
-       print " : caller< " . $handlertype. " >::caller(initial)\n";
-       print " { }\n\n";
-       print " ReturnType operator() (" . join(', ', @templatetypepairs[1..$#templatetypepairs]) . ")\n";
-       print " {\n";
-       print "         return this->target->Call(" . join(', ', @lctemplatetypes[1..$#lctemplatetypes]) . ");\n";
-       print " }\n";
-       print "};\n\n";
-}
-
diff --git a/tools/gdbargs b/tools/gdbargs
deleted file mode 100644 (file)
index 244c1b2..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-handle SIGPIPE pass nostop noprint
-handle SIGHUP pass nostop noprint
-run
diff --git a/tools/genssl b/tools/genssl
new file mode 100755 (executable)
index 0000000..e4d5bf1
--- /dev/null
@@ -0,0 +1,156 @@
+#!/usr/bin/env perl
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+#   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+#   Copyright (C) 2007 Craig Edwards <craigedwards@brainbox.cc>
+#   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/>.
+#
+
+
+BEGIN {
+       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/console.pm module will not
+#            be available!
+
+sub prompt($$) {
+       my ($question, $default) = @_;
+       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>);
+       say '';
+       return $answer ? $answer : $default;
+}
+
+if ($#ARGV != 0 || $ARGV[0] !~ /^(?:auto|gnutls|openssl)$/i) {
+       say STDERR "Usage: $0 <auto|gnutls|openssl>";
+       exit 1;
+}
+
+# On OS X the GnuTLS certtool is prefixed to avoid collision with the system certtool.
+my $certtool = $^O eq 'darwin' ? 'gnutls-certtool' : 'certtool';
+
+# Check whether the user has the required tools installed.
+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.
+my $tool = lc $ARGV[0];
+
+# If the user has not explicitly specified a framework then detect one.
+if ($tool eq 'auto') {
+       if ($has_gnutls) {
+               $tool = 'gnutls';
+       } elsif ($has_openssl) {
+               $tool = 'openssl';
+       } else {
+               say STDERR "SSL generation failed: could not find $certtool or openssl in the PATH!";
+               exit 1;
+       }
+} elsif ($tool eq 'gnutls' && !$has_gnutls) {
+       say STDERR "SSL generation failed: could not find '$certtool' in the PATH!";
+       exit 1;
+} elsif ($tool eq 'openssl' && !$has_openssl) {
+       say STDERR 'SSL generation failed: could not find \'openssl\' in the PATH!';
+       exit 1;
+}
+
+# Harvest information needed to generate the certificate.
+my $common_name = prompt('What is the hostname of your server?', 'irc.example.com');
+my $email = prompt('What email address can you be contacted at?', 'example@example.com');
+my $unit = prompt('What is the name of your unit?', 'Server Admins');
+my $organization = prompt('What is the name of your organization?', 'Example IRC Network');
+my $city = prompt('What city are you located in?', 'Example City');
+my $state = prompt('What state are you located in?', 'Example State');
+my $country = prompt('What is the ISO 3166-1 code for the country you are located in?', 'XZ');
+my $days = prompt('How many days do you want your certificate to be valid for?', '365');
+
+# Contains the SSL certificate in DER form.
+my $dercert;
+
+# Contains the exit code of openssl/gnutls-certtool.
+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"
+email           = "$email"
+unit            = "$unit"
+organization    = "$organization"
+locality        = "$city"
+state           = "$state"
+country         = "$country"
+expiration_days = $days
+tls_www_client
+tls_www_server
+signing_key
+encryption_key
+cert_signing_key
+crl_signing_key
+code_signing_key
+ocsp_signing_key
+time_stamping_key
+__GNUTLS_END__
+       close($tmp);
+       $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-request --load-privkey key.pem --outfile csr.pem --template $tmp";
+       $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();
+       print $tmp <<__OPENSSL_END__;
+$country
+$state
+$city
+$organization
+$unit
+$common_name
+$email
+.
+$organization
+__OPENSSL_END__
+       close($tmp);
+       $status ||= system "cat $tmp | openssl req -x509 -nodes -newkey rsa:2048 -keyout key.pem -out cert.pem -days $days 2>/dev/null";
+       $status ||= system "cat $tmp | openssl req -new -nodes -key key.pem -out csr.pem 2>/dev/null";
+       $status ||= system 'openssl dhparam -out dhparams.pem 2048';
+       $dercert = `openssl x509 -in cert.pem -outform DER` unless $status;
+}
+
+if ($status) {
+       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);
+       say '';
+       say 'If you are using the self-signed certificate then 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
new file mode 100755 (executable)
index 0000000..54d22f9
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/env perl
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+#   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
+# 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;
+       unless (-f 'configure') {
+               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;
+
+$ENV{INSPIRCD_DEBUG} = 3;
+$ENV{INSPIRCD_VERBOSE} = 1;
+
+system 'git', 'clean', '-dfx';
+
+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 = 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';
+       foreach my $socketengine (@socketengines) {
+               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;
+               }
+               if (system 'make', '--jobs', get_cpu_count, 'install') {
+                       say "Failed to compile using the $compiler compiler and the $socketengine socket engine!";
+                       exit 1;
+               }
+               say "Building using the $compiler compiler and the $socketengine socket engine succeeded!";
+       }
+
+       system 'git', 'clean', '-dfx';
+}
index 42b07fa251a37003384dba0059896d76bee3a8aa..ad5d98e4ef6a177726118ea27b9ae4e1b8b634b4 100755 (executable)
@@ -1,15 +1,12 @@
 #!/bin/bash
-set -v
+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
+       sudo apt-get install --assume-yes libgnutls-dev libldap2-dev libmaxminddb-dev libmbedtls-dev libmysqlclient-dev libpcre3-dev libpq-dev libre2-dev libsqlite3-dev libssl-dev libtre-dev
 else
        >&2 echo "'$TRAVIS_OS_NAME' is an unknown Travis CI environment!"
        exit 1
 fi
-set -e
-./configure --enable-extras=m_geoip.cpp,m_ldapauth.cpp,m_ldapoper.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
-./configure --with-cc=$CXX
-make -j4 install
-./run/bin/inspircd --version
+export TEST_BUILD_MODULES="m_geo_maxmind.cpp,m_ldap.cpp,m_mysql.cpp,m_pgsql.cpp,m_regex_pcre.cpp,m_regex_posix.cpp,m_regex_re2.cpp,m_regex_stdlib.cpp,m_regex_tre.cpp,m_sqlite3.cpp,m_ssl_gnutls.cpp,m_ssl_mbedtls.cpp,m_ssl_openssl.cpp,m_sslrehashsignal.cpp"
+./tools/test-build $CXX
diff --git a/vendor/README.md b/vendor/README.md
new file mode 100644 (file)
index 0000000..a97e99c
--- /dev/null
@@ -0,0 +1,43 @@
+# Vendored Libraries
+
+This directory contains vendored dependencies that are shipped with InspIRCd to avoid users needing to install them manually.
+
+## bcrypt
+
+**Author** &mdash; Solar Designer &lt;solar@openwall.com&gt;
+
+**Last Updated** &mdash; 2018-08-14 (v1.3)
+
+**License** &mdash; Public Domain
+
+**Website** &mdash; [https://www.openwall.com/crypt/](https://www.openwall.com/crypt/)
+
+## http_parser
+
+**Author** &mdash; Joyent, Inc. and other Node contributors
+
+**Last Updated** &mdash; 2019-04-25 (v2.9.2)
+
+**License** &mdash; MIT License
+
+**Website** &mdash; [https://github.com/nodejs/http-parser](https://github.com/nodejs/http-parser)
+
+## sha256
+
+**Author** &mdash; Olivier Gay &lt;olivier.gay@a3.epfl.ch&gt;
+
+**Last Updated** &mdash; 2018-09-06 (2007-02-02)
+
+**License** &mdash; 3-clause BSD License
+
+**Website** &mdash; [http://www.ouah.org/ogay/sha2/](http://www.ouah.org/ogay/sha2/)
+
+## utfcpp
+
+**Author** &mdash; Nemanja Trifunovic
+
+**Last Updated** &mdash; 2019-04-25 (ad27c7d5e0e5b4c3baca4a2e70c0336b68c0dffb)
+
+**License** &mdash; Boost Software License
+
+**Website** &mdash; [https://github.com/nemtrif/utfcpp](https://github.com/nemtrif/utfcpp)
diff --git a/vendor/bcrypt/crypt_blowfish.c b/vendor/bcrypt/crypt_blowfish.c
new file mode 100644 (file)
index 0000000..9d3f3be
--- /dev/null
@@ -0,0 +1,907 @@
+/*
+ * 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>
+
+#include <errno.h>
+#ifndef __set_errno
+#define __set_errno(val) errno = (val)
+#endif
+
+/* Just to make sure the prototypes match the actual definitions */
+#include "crypt_blowfish.h"
+
+#ifdef __i386__
+#define BF_ASM                         1
+#define BF_SCALE                       1
+#elif defined(__x86_64__) || defined(__alpha__) || defined(__hppa__)
+#define BF_ASM                         0
+#define BF_SCALE                       1
+#else
+#define BF_ASM                         0
+#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];
+
+#if BF_ASM
+#define BF_body() \
+       _BF_body_r(&data.ctx);
+#else
+#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]);
+#endif
+
+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)
+{
+#if BF_ASM
+       extern void _BF_body_r(BF_ctx *ctx);
+#endif
+       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) {
+               __set_errno(ERANGE);
+               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] != '$') {
+               __set_errno(EINVAL);
+               return NULL;
+       }
+
+       count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
+       if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) {
+               __set_errno(EINVAL);
+               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;
+}
+
+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.
+ */
+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 save_errno, 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);
+       save_errno = errno;
+
+/*
+ * 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));
+       }
+
+       __set_errno(save_errno);
+       if (ok)
+               return retval;
+
+/* Should not happen */
+       _crypt_output_magic(setting, output, size);
+       __set_errno(EINVAL); /* pretend we don't support this hash type */
+       return NULL;
+}
+
+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';
+               __set_errno((output_size < 7 + 22 + 1) ? ERANGE : EINVAL);
+               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;
+}
diff --git a/vendor/bcrypt/crypt_blowfish.h b/vendor/bcrypt/crypt_blowfish.h
new file mode 100644 (file)
index 0000000..2ee0d8c
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Written by Solar Designer <solar at openwall.com> in 2000-2011.
+ * 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) 2000-2011 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.
+ *
+ * See crypt_blowfish.c for more information.
+ */
+
+#ifndef _CRYPT_BLOWFISH_H
+#define _CRYPT_BLOWFISH_H
+
+extern int _crypt_output_magic(const char *setting, char *output, int size);
+extern char *_crypt_blowfish_rn(const char *key, const char *setting,
+       char *output, int size);
+extern char *_crypt_gensalt_blowfish_rn(const char *prefix,
+       unsigned long count,
+       const char *input, int size, char *output, int output_size);
+
+#endif
diff --git a/vendor/http_parser/http_parser.c b/vendor/http_parser/http_parser.c
new file mode 100644 (file)
index 0000000..4896385
--- /dev/null
@@ -0,0 +1,2498 @@
+/* Copyright Joyent, Inc. and other Node contributors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include "http_parser.h"
+#include <assert.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE;
+
+#ifndef ULLONG_MAX
+# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef BIT_AT
+# define BIT_AT(a, i)                                                \
+  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
+   (1 << ((unsigned int) (i) & 7))))
+#endif
+
+#ifndef ELEM_AT
+# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
+#endif
+
+#define SET_ERRNO(e)                                                 \
+do {                                                                 \
+  parser->nread = nread;                                             \
+  parser->http_errno = (e);                                          \
+} while(0)
+
+#define CURRENT_STATE() p_state
+#define UPDATE_STATE(V) p_state = (enum state) (V);
+#define RETURN(V)                                                    \
+do {                                                                 \
+  parser->nread = nread;                                             \
+  parser->state = CURRENT_STATE();                                   \
+  return (V);                                                        \
+} while (0);
+#define REEXECUTE()                                                  \
+  goto reexecute;                                                    \
+
+
+#ifdef __GNUC__
+# define LIKELY(X) __builtin_expect(!!(X), 1)
+# define UNLIKELY(X) __builtin_expect(!!(X), 0)
+#else
+# define LIKELY(X) (X)
+# define UNLIKELY(X) (X)
+#endif
+
+
+/* Run the notify callback FOR, returning ER if it fails */
+#define CALLBACK_NOTIFY_(FOR, ER)                                    \
+do {                                                                 \
+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
+                                                                     \
+  if (LIKELY(settings->on_##FOR)) {                                  \
+    parser->state = CURRENT_STATE();                                 \
+    if (UNLIKELY(0 != settings->on_##FOR(parser))) {                 \
+      SET_ERRNO(HPE_CB_##FOR);                                       \
+    }                                                                \
+    UPDATE_STATE(parser->state);                                     \
+                                                                     \
+    /* We either errored above or got paused; get out */             \
+    if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {             \
+      return (ER);                                                   \
+    }                                                                \
+  }                                                                  \
+} while (0)
+
+/* Run the notify callback FOR and consume the current byte */
+#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)
+
+/* Run the notify callback FOR and don't consume the current byte */
+#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)
+
+/* Run data callback FOR with LEN bytes, returning ER if it fails */
+#define CALLBACK_DATA_(FOR, LEN, ER)                                 \
+do {                                                                 \
+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
+                                                                     \
+  if (FOR##_mark) {                                                  \
+    if (LIKELY(settings->on_##FOR)) {                                \
+      parser->state = CURRENT_STATE();                               \
+      if (UNLIKELY(0 !=                                              \
+                   settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
+        SET_ERRNO(HPE_CB_##FOR);                                     \
+      }                                                              \
+      UPDATE_STATE(parser->state);                                   \
+                                                                     \
+      /* We either errored above or got paused; get out */           \
+      if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {           \
+        return (ER);                                                 \
+      }                                                              \
+    }                                                                \
+    FOR##_mark = NULL;                                               \
+  }                                                                  \
+} while (0)
+
+/* Run the data callback FOR and consume the current byte */
+#define CALLBACK_DATA(FOR)                                           \
+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
+
+/* Run the data callback FOR and don't consume the current byte */
+#define CALLBACK_DATA_NOADVANCE(FOR)                                 \
+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
+
+/* Set the mark FOR; non-destructive if mark is already set */
+#define MARK(FOR)                                                    \
+do {                                                                 \
+  if (!FOR##_mark) {                                                 \
+    FOR##_mark = p;                                                  \
+  }                                                                  \
+} while (0)
+
+/* Don't allow the total size of the HTTP headers (including the status
+ * line) to exceed max_header_size.  This check is here to protect
+ * embedders against denial-of-service attacks where the attacker feeds
+ * us a never-ending header that the embedder keeps buffering.
+ *
+ * This check is arguably the responsibility of embedders but we're doing
+ * it on the embedder's behalf because most won't bother and this way we
+ * make the web a little safer.  max_header_size is still far bigger
+ * than any reasonable request or response so this should never affect
+ * day-to-day operation.
+ */
+#define COUNT_HEADER_SIZE(V)                                         \
+do {                                                                 \
+  nread += (uint32_t)(V);                                            \
+  if (UNLIKELY(nread > max_header_size)) {                           \
+    SET_ERRNO(HPE_HEADER_OVERFLOW);                                  \
+    goto error;                                                      \
+  }                                                                  \
+} while (0)
+
+
+#define PROXY_CONNECTION "proxy-connection"
+#define CONNECTION "connection"
+#define CONTENT_LENGTH "content-length"
+#define TRANSFER_ENCODING "transfer-encoding"
+#define UPGRADE "upgrade"
+#define CHUNKED "chunked"
+#define KEEP_ALIVE "keep-alive"
+#define CLOSE "close"
+
+
+static const char *method_strings[] =
+  {
+#define XX(num, name, string) #string,
+  HTTP_METHOD_MAP(XX)
+#undef XX
+  };
+
+
+/* Tokens as defined by rfc 2616. Also lowercases them.
+ *        token       = 1*<any CHAR except CTLs or separators>
+ *     separators     = "(" | ")" | "<" | ">" | "@"
+ *                    | "," | ";" | ":" | "\" | <">
+ *                    | "/" | "[" | "]" | "?" | "="
+ *                    | "{" | "}" | SP | HT
+ */
+static const char tokens[256] = {
+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
+       ' ',     '!',      0,      '#',     '$',     '%',     '&',    '\'',
+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
+        0,       0,      '*',     '+',      0,      '-',     '.',      0,
+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
+       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
+       '8',     '9',      0,       0,       0,       0,       0,       0,
+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
+        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
+       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
+       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
+       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };
+
+
+static const int8_t unhex[256] =
+  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  };
+
+
+#if HTTP_PARSER_STRICT
+# define T(v) 0
+#else
+# define T(v) v
+#endif
+
+
+static const uint8_t normal_url_char[32] = {
+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
+        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,
+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
+        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,
+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };
+
+#undef T
+
+enum state
+  { s_dead = 1 /* important that this is > 0 */
+
+  , s_start_req_or_res
+  , s_res_or_resp_H
+  , s_start_res
+  , s_res_H
+  , s_res_HT
+  , s_res_HTT
+  , s_res_HTTP
+  , s_res_http_major
+  , s_res_http_dot
+  , s_res_http_minor
+  , s_res_http_end
+  , s_res_first_status_code
+  , s_res_status_code
+  , s_res_status_start
+  , s_res_status
+  , s_res_line_almost_done
+
+  , s_start_req
+
+  , s_req_method
+  , s_req_spaces_before_url
+  , s_req_schema
+  , s_req_schema_slash
+  , s_req_schema_slash_slash
+  , s_req_server_start
+  , s_req_server
+  , s_req_server_with_at
+  , s_req_path
+  , s_req_query_string_start
+  , s_req_query_string
+  , s_req_fragment_start
+  , s_req_fragment
+  , s_req_http_start
+  , s_req_http_H
+  , s_req_http_HT
+  , s_req_http_HTT
+  , s_req_http_HTTP
+  , s_req_http_I
+  , s_req_http_IC
+  , s_req_http_major
+  , s_req_http_dot
+  , s_req_http_minor
+  , s_req_http_end
+  , s_req_line_almost_done
+
+  , s_header_field_start
+  , s_header_field
+  , s_header_value_discard_ws
+  , s_header_value_discard_ws_almost_done
+  , s_header_value_discard_lws
+  , s_header_value_start
+  , s_header_value
+  , s_header_value_lws
+
+  , s_header_almost_done
+
+  , s_chunk_size_start
+  , s_chunk_size
+  , s_chunk_parameters
+  , s_chunk_size_almost_done
+
+  , s_headers_almost_done
+  , s_headers_done
+
+  /* Important: 's_headers_done' must be the last 'header' state. All
+   * states beyond this must be 'body' states. It is used for overflow
+   * checking. See the PARSING_HEADER() macro.
+   */
+
+  , s_chunk_data
+  , s_chunk_data_almost_done
+  , s_chunk_data_done
+
+  , s_body_identity
+  , s_body_identity_eof
+
+  , s_message_done
+  };
+
+
+#define PARSING_HEADER(state) (state <= s_headers_done)
+
+
+enum header_states
+  { h_general = 0
+  , h_C
+  , h_CO
+  , h_CON
+
+  , h_matching_connection
+  , h_matching_proxy_connection
+  , h_matching_content_length
+  , h_matching_transfer_encoding
+  , h_matching_upgrade
+
+  , h_connection
+  , h_content_length
+  , h_content_length_num
+  , h_content_length_ws
+  , h_transfer_encoding
+  , h_upgrade
+
+  , h_matching_transfer_encoding_chunked
+  , h_matching_connection_token_start
+  , h_matching_connection_keep_alive
+  , h_matching_connection_close
+  , h_matching_connection_upgrade
+  , h_matching_connection_token
+
+  , h_transfer_encoding_chunked
+  , h_connection_keep_alive
+  , h_connection_close
+  , h_connection_upgrade
+  };
+
+enum http_host_state
+  {
+    s_http_host_dead = 1
+  , s_http_userinfo_start
+  , s_http_userinfo
+  , s_http_host_start
+  , s_http_host_v6_start
+  , s_http_host
+  , s_http_host_v6
+  , s_http_host_v6_end
+  , s_http_host_v6_zone_start
+  , s_http_host_v6_zone
+  , s_http_host_port_start
+  , s_http_host_port
+};
+
+/* Macros for character classes; depends on strict-mode  */
+#define CR                  '\r'
+#define LF                  '\n'
+#define LOWER(c)            (unsigned char)(c | 0x20)
+#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \
+  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
+  (c) == ')')
+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
+  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
+  (c) == '$' || (c) == ',')
+
+#define STRICT_TOKEN(c)     ((c == ' ') ? 0 : tokens[(unsigned char)c])
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c)            STRICT_TOKEN(c)
+#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))
+#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define TOKEN(c)            tokens[(unsigned char)c]
+#define IS_URL_CHAR(c)                                                         \
+  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
+#define IS_HOST_CHAR(c)                                                        \
+  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+/**
+ * Verify that a char is a valid visible (printable) US-ASCII
+ * character or %x80-FF
+ **/
+#define IS_HEADER_CHAR(ch)                                                     \
+  (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
+
+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
+
+
+#if HTTP_PARSER_STRICT
+# define STRICT_CHECK(cond)                                          \
+do {                                                                 \
+  if (cond) {                                                        \
+    SET_ERRNO(HPE_STRICT);                                           \
+    goto error;                                                      \
+  }                                                                  \
+} while (0)
+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
+#else
+# define STRICT_CHECK(cond)
+# define NEW_MESSAGE() start_state
+#endif
+
+
+/* Map errno values to strings for human-readable output */
+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
+static struct {
+  const char *name;
+  const char *description;
+} http_strerror_tab[] = {
+  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
+};
+#undef HTTP_STRERROR_GEN
+
+int http_message_needs_eof(const http_parser *parser);
+
+/* Our URL parser.
+ *
+ * This is designed to be shared by http_parser_execute() for URL validation,
+ * hence it has a state transition + byte-for-byte interface. In addition, it
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
+ * work of turning state transitions URL components for its API.
+ *
+ * This function should only be invoked with non-space characters. It is
+ * assumed that the caller cares about (and can detect) the transition between
+ * URL and non-URL states by looking for these.
+ */
+static enum state
+parse_url_char(enum state s, const char ch)
+{
+  if (ch == ' ' || ch == '\r' || ch == '\n') {
+    return s_dead;
+  }
+
+#if HTTP_PARSER_STRICT
+  if (ch == '\t' || ch == '\f') {
+    return s_dead;
+  }
+#endif
+
+  switch (s) {
+    case s_req_spaces_before_url:
+      /* Proxied requests are followed by scheme of an absolute URI (alpha).
+       * All methods except CONNECT are followed by '/' or '*'.
+       */
+
+      if (ch == '/' || ch == '*') {
+        return s_req_path;
+      }
+
+      if (IS_ALPHA(ch)) {
+        return s_req_schema;
+      }
+
+      break;
+
+    case s_req_schema:
+      if (IS_ALPHA(ch)) {
+        return s;
+      }
+
+      if (ch == ':') {
+        return s_req_schema_slash;
+      }
+
+      break;
+
+    case s_req_schema_slash:
+      if (ch == '/') {
+        return s_req_schema_slash_slash;
+      }
+
+      break;
+
+    case s_req_schema_slash_slash:
+      if (ch == '/') {
+        return s_req_server_start;
+      }
+
+      break;
+
+    case s_req_server_with_at:
+      if (ch == '@') {
+        return s_dead;
+      }
+
+    /* fall through */
+    case s_req_server_start:
+    case s_req_server:
+      if (ch == '/') {
+        return s_req_path;
+      }
+
+      if (ch == '?') {
+        return s_req_query_string_start;
+      }
+
+      if (ch == '@') {
+        return s_req_server_with_at;
+      }
+
+      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
+        return s_req_server;
+      }
+
+      break;
+
+    case s_req_path:
+      if (IS_URL_CHAR(ch)) {
+        return s;
+      }
+
+      switch (ch) {
+        case '?':
+          return s_req_query_string_start;
+
+        case '#':
+          return s_req_fragment_start;
+      }
+
+      break;
+
+    case s_req_query_string_start:
+    case s_req_query_string:
+      if (IS_URL_CHAR(ch)) {
+        return s_req_query_string;
+      }
+
+      switch (ch) {
+        case '?':
+          /* allow extra '?' in query string */
+          return s_req_query_string;
+
+        case '#':
+          return s_req_fragment_start;
+      }
+
+      break;
+
+    case s_req_fragment_start:
+      if (IS_URL_CHAR(ch)) {
+        return s_req_fragment;
+      }
+
+      switch (ch) {
+        case '?':
+          return s_req_fragment;
+
+        case '#':
+          return s;
+      }
+
+      break;
+
+    case s_req_fragment:
+      if (IS_URL_CHAR(ch)) {
+        return s;
+      }
+
+      switch (ch) {
+        case '?':
+        case '#':
+          return s;
+      }
+
+      break;
+
+    default:
+      break;
+  }
+
+  /* We should never fall out of the switch above unless there's an error */
+  return s_dead;
+}
+
+size_t http_parser_execute (http_parser *parser,
+                            const http_parser_settings *settings,
+                            const char *data,
+                            size_t len)
+{
+  char c, ch;
+  int8_t unhex_val;
+  const char *p = data;
+  const char *header_field_mark = 0;
+  const char *header_value_mark = 0;
+  const char *url_mark = 0;
+  const char *body_mark = 0;
+  const char *status_mark = 0;
+  enum state p_state = (enum state) parser->state;
+  const unsigned int lenient = parser->lenient_http_headers;
+  uint32_t nread = parser->nread;
+
+  /* We're in an error state. Don't bother doing anything. */
+  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+    return 0;
+  }
+
+  if (len == 0) {
+    switch (CURRENT_STATE()) {
+      case s_body_identity_eof:
+        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
+         * we got paused.
+         */
+        CALLBACK_NOTIFY_NOADVANCE(message_complete);
+        return 0;
+
+      case s_dead:
+      case s_start_req_or_res:
+      case s_start_res:
+      case s_start_req:
+        return 0;
+
+      default:
+        SET_ERRNO(HPE_INVALID_EOF_STATE);
+        return 1;
+    }
+  }
+
+
+  if (CURRENT_STATE() == s_header_field)
+    header_field_mark = data;
+  if (CURRENT_STATE() == s_header_value)
+    header_value_mark = data;
+  switch (CURRENT_STATE()) {
+  case s_req_path:
+  case s_req_schema:
+  case s_req_schema_slash:
+  case s_req_schema_slash_slash:
+  case s_req_server_start:
+  case s_req_server:
+  case s_req_server_with_at:
+  case s_req_query_string_start:
+  case s_req_query_string:
+  case s_req_fragment_start:
+  case s_req_fragment:
+    url_mark = data;
+    break;
+  case s_res_status:
+    status_mark = data;
+    break;
+  default:
+    break;
+  }
+
+  for (p=data; p != data + len; p++) {
+    ch = *p;
+
+    if (PARSING_HEADER(CURRENT_STATE()))
+      COUNT_HEADER_SIZE(1);
+
+reexecute:
+    switch (CURRENT_STATE()) {
+
+      case s_dead:
+        /* this state is used after a 'Connection: close' message
+         * the parser will error out if it reads another message
+         */
+        if (LIKELY(ch == CR || ch == LF))
+          break;
+
+        SET_ERRNO(HPE_CLOSED_CONNECTION);
+        goto error;
+
+      case s_start_req_or_res:
+      {
+        if (ch == CR || ch == LF)
+          break;
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        if (ch == 'H') {
+          UPDATE_STATE(s_res_or_resp_H);
+
+          CALLBACK_NOTIFY(message_begin);
+        } else {
+          parser->type = HTTP_REQUEST;
+          UPDATE_STATE(s_start_req);
+          REEXECUTE();
+        }
+
+        break;
+      }
+
+      case s_res_or_resp_H:
+        if (ch == 'T') {
+          parser->type = HTTP_RESPONSE;
+          UPDATE_STATE(s_res_HT);
+        } else {
+          if (UNLIKELY(ch != 'E')) {
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+          }
+
+          parser->type = HTTP_REQUEST;
+          parser->method = HTTP_HEAD;
+          parser->index = 2;
+          UPDATE_STATE(s_req_method);
+        }
+        break;
+
+      case s_start_res:
+      {
+        if (ch == CR || ch == LF)
+          break;
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        if (ch == 'H') {
+          UPDATE_STATE(s_res_H);
+        } else {
+          SET_ERRNO(HPE_INVALID_CONSTANT);
+          goto error;
+        }
+
+        CALLBACK_NOTIFY(message_begin);
+        break;
+      }
+
+      case s_res_H:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_res_HT);
+        break;
+
+      case s_res_HT:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_res_HTT);
+        break;
+
+      case s_res_HTT:
+        STRICT_CHECK(ch != 'P');
+        UPDATE_STATE(s_res_HTTP);
+        break;
+
+      case s_res_HTTP:
+        STRICT_CHECK(ch != '/');
+        UPDATE_STATE(s_res_http_major);
+        break;
+
+      case s_res_http_major:
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major = ch - '0';
+        UPDATE_STATE(s_res_http_dot);
+        break;
+
+      case s_res_http_dot:
+      {
+        if (UNLIKELY(ch != '.')) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        UPDATE_STATE(s_res_http_minor);
+        break;
+      }
+
+      case s_res_http_minor:
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor = ch - '0';
+        UPDATE_STATE(s_res_http_end);
+        break;
+
+      case s_res_http_end:
+      {
+        if (UNLIKELY(ch != ' ')) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        UPDATE_STATE(s_res_first_status_code);
+        break;
+      }
+
+      case s_res_first_status_code:
+      {
+        if (!IS_NUM(ch)) {
+          if (ch == ' ') {
+            break;
+          }
+
+          SET_ERRNO(HPE_INVALID_STATUS);
+          goto error;
+        }
+        parser->status_code = ch - '0';
+        UPDATE_STATE(s_res_status_code);
+        break;
+      }
+
+      case s_res_status_code:
+      {
+        if (!IS_NUM(ch)) {
+          switch (ch) {
+            case ' ':
+              UPDATE_STATE(s_res_status_start);
+              break;
+            case CR:
+            case LF:
+              UPDATE_STATE(s_res_status_start);
+              REEXECUTE();
+              break;
+            default:
+              SET_ERRNO(HPE_INVALID_STATUS);
+              goto error;
+          }
+          break;
+        }
+
+        parser->status_code *= 10;
+        parser->status_code += ch - '0';
+
+        if (UNLIKELY(parser->status_code > 999)) {
+          SET_ERRNO(HPE_INVALID_STATUS);
+          goto error;
+        }
+
+        break;
+      }
+
+      case s_res_status_start:
+      {
+        MARK(status);
+        UPDATE_STATE(s_res_status);
+        parser->index = 0;
+
+        if (ch == CR || ch == LF)
+          REEXECUTE();
+
+        break;
+      }
+
+      case s_res_status:
+        if (ch == CR) {
+          UPDATE_STATE(s_res_line_almost_done);
+          CALLBACK_DATA(status);
+          break;
+        }
+
+        if (ch == LF) {
+          UPDATE_STATE(s_header_field_start);
+          CALLBACK_DATA(status);
+          break;
+        }
+
+        break;
+
+      case s_res_line_almost_done:
+        STRICT_CHECK(ch != LF);
+        UPDATE_STATE(s_header_field_start);
+        break;
+
+      case s_start_req:
+      {
+        if (ch == CR || ch == LF)
+          break;
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        if (UNLIKELY(!IS_ALPHA(ch))) {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        parser->method = (enum http_method) 0;
+        parser->index = 1;
+        switch (ch) {
+          case 'A': parser->method = HTTP_ACL; break;
+          case 'B': parser->method = HTTP_BIND; break;
+          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
+          case 'D': parser->method = HTTP_DELETE; break;
+          case 'G': parser->method = HTTP_GET; break;
+          case 'H': parser->method = HTTP_HEAD; break;
+          case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
+          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
+          case 'N': parser->method = HTTP_NOTIFY; break;
+          case 'O': parser->method = HTTP_OPTIONS; break;
+          case 'P': parser->method = HTTP_POST;
+            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
+            break;
+          case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
+          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break;
+          case 'T': parser->method = HTTP_TRACE; break;
+          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
+          default:
+            SET_ERRNO(HPE_INVALID_METHOD);
+            goto error;
+        }
+        UPDATE_STATE(s_req_method);
+
+        CALLBACK_NOTIFY(message_begin);
+
+        break;
+      }
+
+      case s_req_method:
+      {
+        const char *matcher;
+        if (UNLIKELY(ch == '\0')) {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        matcher = method_strings[parser->method];
+        if (ch == ' ' && matcher[parser->index] == '\0') {
+          UPDATE_STATE(s_req_spaces_before_url);
+        } else if (ch == matcher[parser->index]) {
+          ; /* nada */
+        } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') {
+
+          switch (parser->method << 16 | parser->index << 8 | ch) {
+#define XX(meth, pos, ch, new_meth) \
+            case (HTTP_##meth << 16 | pos << 8 | ch): \
+              parser->method = HTTP_##new_meth; break;
+
+            XX(POST,      1, 'U', PUT)
+            XX(POST,      1, 'A', PATCH)
+            XX(POST,      1, 'R', PROPFIND)
+            XX(PUT,       2, 'R', PURGE)
+            XX(CONNECT,   1, 'H', CHECKOUT)
+            XX(CONNECT,   2, 'P', COPY)
+            XX(MKCOL,     1, 'O', MOVE)
+            XX(MKCOL,     1, 'E', MERGE)
+            XX(MKCOL,     1, '-', MSEARCH)
+            XX(MKCOL,     2, 'A', MKACTIVITY)
+            XX(MKCOL,     3, 'A', MKCALENDAR)
+            XX(SUBSCRIBE, 1, 'E', SEARCH)
+            XX(SUBSCRIBE, 1, 'O', SOURCE)
+            XX(REPORT,    2, 'B', REBIND)
+            XX(PROPFIND,  4, 'P', PROPPATCH)
+            XX(LOCK,      1, 'I', LINK)
+            XX(UNLOCK,    2, 'S', UNSUBSCRIBE)
+            XX(UNLOCK,    2, 'B', UNBIND)
+            XX(UNLOCK,    3, 'I', UNLINK)
+#undef XX
+            default:
+              SET_ERRNO(HPE_INVALID_METHOD);
+              goto error;
+          }
+        } else {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        ++parser->index;
+        break;
+      }
+
+      case s_req_spaces_before_url:
+      {
+        if (ch == ' ') break;
+
+        MARK(url);
+        if (parser->method == HTTP_CONNECT) {
+          UPDATE_STATE(s_req_server_start);
+        }
+
+        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+        if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+          SET_ERRNO(HPE_INVALID_URL);
+          goto error;
+        }
+
+        break;
+      }
+
+      case s_req_schema:
+      case s_req_schema_slash:
+      case s_req_schema_slash_slash:
+      case s_req_server_start:
+      {
+        switch (ch) {
+          /* No whitespace allowed here */
+          case ' ':
+          case CR:
+          case LF:
+            SET_ERRNO(HPE_INVALID_URL);
+            goto error;
+          default:
+            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+              SET_ERRNO(HPE_INVALID_URL);
+              goto error;
+            }
+        }
+
+        break;
+      }
+
+      case s_req_server:
+      case s_req_server_with_at:
+      case s_req_path:
+      case s_req_query_string_start:
+      case s_req_query_string:
+      case s_req_fragment_start:
+      case s_req_fragment:
+      {
+        switch (ch) {
+          case ' ':
+            UPDATE_STATE(s_req_http_start);
+            CALLBACK_DATA(url);
+            break;
+          case CR:
+          case LF:
+            parser->http_major = 0;
+            parser->http_minor = 9;
+            UPDATE_STATE((ch == CR) ?
+              s_req_line_almost_done :
+              s_header_field_start);
+            CALLBACK_DATA(url);
+            break;
+          default:
+            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+              SET_ERRNO(HPE_INVALID_URL);
+              goto error;
+            }
+        }
+        break;
+      }
+
+      case s_req_http_start:
+        switch (ch) {
+          case ' ':
+            break;
+          case 'H':
+            UPDATE_STATE(s_req_http_H);
+            break;
+          case 'I':
+            if (parser->method == HTTP_SOURCE) {
+              UPDATE_STATE(s_req_http_I);
+              break;
+            }
+            /* fall through */
+          default:
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+        }
+        break;
+
+      case s_req_http_H:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_req_http_HT);
+        break;
+
+      case s_req_http_HT:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_req_http_HTT);
+        break;
+
+      case s_req_http_HTT:
+        STRICT_CHECK(ch != 'P');
+        UPDATE_STATE(s_req_http_HTTP);
+        break;
+
+      case s_req_http_I:
+        STRICT_CHECK(ch != 'C');
+        UPDATE_STATE(s_req_http_IC);
+        break;
+
+      case s_req_http_IC:
+        STRICT_CHECK(ch != 'E');
+        UPDATE_STATE(s_req_http_HTTP);  /* Treat "ICE" as "HTTP". */
+        break;
+
+      case s_req_http_HTTP:
+        STRICT_CHECK(ch != '/');
+        UPDATE_STATE(s_req_http_major);
+        break;
+
+      case s_req_http_major:
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major = ch - '0';
+        UPDATE_STATE(s_req_http_dot);
+        break;
+
+      case s_req_http_dot:
+      {
+        if (UNLIKELY(ch != '.')) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        UPDATE_STATE(s_req_http_minor);
+        break;
+      }
+
+      case s_req_http_minor:
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor = ch - '0';
+        UPDATE_STATE(s_req_http_end);
+        break;
+
+      case s_req_http_end:
+      {
+        if (ch == CR) {
+          UPDATE_STATE(s_req_line_almost_done);
+          break;
+        }
+
+        if (ch == LF) {
+          UPDATE_STATE(s_header_field_start);
+          break;
+        }
+
+        SET_ERRNO(HPE_INVALID_VERSION);
+        goto error;
+        break;
+      }
+
+      /* end of request line */
+      case s_req_line_almost_done:
+      {
+        if (UNLIKELY(ch != LF)) {
+          SET_ERRNO(HPE_LF_EXPECTED);
+          goto error;
+        }
+
+        UPDATE_STATE(s_header_field_start);
+        break;
+      }
+
+      case s_header_field_start:
+      {
+        if (ch == CR) {
+          UPDATE_STATE(s_headers_almost_done);
+          break;
+        }
+
+        if (ch == LF) {
+          /* they might be just sending \n instead of \r\n so this would be
+           * the second \n to denote the end of headers*/
+          UPDATE_STATE(s_headers_almost_done);
+          REEXECUTE();
+        }
+
+        c = TOKEN(ch);
+
+        if (UNLIKELY(!c)) {
+          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+          goto error;
+        }
+
+        MARK(header_field);
+
+        parser->index = 0;
+        UPDATE_STATE(s_header_field);
+
+        switch (c) {
+          case 'c':
+            parser->header_state = h_C;
+            break;
+
+          case 'p':
+            parser->header_state = h_matching_proxy_connection;
+            break;
+
+          case 't':
+            parser->header_state = h_matching_transfer_encoding;
+            break;
+
+          case 'u':
+            parser->header_state = h_matching_upgrade;
+            break;
+
+          default:
+            parser->header_state = h_general;
+            break;
+        }
+        break;
+      }
+
+      case s_header_field:
+      {
+        const char* start = p;
+        for (; p != data + len; p++) {
+          ch = *p;
+          c = TOKEN(ch);
+
+          if (!c)
+            break;
+
+          switch (parser->header_state) {
+            case h_general: {
+              size_t left = data + len - p;
+              const char* pe = p + MIN(left, max_header_size);
+              while (p+1 < pe && TOKEN(p[1])) {
+                p++;
+              }
+              break;
+            }
+
+            case h_C:
+              parser->index++;
+              parser->header_state = (c == 'o' ? h_CO : h_general);
+              break;
+
+            case h_CO:
+              parser->index++;
+              parser->header_state = (c == 'n' ? h_CON : h_general);
+              break;
+
+            case h_CON:
+              parser->index++;
+              switch (c) {
+                case 'n':
+                  parser->header_state = h_matching_connection;
+                  break;
+                case 't':
+                  parser->header_state = h_matching_content_length;
+                  break;
+                default:
+                  parser->header_state = h_general;
+                  break;
+              }
+              break;
+
+            /* connection */
+
+            case h_matching_connection:
+              parser->index++;
+              if (parser->index > sizeof(CONNECTION)-1
+                  || c != CONNECTION[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(CONNECTION)-2) {
+                parser->header_state = h_connection;
+              }
+              break;
+
+            /* proxy-connection */
+
+            case h_matching_proxy_connection:
+              parser->index++;
+              if (parser->index > sizeof(PROXY_CONNECTION)-1
+                  || c != PROXY_CONNECTION[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
+                parser->header_state = h_connection;
+              }
+              break;
+
+            /* content-length */
+
+            case h_matching_content_length:
+              parser->index++;
+              if (parser->index > sizeof(CONTENT_LENGTH)-1
+                  || c != CONTENT_LENGTH[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
+                parser->header_state = h_content_length;
+              }
+              break;
+
+            /* transfer-encoding */
+
+            case h_matching_transfer_encoding:
+              parser->index++;
+              if (parser->index > sizeof(TRANSFER_ENCODING)-1
+                  || c != TRANSFER_ENCODING[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
+                parser->header_state = h_transfer_encoding;
+              }
+              break;
+
+            /* upgrade */
+
+            case h_matching_upgrade:
+              parser->index++;
+              if (parser->index > sizeof(UPGRADE)-1
+                  || c != UPGRADE[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(UPGRADE)-2) {
+                parser->header_state = h_upgrade;
+              }
+              break;
+
+            case h_connection:
+            case h_content_length:
+            case h_transfer_encoding:
+            case h_upgrade:
+              if (ch != ' ') parser->header_state = h_general;
+              break;
+
+            default:
+              assert(0 && "Unknown header_state");
+              break;
+          }
+        }
+
+        if (p == data + len) {
+          --p;
+          COUNT_HEADER_SIZE(p - start);
+          break;
+        }
+
+        COUNT_HEADER_SIZE(p - start);
+
+        if (ch == ':') {
+          UPDATE_STATE(s_header_value_discard_ws);
+          CALLBACK_DATA(header_field);
+          break;
+        }
+
+        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+        goto error;
+      }
+
+      case s_header_value_discard_ws:
+        if (ch == ' ' || ch == '\t') break;
+
+        if (ch == CR) {
+          UPDATE_STATE(s_header_value_discard_ws_almost_done);
+          break;
+        }
+
+        if (ch == LF) {
+          UPDATE_STATE(s_header_value_discard_lws);
+          break;
+        }
+
+        /* fall through */
+
+      case s_header_value_start:
+      {
+        MARK(header_value);
+
+        UPDATE_STATE(s_header_value);
+        parser->index = 0;
+
+        c = LOWER(ch);
+
+        switch (parser->header_state) {
+          case h_upgrade:
+            parser->flags |= F_UPGRADE;
+            parser->header_state = h_general;
+            break;
+
+          case h_transfer_encoding:
+            /* looking for 'Transfer-Encoding: chunked' */
+            if ('c' == c) {
+              parser->header_state = h_matching_transfer_encoding_chunked;
+            } else {
+              parser->header_state = h_general;
+            }
+            break;
+
+          case h_content_length:
+            if (UNLIKELY(!IS_NUM(ch))) {
+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+              goto error;
+            }
+
+            if (parser->flags & F_CONTENTLENGTH) {
+              SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+              goto error;
+            }
+
+            parser->flags |= F_CONTENTLENGTH;
+            parser->content_length = ch - '0';
+            parser->header_state = h_content_length_num;
+            break;
+
+          /* when obsolete line folding is encountered for content length
+           * continue to the s_header_value state */
+          case h_content_length_ws:
+            break;
+
+          case h_connection:
+            /* looking for 'Connection: keep-alive' */
+            if (c == 'k') {
+              parser->header_state = h_matching_connection_keep_alive;
+            /* looking for 'Connection: close' */
+            } else if (c == 'c') {
+              parser->header_state = h_matching_connection_close;
+            } else if (c == 'u') {
+              parser->header_state = h_matching_connection_upgrade;
+            } else {
+              parser->header_state = h_matching_connection_token;
+            }
+            break;
+
+          /* Multi-value `Connection` header */
+          case h_matching_connection_token_start:
+            break;
+
+          default:
+            parser->header_state = h_general;
+            break;
+        }
+        break;
+      }
+
+      case s_header_value:
+      {
+        const char* start = p;
+        enum header_states h_state = (enum header_states) parser->header_state;
+        for (; p != data + len; p++) {
+          ch = *p;
+          if (ch == CR) {
+            UPDATE_STATE(s_header_almost_done);
+            parser->header_state = h_state;
+            CALLBACK_DATA(header_value);
+            break;
+          }
+
+          if (ch == LF) {
+            UPDATE_STATE(s_header_almost_done);
+            COUNT_HEADER_SIZE(p - start);
+            parser->header_state = h_state;
+            CALLBACK_DATA_NOADVANCE(header_value);
+            REEXECUTE();
+          }
+
+          if (!lenient && !IS_HEADER_CHAR(ch)) {
+            SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+            goto error;
+          }
+
+          c = LOWER(ch);
+
+          switch (h_state) {
+            case h_general:
+              {
+                size_t left = data + len - p;
+                const char* pe = p + MIN(left, max_header_size);
+
+                for (; p != pe; p++) {
+                  ch = *p;
+                  if (ch == CR || ch == LF) {
+                    --p;
+                    break;
+                  }
+                  if (!lenient && !IS_HEADER_CHAR(ch)) {
+                    SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+                    goto error;
+                  }
+                }
+                if (p == data + len)
+                  --p;
+                break;
+              }
+
+            case h_connection:
+            case h_transfer_encoding:
+              assert(0 && "Shouldn't get here.");
+              break;
+
+            case h_content_length:
+              if (ch == ' ') break;
+              h_state = h_content_length_num;
+              /* fall through */
+
+            case h_content_length_num:
+            {
+              uint64_t t;
+
+              if (ch == ' ') {
+                h_state = h_content_length_ws;
+                break;
+              }
+
+              if (UNLIKELY(!IS_NUM(ch))) {
+                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                parser->header_state = h_state;
+                goto error;
+              }
+
+              t = parser->content_length;
+              t *= 10;
+              t += ch - '0';
+
+              /* Overflow? Test against a conservative limit for simplicity. */
+              if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
+                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                parser->header_state = h_state;
+                goto error;
+              }
+
+              parser->content_length = t;
+              break;
+            }
+
+            case h_content_length_ws:
+              if (ch == ' ') break;
+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+              parser->header_state = h_state;
+              goto error;
+
+            /* Transfer-Encoding: chunked */
+            case h_matching_transfer_encoding_chunked:
+              parser->index++;
+              if (parser->index > sizeof(CHUNKED)-1
+                  || c != CHUNKED[parser->index]) {
+                h_state = h_general;
+              } else if (parser->index == sizeof(CHUNKED)-2) {
+                h_state = h_transfer_encoding_chunked;
+              }
+              break;
+
+            case h_matching_connection_token_start:
+              /* looking for 'Connection: keep-alive' */
+              if (c == 'k') {
+                h_state = h_matching_connection_keep_alive;
+              /* looking for 'Connection: close' */
+              } else if (c == 'c') {
+                h_state = h_matching_connection_close;
+              } else if (c == 'u') {
+                h_state = h_matching_connection_upgrade;
+              } else if (STRICT_TOKEN(c)) {
+                h_state = h_matching_connection_token;
+              } else if (c == ' ' || c == '\t') {
+                /* Skip lws */
+              } else {
+                h_state = h_general;
+              }
+              break;
+
+            /* looking for 'Connection: keep-alive' */
+            case h_matching_connection_keep_alive:
+              parser->index++;
+              if (parser->index > sizeof(KEEP_ALIVE)-1
+                  || c != KEEP_ALIVE[parser->index]) {
+                h_state = h_matching_connection_token;
+              } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
+                h_state = h_connection_keep_alive;
+              }
+              break;
+
+            /* looking for 'Connection: close' */
+            case h_matching_connection_close:
+              parser->index++;
+              if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
+                h_state = h_matching_connection_token;
+              } else if (parser->index == sizeof(CLOSE)-2) {
+                h_state = h_connection_close;
+              }
+              break;
+
+            /* looking for 'Connection: upgrade' */
+            case h_matching_connection_upgrade:
+              parser->index++;
+              if (parser->index > sizeof(UPGRADE) - 1 ||
+                  c != UPGRADE[parser->index]) {
+                h_state = h_matching_connection_token;
+              } else if (parser->index == sizeof(UPGRADE)-2) {
+                h_state = h_connection_upgrade;
+              }
+              break;
+
+            case h_matching_connection_token:
+              if (ch == ',') {
+                h_state = h_matching_connection_token_start;
+                parser->index = 0;
+              }
+              break;
+
+            case h_transfer_encoding_chunked:
+              if (ch != ' ') h_state = h_general;
+              break;
+
+            case h_connection_keep_alive:
+            case h_connection_close:
+            case h_connection_upgrade:
+              if (ch == ',') {
+                if (h_state == h_connection_keep_alive) {
+                  parser->flags |= F_CONNECTION_KEEP_ALIVE;
+                } else if (h_state == h_connection_close) {
+                  parser->flags |= F_CONNECTION_CLOSE;
+                } else if (h_state == h_connection_upgrade) {
+                  parser->flags |= F_CONNECTION_UPGRADE;
+                }
+                h_state = h_matching_connection_token_start;
+                parser->index = 0;
+              } else if (ch != ' ') {
+                h_state = h_matching_connection_token;
+              }
+              break;
+
+            default:
+              UPDATE_STATE(s_header_value);
+              h_state = h_general;
+              break;
+          }
+        }
+        parser->header_state = h_state;
+
+        if (p == data + len)
+          --p;
+
+        COUNT_HEADER_SIZE(p - start);
+        break;
+      }
+
+      case s_header_almost_done:
+      {
+        if (UNLIKELY(ch != LF)) {
+          SET_ERRNO(HPE_LF_EXPECTED);
+          goto error;
+        }
+
+        UPDATE_STATE(s_header_value_lws);
+        break;
+      }
+
+      case s_header_value_lws:
+      {
+        if (ch == ' ' || ch == '\t') {
+          if (parser->header_state == h_content_length_num) {
+              /* treat obsolete line folding as space */
+              parser->header_state = h_content_length_ws;
+          }
+          UPDATE_STATE(s_header_value_start);
+          REEXECUTE();
+        }
+
+        /* finished the header */
+        switch (parser->header_state) {
+          case h_connection_keep_alive:
+            parser->flags |= F_CONNECTION_KEEP_ALIVE;
+            break;
+          case h_connection_close:
+            parser->flags |= F_CONNECTION_CLOSE;
+            break;
+          case h_transfer_encoding_chunked:
+            parser->flags |= F_CHUNKED;
+            break;
+          case h_connection_upgrade:
+            parser->flags |= F_CONNECTION_UPGRADE;
+            break;
+          default:
+            break;
+        }
+
+        UPDATE_STATE(s_header_field_start);
+        REEXECUTE();
+      }
+
+      case s_header_value_discard_ws_almost_done:
+      {
+        STRICT_CHECK(ch != LF);
+        UPDATE_STATE(s_header_value_discard_lws);
+        break;
+      }
+
+      case s_header_value_discard_lws:
+      {
+        if (ch == ' ' || ch == '\t') {
+          UPDATE_STATE(s_header_value_discard_ws);
+          break;
+        } else {
+          switch (parser->header_state) {
+            case h_connection_keep_alive:
+              parser->flags |= F_CONNECTION_KEEP_ALIVE;
+              break;
+            case h_connection_close:
+              parser->flags |= F_CONNECTION_CLOSE;
+              break;
+            case h_connection_upgrade:
+              parser->flags |= F_CONNECTION_UPGRADE;
+              break;
+            case h_transfer_encoding_chunked:
+              parser->flags |= F_CHUNKED;
+              break;
+            case h_content_length:
+              /* do not allow empty content length */
+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+              goto error;
+              break;
+            default:
+              break;
+          }
+
+          /* header value was empty */
+          MARK(header_value);
+          UPDATE_STATE(s_header_field_start);
+          CALLBACK_DATA_NOADVANCE(header_value);
+          REEXECUTE();
+        }
+      }
+
+      case s_headers_almost_done:
+      {
+        STRICT_CHECK(ch != LF);
+
+        if (parser->flags & F_TRAILING) {
+          /* End of a chunked request */
+          UPDATE_STATE(s_message_done);
+          CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
+          REEXECUTE();
+        }
+
+        /* Cannot use chunked encoding and a content-length header together
+           per the HTTP specification. */
+        if ((parser->flags & F_CHUNKED) &&
+            (parser->flags & F_CONTENTLENGTH)) {
+          SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+          goto error;
+        }
+
+        UPDATE_STATE(s_headers_done);
+
+        /* Set this here so that on_headers_complete() callbacks can see it */
+        if ((parser->flags & F_UPGRADE) &&
+            (parser->flags & F_CONNECTION_UPGRADE)) {
+          /* For responses, "Upgrade: foo" and "Connection: upgrade" are
+           * mandatory only when it is a 101 Switching Protocols response,
+           * otherwise it is purely informational, to announce support.
+           */
+          parser->upgrade =
+              (parser->type == HTTP_REQUEST || parser->status_code == 101);
+        } else {
+          parser->upgrade = (parser->method == HTTP_CONNECT);
+        }
+
+        /* Here we call the headers_complete callback. This is somewhat
+         * different than other callbacks because if the user returns 1, we
+         * will interpret that as saying that this message has no body. This
+         * is needed for the annoying case of recieving a response to a HEAD
+         * request.
+         *
+         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
+         * we have to simulate it by handling a change in errno below.
+         */
+        if (settings->on_headers_complete) {
+          switch (settings->on_headers_complete(parser)) {
+            case 0:
+              break;
+
+            case 2:
+              parser->upgrade = 1;
+
+              /* fall through */
+            case 1:
+              parser->flags |= F_SKIPBODY;
+              break;
+
+            default:
+              SET_ERRNO(HPE_CB_headers_complete);
+              RETURN(p - data); /* Error */
+          }
+        }
+
+        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+          RETURN(p - data);
+        }
+
+        REEXECUTE();
+      }
+
+      case s_headers_done:
+      {
+        int hasBody;
+        STRICT_CHECK(ch != LF);
+
+        parser->nread = 0;
+        nread = 0;
+
+        hasBody = parser->flags & F_CHUNKED ||
+          (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
+        if (parser->upgrade && (parser->method == HTTP_CONNECT ||
+                                (parser->flags & F_SKIPBODY) || !hasBody)) {
+          /* Exit, the rest of the message is in a different protocol. */
+          UPDATE_STATE(NEW_MESSAGE());
+          CALLBACK_NOTIFY(message_complete);
+          RETURN((p - data) + 1);
+        }
+
+        if (parser->flags & F_SKIPBODY) {
+          UPDATE_STATE(NEW_MESSAGE());
+          CALLBACK_NOTIFY(message_complete);
+        } else if (parser->flags & F_CHUNKED) {
+          /* chunked encoding - ignore Content-Length header */
+          UPDATE_STATE(s_chunk_size_start);
+        } else {
+          if (parser->content_length == 0) {
+            /* Content-Length header given but zero: Content-Length: 0\r\n */
+            UPDATE_STATE(NEW_MESSAGE());
+            CALLBACK_NOTIFY(message_complete);
+          } else if (parser->content_length != ULLONG_MAX) {
+            /* Content-Length header given and non-zero */
+            UPDATE_STATE(s_body_identity);
+          } else {
+            if (!http_message_needs_eof(parser)) {
+              /* Assume content-length 0 - read the next */
+              UPDATE_STATE(NEW_MESSAGE());
+              CALLBACK_NOTIFY(message_complete);
+            } else {
+              /* Read body until EOF */
+              UPDATE_STATE(s_body_identity_eof);
+            }
+          }
+        }
+
+        break;
+      }
+
+      case s_body_identity:
+      {
+        uint64_t to_read = MIN(parser->content_length,
+                               (uint64_t) ((data + len) - p));
+
+        assert(parser->content_length != 0
+            && parser->content_length != ULLONG_MAX);
+
+        /* The difference between advancing content_length and p is because
+         * the latter will automaticaly advance on the next loop iteration.
+         * Further, if content_length ends up at 0, we want to see the last
+         * byte again for our message complete callback.
+         */
+        MARK(body);
+        parser->content_length -= to_read;
+        p += to_read - 1;
+
+        if (parser->content_length == 0) {
+          UPDATE_STATE(s_message_done);
+
+          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
+           *
+           * The alternative to doing this is to wait for the next byte to
+           * trigger the data callback, just as in every other case. The
+           * problem with this is that this makes it difficult for the test
+           * harness to distinguish between complete-on-EOF and
+           * complete-on-length. It's not clear that this distinction is
+           * important for applications, but let's keep it for now.
+           */
+          CALLBACK_DATA_(body, p - body_mark + 1, p - data);
+          REEXECUTE();
+        }
+
+        break;
+      }
+
+      /* read until EOF */
+      case s_body_identity_eof:
+        MARK(body);
+        p = data + len - 1;
+
+        break;
+
+      case s_message_done:
+        UPDATE_STATE(NEW_MESSAGE());
+        CALLBACK_NOTIFY(message_complete);
+        if (parser->upgrade) {
+          /* Exit, the rest of the message is in a different protocol. */
+          RETURN((p - data) + 1);
+        }
+        break;
+
+      case s_chunk_size_start:
+      {
+        assert(nread == 1);
+        assert(parser->flags & F_CHUNKED);
+
+        unhex_val = unhex[(unsigned char)ch];
+        if (UNLIKELY(unhex_val == -1)) {
+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+          goto error;
+        }
+
+        parser->content_length = unhex_val;
+        UPDATE_STATE(s_chunk_size);
+        break;
+      }
+
+      case s_chunk_size:
+      {
+        uint64_t t;
+
+        assert(parser->flags & F_CHUNKED);
+
+        if (ch == CR) {
+          UPDATE_STATE(s_chunk_size_almost_done);
+          break;
+        }
+
+        unhex_val = unhex[(unsigned char)ch];
+
+        if (unhex_val == -1) {
+          if (ch == ';' || ch == ' ') {
+            UPDATE_STATE(s_chunk_parameters);
+            break;
+          }
+
+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+          goto error;
+        }
+
+        t = parser->content_length;
+        t *= 16;
+        t += unhex_val;
+
+        /* Overflow? Test against a conservative limit for simplicity. */
+        if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
+          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+          goto error;
+        }
+
+        parser->content_length = t;
+        break;
+      }
+
+      case s_chunk_parameters:
+      {
+        assert(parser->flags & F_CHUNKED);
+        /* just ignore this shit. TODO check for overflow */
+        if (ch == CR) {
+          UPDATE_STATE(s_chunk_size_almost_done);
+          break;
+        }
+        break;
+      }
+
+      case s_chunk_size_almost_done:
+      {
+        assert(parser->flags & F_CHUNKED);
+        STRICT_CHECK(ch != LF);
+
+        parser->nread = 0;
+        nread = 0;
+
+        if (parser->content_length == 0) {
+          parser->flags |= F_TRAILING;
+          UPDATE_STATE(s_header_field_start);
+        } else {
+          UPDATE_STATE(s_chunk_data);
+        }
+        CALLBACK_NOTIFY(chunk_header);
+        break;
+      }
+
+      case s_chunk_data:
+      {
+        uint64_t to_read = MIN(parser->content_length,
+                               (uint64_t) ((data + len) - p));
+
+        assert(parser->flags & F_CHUNKED);
+        assert(parser->content_length != 0
+            && parser->content_length != ULLONG_MAX);
+
+        /* See the explanation in s_body_identity for why the content
+         * length and data pointers are managed this way.
+         */
+        MARK(body);
+        parser->content_length -= to_read;
+        p += to_read - 1;
+
+        if (parser->content_length == 0) {
+          UPDATE_STATE(s_chunk_data_almost_done);
+        }
+
+        break;
+      }
+
+      case s_chunk_data_almost_done:
+        assert(parser->flags & F_CHUNKED);
+        assert(parser->content_length == 0);
+        STRICT_CHECK(ch != CR);
+        UPDATE_STATE(s_chunk_data_done);
+        CALLBACK_DATA(body);
+        break;
+
+      case s_chunk_data_done:
+        assert(parser->flags & F_CHUNKED);
+        STRICT_CHECK(ch != LF);
+        parser->nread = 0;
+        nread = 0;
+        UPDATE_STATE(s_chunk_size_start);
+        CALLBACK_NOTIFY(chunk_complete);
+        break;
+
+      default:
+        assert(0 && "unhandled state");
+        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
+        goto error;
+    }
+  }
+
+  /* Run callbacks for any marks that we have leftover after we ran out of
+   * bytes. There should be at most one of these set, so it's OK to invoke
+   * them in series (unset marks will not result in callbacks).
+   *
+   * We use the NOADVANCE() variety of callbacks here because 'p' has already
+   * overflowed 'data' and this allows us to correct for the off-by-one that
+   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
+   * value that's in-bounds).
+   */
+
+  assert(((header_field_mark ? 1 : 0) +
+          (header_value_mark ? 1 : 0) +
+          (url_mark ? 1 : 0)  +
+          (body_mark ? 1 : 0) +
+          (status_mark ? 1 : 0)) <= 1);
+
+  CALLBACK_DATA_NOADVANCE(header_field);
+  CALLBACK_DATA_NOADVANCE(header_value);
+  CALLBACK_DATA_NOADVANCE(url);
+  CALLBACK_DATA_NOADVANCE(body);
+  CALLBACK_DATA_NOADVANCE(status);
+
+  RETURN(len);
+
+error:
+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
+    SET_ERRNO(HPE_UNKNOWN);
+  }
+
+  RETURN(p - data);
+}
+
+
+/* Does the parser need to see an EOF to find the end of the message? */
+int
+http_message_needs_eof (const http_parser *parser)
+{
+  if (parser->type == HTTP_REQUEST) {
+    return 0;
+  }
+
+  /* See RFC 2616 section 4.4 */
+  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+      parser->status_code == 204 ||     /* No Content */
+      parser->status_code == 304 ||     /* Not Modified */
+      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */
+    return 0;
+  }
+
+  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
+    return 0;
+  }
+
+  return 1;
+}
+
+
+int
+http_should_keep_alive (const http_parser *parser)
+{
+  if (parser->http_major > 0 && parser->http_minor > 0) {
+    /* HTTP/1.1 */
+    if (parser->flags & F_CONNECTION_CLOSE) {
+      return 0;
+    }
+  } else {
+    /* HTTP/1.0 or earlier */
+    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
+      return 0;
+    }
+  }
+
+  return !http_message_needs_eof(parser);
+}
+
+
+const char *
+http_method_str (enum http_method m)
+{
+  return ELEM_AT(method_strings, m, "<unknown>");
+}
+
+const char *
+http_status_str (enum http_status s)
+{
+  switch (s) {
+#define XX(num, name, string) case HTTP_STATUS_##name: return #string;
+    HTTP_STATUS_MAP(XX)
+#undef XX
+    default: return "<unknown>";
+  }
+}
+
+void
+http_parser_init (http_parser *parser, enum http_parser_type t)
+{
+  void *data = parser->data; /* preserve application data */
+  memset(parser, 0, sizeof(*parser));
+  parser->data = data;
+  parser->type = t;
+  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
+  parser->http_errno = HPE_OK;
+}
+
+void
+http_parser_settings_init(http_parser_settings *settings)
+{
+  memset(settings, 0, sizeof(*settings));
+}
+
+const char *
+http_errno_name(enum http_errno err) {
+  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+  return http_strerror_tab[err].name;
+}
+
+const char *
+http_errno_description(enum http_errno err) {
+  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+  return http_strerror_tab[err].description;
+}
+
+static enum http_host_state
+http_parse_host_char(enum http_host_state s, const char ch) {
+  switch(s) {
+    case s_http_userinfo:
+    case s_http_userinfo_start:
+      if (ch == '@') {
+        return s_http_host_start;
+      }
+
+      if (IS_USERINFO_CHAR(ch)) {
+        return s_http_userinfo;
+      }
+      break;
+
+    case s_http_host_start:
+      if (ch == '[') {
+        return s_http_host_v6_start;
+      }
+
+      if (IS_HOST_CHAR(ch)) {
+        return s_http_host;
+      }
+
+      break;
+
+    case s_http_host:
+      if (IS_HOST_CHAR(ch)) {
+        return s_http_host;
+      }
+
+    /* fall through */
+    case s_http_host_v6_end:
+      if (ch == ':') {
+        return s_http_host_port_start;
+      }
+
+      break;
+
+    case s_http_host_v6:
+      if (ch == ']') {
+        return s_http_host_v6_end;
+      }
+
+    /* fall through */
+    case s_http_host_v6_start:
+      if (IS_HEX(ch) || ch == ':' || ch == '.') {
+        return s_http_host_v6;
+      }
+
+      if (s == s_http_host_v6 && ch == '%') {
+        return s_http_host_v6_zone_start;
+      }
+      break;
+
+    case s_http_host_v6_zone:
+      if (ch == ']') {
+        return s_http_host_v6_end;
+      }
+
+    /* fall through */
+    case s_http_host_v6_zone_start:
+      /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
+      if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
+          ch == '~') {
+        return s_http_host_v6_zone;
+      }
+      break;
+
+    case s_http_host_port:
+    case s_http_host_port_start:
+      if (IS_NUM(ch)) {
+        return s_http_host_port;
+      }
+
+      break;
+
+    default:
+      break;
+  }
+  return s_http_host_dead;
+}
+
+static int
+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
+  enum http_host_state s;
+
+  const char *p;
+  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
+
+  assert(u->field_set & (1 << UF_HOST));
+
+  u->field_data[UF_HOST].len = 0;
+
+  s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
+    enum http_host_state new_s = http_parse_host_char(s, *p);
+
+    if (new_s == s_http_host_dead) {
+      return 1;
+    }
+
+    switch(new_s) {
+      case s_http_host:
+        if (s != s_http_host) {
+          u->field_data[UF_HOST].off = (uint16_t)(p - buf);
+        }
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_v6:
+        if (s != s_http_host_v6) {
+          u->field_data[UF_HOST].off = (uint16_t)(p - buf);
+        }
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_v6_zone_start:
+      case s_http_host_v6_zone:
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_port:
+        if (s != s_http_host_port) {
+          u->field_data[UF_PORT].off = (uint16_t)(p - buf);
+          u->field_data[UF_PORT].len = 0;
+          u->field_set |= (1 << UF_PORT);
+        }
+        u->field_data[UF_PORT].len++;
+        break;
+
+      case s_http_userinfo:
+        if (s != s_http_userinfo) {
+          u->field_data[UF_USERINFO].off = (uint16_t)(p - buf);
+          u->field_data[UF_USERINFO].len = 0;
+          u->field_set |= (1 << UF_USERINFO);
+        }
+        u->field_data[UF_USERINFO].len++;
+        break;
+
+      default:
+        break;
+    }
+    s = new_s;
+  }
+
+  /* Make sure we don't end somewhere unexpected */
+  switch (s) {
+    case s_http_host_start:
+    case s_http_host_v6_start:
+    case s_http_host_v6:
+    case s_http_host_v6_zone_start:
+    case s_http_host_v6_zone:
+    case s_http_host_port_start:
+    case s_http_userinfo:
+    case s_http_userinfo_start:
+      return 1;
+    default:
+      break;
+  }
+
+  return 0;
+}
+
+void
+http_parser_url_init(struct http_parser_url *u) {
+  memset(u, 0, sizeof(*u));
+}
+
+int
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+                      struct http_parser_url *u)
+{
+  enum state s;
+  const char *p;
+  enum http_parser_url_fields uf, old_uf;
+  int found_at = 0;
+
+  if (buflen == 0) {
+    return 1;
+  }
+
+  u->port = u->field_set = 0;
+  s = is_connect ? s_req_server_start : s_req_spaces_before_url;
+  old_uf = UF_MAX;
+
+  for (p = buf; p < buf + buflen; p++) {
+    s = parse_url_char(s, *p);
+
+    /* Figure out the next field that we're operating on */
+    switch (s) {
+      case s_dead:
+        return 1;
+
+      /* Skip delimeters */
+      case s_req_schema_slash:
+      case s_req_schema_slash_slash:
+      case s_req_server_start:
+      case s_req_query_string_start:
+      case s_req_fragment_start:
+        continue;
+
+      case s_req_schema:
+        uf = UF_SCHEMA;
+        break;
+
+      case s_req_server_with_at:
+        found_at = 1;
+
+      /* fall through */
+      case s_req_server:
+        uf = UF_HOST;
+        break;
+
+      case s_req_path:
+        uf = UF_PATH;
+        break;
+
+      case s_req_query_string:
+        uf = UF_QUERY;
+        break;
+
+      case s_req_fragment:
+        uf = UF_FRAGMENT;
+        break;
+
+      default:
+        assert(!"Unexpected state");
+        return 1;
+    }
+
+    /* Nothing's changed; soldier on */
+    if (uf == old_uf) {
+      u->field_data[uf].len++;
+      continue;
+    }
+
+    u->field_data[uf].off = (uint16_t)(p - buf);
+    u->field_data[uf].len = 1;
+
+    u->field_set |= (1 << uf);
+    old_uf = uf;
+  }
+
+  /* host must be present if there is a schema */
+  /* parsing http:///toto will fail */
+  if ((u->field_set & (1 << UF_SCHEMA)) &&
+      (u->field_set & (1 << UF_HOST)) == 0) {
+    return 1;
+  }
+
+  if (u->field_set & (1 << UF_HOST)) {
+    if (http_parse_host(buf, u, found_at) != 0) {
+      return 1;
+    }
+  }
+
+  /* CONNECT requests can only contain "hostname:port" */
+  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
+    return 1;
+  }
+
+  if (u->field_set & (1 << UF_PORT)) {
+    uint16_t off;
+    uint16_t len;
+    const char* p;
+    const char* end;
+    unsigned long v;
+
+    off = u->field_data[UF_PORT].off;
+    len = u->field_data[UF_PORT].len;
+    end = buf + off + len;
+
+    /* NOTE: The characters are already validated and are in the [0-9] range */
+    assert(off + len <= buflen && "Port number overflow");
+    v = 0;
+    for (p = buf + off; p < end; p++) {
+      v *= 10;
+      v += *p - '0';
+
+      /* Ports have a max value of 2^16 */
+      if (v > 0xffff) {
+        return 1;
+      }
+    }
+
+    u->port = (uint16_t) v;
+  }
+
+  return 0;
+}
+
+void
+http_parser_pause(http_parser *parser, int paused) {
+  /* Users should only be pausing/unpausing a parser that is not in an error
+   * state. In non-debug builds, there's not much that we can do about this
+   * other than ignore it.
+   */
+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
+      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
+    uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */
+    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
+  } else {
+    assert(0 && "Attempting to pause parser in error state");
+  }
+}
+
+int
+http_body_is_final(const struct http_parser *parser) {
+    return parser->state == s_message_done;
+}
+
+unsigned long
+http_parser_version(void) {
+  return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
+         HTTP_PARSER_VERSION_MINOR * 0x00100 |
+         HTTP_PARSER_VERSION_PATCH * 0x00001;
+}
+
+void
+http_parser_set_max_header_size(uint32_t size) {
+  max_header_size = size;
+}
diff --git a/vendor/http_parser/http_parser.h b/vendor/http_parser/http_parser.h
new file mode 100644 (file)
index 0000000..16b5281
--- /dev/null
@@ -0,0 +1,439 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef http_parser_h
+#define http_parser_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Also update SONAME in the Makefile whenever you change these. */
+#define HTTP_PARSER_VERSION_MAJOR 2
+#define HTTP_PARSER_VERSION_MINOR 9
+#define HTTP_PARSER_VERSION_PATCH 2
+
+#include <stddef.h>
+#if defined(_WIN32) && !defined(__MINGW32__) && \
+  (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
+#include <BaseTsd.h>
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
+ * faster
+ */
+#ifndef HTTP_PARSER_STRICT
+# define HTTP_PARSER_STRICT 1
+#endif
+
+/* Maximium header size allowed. If the macro is not defined
+ * before including this header then the default is used. To
+ * change the maximum header size, define the macro in the build
+ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
+ * the effective limit on the size of the header, define the macro
+ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
+ */
+#ifndef HTTP_MAX_HEADER_SIZE
+# define HTTP_MAX_HEADER_SIZE (80*1024)
+#endif
+
+typedef struct http_parser http_parser;
+typedef struct http_parser_settings http_parser_settings;
+
+
+/* Callbacks should return non-zero to indicate an error. The parser will
+ * then halt execution.
+ *
+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
+ * returning '1' from on_headers_complete will tell the parser that it
+ * should not expect a body. This is used when receiving a response to a
+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
+ * chunked' headers that indicate the presence of a body.
+ *
+ * Returning `2` from on_headers_complete will tell parser that it should not
+ * expect neither a body nor any futher responses on this connection. This is
+ * useful for handling responses to a CONNECT request which may not contain
+ * `Upgrade` or `Connection: upgrade` headers.
+ *
+ * http_data_cb does not return data chunks. It will be called arbitrarily
+ * many times for each string. E.G. you might get 10 callbacks for "on_url"
+ * each providing just a few characters more data.
+ */
+typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
+typedef int (*http_cb) (http_parser*);
+
+
+/* Status Codes */
+#define HTTP_STATUS_MAP(XX)                                                 \
+  XX(100, CONTINUE,                        Continue)                        \
+  XX(101, SWITCHING_PROTOCOLS,             Switching Protocols)             \
+  XX(102, PROCESSING,                      Processing)                      \
+  XX(200, OK,                              OK)                              \
+  XX(201, CREATED,                         Created)                         \
+  XX(202, ACCEPTED,                        Accepted)                        \
+  XX(203, NON_AUTHORITATIVE_INFORMATION,   Non-Authoritative Information)   \
+  XX(204, NO_CONTENT,                      No Content)                      \
+  XX(205, RESET_CONTENT,                   Reset Content)                   \
+  XX(206, PARTIAL_CONTENT,                 Partial Content)                 \
+  XX(207, MULTI_STATUS,                    Multi-Status)                    \
+  XX(208, ALREADY_REPORTED,                Already Reported)                \
+  XX(226, IM_USED,                         IM Used)                         \
+  XX(300, MULTIPLE_CHOICES,                Multiple Choices)                \
+  XX(301, MOVED_PERMANENTLY,               Moved Permanently)               \
+  XX(302, FOUND,                           Found)                           \
+  XX(303, SEE_OTHER,                       See Other)                       \
+  XX(304, NOT_MODIFIED,                    Not Modified)                    \
+  XX(305, USE_PROXY,                       Use Proxy)                       \
+  XX(307, TEMPORARY_REDIRECT,              Temporary Redirect)              \
+  XX(308, PERMANENT_REDIRECT,              Permanent Redirect)              \
+  XX(400, BAD_REQUEST,                     Bad Request)                     \
+  XX(401, UNAUTHORIZED,                    Unauthorized)                    \
+  XX(402, PAYMENT_REQUIRED,                Payment Required)                \
+  XX(403, FORBIDDEN,                       Forbidden)                       \
+  XX(404, NOT_FOUND,                       Not Found)                       \
+  XX(405, METHOD_NOT_ALLOWED,              Method Not Allowed)              \
+  XX(406, NOT_ACCEPTABLE,                  Not Acceptable)                  \
+  XX(407, PROXY_AUTHENTICATION_REQUIRED,   Proxy Authentication Required)   \
+  XX(408, REQUEST_TIMEOUT,                 Request Timeout)                 \
+  XX(409, CONFLICT,                        Conflict)                        \
+  XX(410, GONE,                            Gone)                            \
+  XX(411, LENGTH_REQUIRED,                 Length Required)                 \
+  XX(412, PRECONDITION_FAILED,             Precondition Failed)             \
+  XX(413, PAYLOAD_TOO_LARGE,               Payload Too Large)               \
+  XX(414, URI_TOO_LONG,                    URI Too Long)                    \
+  XX(415, UNSUPPORTED_MEDIA_TYPE,          Unsupported Media Type)          \
+  XX(416, RANGE_NOT_SATISFIABLE,           Range Not Satisfiable)           \
+  XX(417, EXPECTATION_FAILED,              Expectation Failed)              \
+  XX(421, MISDIRECTED_REQUEST,             Misdirected Request)             \
+  XX(422, UNPROCESSABLE_ENTITY,            Unprocessable Entity)            \
+  XX(423, LOCKED,                          Locked)                          \
+  XX(424, FAILED_DEPENDENCY,               Failed Dependency)               \
+  XX(426, UPGRADE_REQUIRED,                Upgrade Required)                \
+  XX(428, PRECONDITION_REQUIRED,           Precondition Required)           \
+  XX(429, TOO_MANY_REQUESTS,               Too Many Requests)               \
+  XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
+  XX(451, UNAVAILABLE_FOR_LEGAL_REASONS,   Unavailable For Legal Reasons)   \
+  XX(500, INTERNAL_SERVER_ERROR,           Internal Server Error)           \
+  XX(501, NOT_IMPLEMENTED,                 Not Implemented)                 \
+  XX(502, BAD_GATEWAY,                     Bad Gateway)                     \
+  XX(503, SERVICE_UNAVAILABLE,             Service Unavailable)             \
+  XX(504, GATEWAY_TIMEOUT,                 Gateway Timeout)                 \
+  XX(505, HTTP_VERSION_NOT_SUPPORTED,      HTTP Version Not Supported)      \
+  XX(506, VARIANT_ALSO_NEGOTIATES,         Variant Also Negotiates)         \
+  XX(507, INSUFFICIENT_STORAGE,            Insufficient Storage)            \
+  XX(508, LOOP_DETECTED,                   Loop Detected)                   \
+  XX(510, NOT_EXTENDED,                    Not Extended)                    \
+  XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
+
+enum http_status
+  {
+#define XX(num, name, string) HTTP_STATUS_##name = num,
+  HTTP_STATUS_MAP(XX)
+#undef XX
+  };
+
+
+/* Request Methods */
+#define HTTP_METHOD_MAP(XX)         \
+  XX(0,  DELETE,      DELETE)       \
+  XX(1,  GET,         GET)          \
+  XX(2,  HEAD,        HEAD)         \
+  XX(3,  POST,        POST)         \
+  XX(4,  PUT,         PUT)          \
+  /* pathological */                \
+  XX(5,  CONNECT,     CONNECT)      \
+  XX(6,  OPTIONS,     OPTIONS)      \
+  XX(7,  TRACE,       TRACE)        \
+  /* WebDAV */                      \
+  XX(8,  COPY,        COPY)         \
+  XX(9,  LOCK,        LOCK)         \
+  XX(10, MKCOL,       MKCOL)        \
+  XX(11, MOVE,        MOVE)         \
+  XX(12, PROPFIND,    PROPFIND)     \
+  XX(13, PROPPATCH,   PROPPATCH)    \
+  XX(14, SEARCH,      SEARCH)       \
+  XX(15, UNLOCK,      UNLOCK)       \
+  XX(16, BIND,        BIND)         \
+  XX(17, REBIND,      REBIND)       \
+  XX(18, UNBIND,      UNBIND)       \
+  XX(19, ACL,         ACL)          \
+  /* subversion */                  \
+  XX(20, REPORT,      REPORT)       \
+  XX(21, MKACTIVITY,  MKACTIVITY)   \
+  XX(22, CHECKOUT,    CHECKOUT)     \
+  XX(23, MERGE,       MERGE)        \
+  /* upnp */                        \
+  XX(24, MSEARCH,     M-SEARCH)     \
+  XX(25, NOTIFY,      NOTIFY)       \
+  XX(26, SUBSCRIBE,   SUBSCRIBE)    \
+  XX(27, UNSUBSCRIBE, UNSUBSCRIBE)  \
+  /* RFC-5789 */                    \
+  XX(28, PATCH,       PATCH)        \
+  XX(29, PURGE,       PURGE)        \
+  /* CalDAV */                      \
+  XX(30, MKCALENDAR,  MKCALENDAR)   \
+  /* RFC-2068, section 19.6.1.2 */  \
+  XX(31, LINK,        LINK)         \
+  XX(32, UNLINK,      UNLINK)       \
+  /* icecast */                     \
+  XX(33, SOURCE,      SOURCE)       \
+
+enum http_method
+  {
+#define XX(num, name, string) HTTP_##name = num,
+  HTTP_METHOD_MAP(XX)
+#undef XX
+  };
+
+
+enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
+
+
+/* Flag values for http_parser.flags field */
+enum flags
+  { F_CHUNKED               = 1 << 0
+  , F_CONNECTION_KEEP_ALIVE = 1 << 1
+  , F_CONNECTION_CLOSE      = 1 << 2
+  , F_CONNECTION_UPGRADE    = 1 << 3
+  , F_TRAILING              = 1 << 4
+  , F_UPGRADE               = 1 << 5
+  , F_SKIPBODY              = 1 << 6
+  , F_CONTENTLENGTH         = 1 << 7
+  };
+
+
+/* Map for errno-related constants
+ *
+ * The provided argument should be a macro that takes 2 arguments.
+ */
+#define HTTP_ERRNO_MAP(XX)                                           \
+  /* No error */                                                     \
+  XX(OK, "success")                                                  \
+                                                                     \
+  /* Callback-related errors */                                      \
+  XX(CB_message_begin, "the on_message_begin callback failed")       \
+  XX(CB_url, "the on_url callback failed")                           \
+  XX(CB_header_field, "the on_header_field callback failed")         \
+  XX(CB_header_value, "the on_header_value callback failed")         \
+  XX(CB_headers_complete, "the on_headers_complete callback failed") \
+  XX(CB_body, "the on_body callback failed")                         \
+  XX(CB_message_complete, "the on_message_complete callback failed") \
+  XX(CB_status, "the on_status callback failed")                     \
+  XX(CB_chunk_header, "the on_chunk_header callback failed")         \
+  XX(CB_chunk_complete, "the on_chunk_complete callback failed")     \
+                                                                     \
+  /* Parsing-related errors */                                       \
+  XX(INVALID_EOF_STATE, "stream ended at an unexpected time")        \
+  XX(HEADER_OVERFLOW,                                                \
+     "too many header bytes seen; overflow detected")                \
+  XX(CLOSED_CONNECTION,                                              \
+     "data received after completed connection: close message")      \
+  XX(INVALID_VERSION, "invalid HTTP version")                        \
+  XX(INVALID_STATUS, "invalid HTTP status code")                     \
+  XX(INVALID_METHOD, "invalid HTTP method")                          \
+  XX(INVALID_URL, "invalid URL")                                     \
+  XX(INVALID_HOST, "invalid host")                                   \
+  XX(INVALID_PORT, "invalid port")                                   \
+  XX(INVALID_PATH, "invalid path")                                   \
+  XX(INVALID_QUERY_STRING, "invalid query string")                   \
+  XX(INVALID_FRAGMENT, "invalid fragment")                           \
+  XX(LF_EXPECTED, "LF character expected")                           \
+  XX(INVALID_HEADER_TOKEN, "invalid character in header")            \
+  XX(INVALID_CONTENT_LENGTH,                                         \
+     "invalid character in content-length header")                   \
+  XX(UNEXPECTED_CONTENT_LENGTH,                                      \
+     "unexpected content-length header")                             \
+  XX(INVALID_CHUNK_SIZE,                                             \
+     "invalid character in chunk size header")                       \
+  XX(INVALID_CONSTANT, "invalid constant string")                    \
+  XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
+  XX(STRICT, "strict mode assertion failed")                         \
+  XX(PAUSED, "parser is paused")                                     \
+  XX(UNKNOWN, "an unknown error occurred")
+
+
+/* Define HPE_* values for each errno value above */
+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
+enum http_errno {
+  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+};
+#undef HTTP_ERRNO_GEN
+
+
+/* Get an http_errno value from an http_parser */
+#define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)
+
+
+struct http_parser {
+  /** PRIVATE **/
+  unsigned int type : 2;         /* enum http_parser_type */
+  unsigned int flags : 8;        /* F_* values from 'flags' enum; semi-public */
+  unsigned int state : 7;        /* enum state from http_parser.c */
+  unsigned int header_state : 7; /* enum header_state from http_parser.c */
+  unsigned int index : 7;        /* index into current matcher */
+  unsigned int lenient_http_headers : 1;
+
+  uint32_t nread;          /* # bytes read in various scenarios */
+  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
+
+  /** READ-ONLY **/
+  unsigned short http_major;
+  unsigned short http_minor;
+  unsigned int status_code : 16; /* responses only */
+  unsigned int method : 8;       /* requests only */
+  unsigned int http_errno : 7;
+
+  /* 1 = Upgrade header was present and the parser has exited because of that.
+   * 0 = No upgrade header present.
+   * Should be checked when http_parser_execute() returns in addition to
+   * error checking.
+   */
+  unsigned int upgrade : 1;
+
+  /** PUBLIC **/
+  void *data; /* A pointer to get hook to the "connection" or "socket" object */
+};
+
+
+struct http_parser_settings {
+  http_cb      on_message_begin;
+  http_data_cb on_url;
+  http_data_cb on_status;
+  http_data_cb on_header_field;
+  http_data_cb on_header_value;
+  http_cb      on_headers_complete;
+  http_data_cb on_body;
+  http_cb      on_message_complete;
+  /* When on_chunk_header is called, the current chunk length is stored
+   * in parser->content_length.
+   */
+  http_cb      on_chunk_header;
+  http_cb      on_chunk_complete;
+};
+
+
+enum http_parser_url_fields
+  { UF_SCHEMA           = 0
+  , UF_HOST             = 1
+  , UF_PORT             = 2
+  , UF_PATH             = 3
+  , UF_QUERY            = 4
+  , UF_FRAGMENT         = 5
+  , UF_USERINFO         = 6
+  , UF_MAX              = 7
+  };
+
+
+/* Result structure for http_parser_parse_url().
+ *
+ * Callers should index into field_data[] with UF_* values iff field_set
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
+ * because we probably have padding left over), we convert any port to
+ * a uint16_t.
+ */
+struct http_parser_url {
+  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */
+  uint16_t port;                /* Converted UF_PORT string */
+
+  struct {
+    uint16_t off;               /* Offset into buffer in which field starts */
+    uint16_t len;               /* Length of run in buffer */
+  } field_data[UF_MAX];
+};
+
+
+/* Returns the library version. Bits 16-23 contain the major version number,
+ * bits 8-15 the minor version number and bits 0-7 the patch level.
+ * Usage example:
+ *
+ *   unsigned long version = http_parser_version();
+ *   unsigned major = (version >> 16) & 255;
+ *   unsigned minor = (version >> 8) & 255;
+ *   unsigned patch = version & 255;
+ *   printf("http_parser v%u.%u.%u\n", major, minor, patch);
+ */
+unsigned long http_parser_version(void);
+
+void http_parser_init(http_parser *parser, enum http_parser_type type);
+
+
+/* Initialize http_parser_settings members to 0
+ */
+void http_parser_settings_init(http_parser_settings *settings);
+
+
+/* Executes the parser. Returns number of parsed bytes. Sets
+ * `parser->http_errno` on error. */
+size_t http_parser_execute(http_parser *parser,
+                           const http_parser_settings *settings,
+                           const char *data,
+                           size_t len);
+
+
+/* If http_should_keep_alive() in the on_headers_complete or
+ * on_message_complete callback returns 0, then this should be
+ * the last message on the connection.
+ * If you are the server, respond with the "Connection: close" header.
+ * If you are the client, close the connection.
+ */
+int http_should_keep_alive(const http_parser *parser);
+
+/* Returns a string version of the HTTP method. */
+const char *http_method_str(enum http_method m);
+
+/* Returns a string version of the HTTP status code. */
+const char *http_status_str(enum http_status s);
+
+/* Return a string name of the given error */
+const char *http_errno_name(enum http_errno err);
+
+/* Return a string description of the given error */
+const char *http_errno_description(enum http_errno err);
+
+/* Initialize all http_parser_url members to 0 */
+void http_parser_url_init(struct http_parser_url *u);
+
+/* Parse a URL; return nonzero on failure */
+int http_parser_parse_url(const char *buf, size_t buflen,
+                          int is_connect,
+                          struct http_parser_url *u);
+
+/* Pause or un-pause the parser; a nonzero value pauses */
+void http_parser_pause(http_parser *parser, int paused);
+
+/* Checks if this is the final chunk of the body. */
+int http_body_is_final(const http_parser *parser);
+
+/* Change the maximum header size provided at compile time. */
+void http_parser_set_max_header_size(uint32_t size);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/vendor/sha2/sha2.c b/vendor/sha2/sha2.c
new file mode 100644 (file)
index 0000000..0fe4c37
--- /dev/null
@@ -0,0 +1,949 @@
+/*
+ * FIPS 180-2 SHA-224/256/384/512 implementation
+ * Last update: 02/02/2007
+ * Issue date:  04/30/2005
+ *
+ * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#define UNROLL_LOOPS /* Enable loops unrolling */
+#endif
+
+#include <string.h>
+
+#include "sha2.h"
+
+#define SHFR(x, n)    (x >> n)
+#define ROTR(x, n)   ((x >> n) | (x << ((sizeof(x) << 3) - n)))
+#define ROTL(x, n)   ((x << n) | (x >> ((sizeof(x) << 3) - n)))
+#define CH(x, y, z)  ((x & y) ^ (~x & z))
+#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+
+#define SHA256_F1(x) (ROTR(x,  2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define SHA256_F2(x) (ROTR(x,  6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define SHA256_F3(x) (ROTR(x,  7) ^ ROTR(x, 18) ^ SHFR(x,  3))
+#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
+
+#define SHA512_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39))
+#define SHA512_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41))
+#define SHA512_F3(x) (ROTR(x,  1) ^ ROTR(x,  8) ^ SHFR(x,  7))
+#define SHA512_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x,  6))
+
+#define UNPACK32(x, str)                      \
+{                                             \
+    *((str) + 3) = (uint8) ((x)      );       \
+    *((str) + 2) = (uint8) ((x) >>  8);       \
+    *((str) + 1) = (uint8) ((x) >> 16);       \
+    *((str) + 0) = (uint8) ((x) >> 24);       \
+}
+
+#define PACK32(str, x)                        \
+{                                             \
+    *(x) =   ((uint32) *((str) + 3)      )    \
+           | ((uint32) *((str) + 2) <<  8)    \
+           | ((uint32) *((str) + 1) << 16)    \
+           | ((uint32) *((str) + 0) << 24);   \
+}
+
+#define UNPACK64(x, str)                      \
+{                                             \
+    *((str) + 7) = (uint8) ((x)      );       \
+    *((str) + 6) = (uint8) ((x) >>  8);       \
+    *((str) + 5) = (uint8) ((x) >> 16);       \
+    *((str) + 4) = (uint8) ((x) >> 24);       \
+    *((str) + 3) = (uint8) ((x) >> 32);       \
+    *((str) + 2) = (uint8) ((x) >> 40);       \
+    *((str) + 1) = (uint8) ((x) >> 48);       \
+    *((str) + 0) = (uint8) ((x) >> 56);       \
+}
+
+#define PACK64(str, x)                        \
+{                                             \
+    *(x) =   ((uint64) *((str) + 7)      )    \
+           | ((uint64) *((str) + 6) <<  8)    \
+           | ((uint64) *((str) + 5) << 16)    \
+           | ((uint64) *((str) + 4) << 24)    \
+           | ((uint64) *((str) + 3) << 32)    \
+           | ((uint64) *((str) + 2) << 40)    \
+           | ((uint64) *((str) + 1) << 48)    \
+           | ((uint64) *((str) + 0) << 56);   \
+}
+
+/* Macros used for loops unrolling */
+
+#define SHA256_SCR(i)                         \
+{                                             \
+    w[i] =  SHA256_F4(w[i -  2]) + w[i -  7]  \
+          + SHA256_F3(w[i - 15]) + w[i - 16]; \
+}
+
+#define SHA512_SCR(i)                         \
+{                                             \
+    w[i] =  SHA512_F4(w[i -  2]) + w[i -  7]  \
+          + SHA512_F3(w[i - 15]) + w[i - 16]; \
+}
+
+#define SHA256_EXP(a, b, c, d, e, f, g, h, j)               \
+{                                                           \
+    t1 = wv[h] + SHA256_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \
+         + sha256_k[j] + w[j];                              \
+    t2 = SHA256_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]);       \
+    wv[d] += t1;                                            \
+    wv[h] = t1 + t2;                                        \
+}
+
+#define SHA512_EXP(a, b, c, d, e, f, g ,h, j)               \
+{                                                           \
+    t1 = wv[h] + SHA512_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \
+         + sha512_k[j] + w[j];                              \
+    t2 = SHA512_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]);       \
+    wv[d] += t1;                                            \
+    wv[h] = t1 + t2;                                        \
+}
+
+uint32 sha224_h0[8] =
+            {0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
+             0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4};
+
+uint32 sha256_h0[8] =
+            {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+             0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
+
+uint64 sha384_h0[8] =
+            {0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL,
+             0x9159015a3070dd17ULL, 0x152fecd8f70e5939ULL,
+             0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL,
+             0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL};
+
+uint64 sha512_h0[8] =
+            {0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
+             0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
+             0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+             0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL};
+
+uint32 sha256_k[64] =
+            {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+             0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+             0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+             0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+             0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+             0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+             0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+             0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+             0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+             0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+             0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+             0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+             0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+             0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+             0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+             0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
+
+uint64 sha512_k[80] =
+            {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+             0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+             0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+             0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+             0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+             0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+             0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+             0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+             0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+             0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+             0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+             0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+             0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+             0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+             0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+             0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+             0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+             0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+             0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+             0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+             0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+             0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+             0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+             0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+             0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+             0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+             0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+             0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+             0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+             0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+             0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+             0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+             0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+             0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+             0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+             0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+             0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+             0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+             0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+             0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL};
+
+/* SHA-256 functions */
+
+void sha256_transf(sha256_ctx *ctx, const unsigned char *message,
+                   unsigned int block_nb)
+{
+    uint32 w[64];
+    uint32 wv[8];
+    uint32 t1, t2;
+    const unsigned char *sub_block;
+    int i;
+
+#ifndef UNROLL_LOOPS
+    int j;
+#endif
+
+    for (i = 0; i < (int) block_nb; i++) {
+        sub_block = message + (i << 6);
+
+#ifndef UNROLL_LOOPS
+        for (j = 0; j < 16; j++) {
+            PACK32(&sub_block[j << 2], &w[j]);
+        }
+
+        for (j = 16; j < 64; j++) {
+            SHA256_SCR(j);
+        }
+
+        for (j = 0; j < 8; j++) {
+            wv[j] = ctx->h[j];
+        }
+
+        for (j = 0; j < 64; j++) {
+            t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6])
+                + sha256_k[j] + w[j];
+            t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
+            wv[7] = wv[6];
+            wv[6] = wv[5];
+            wv[5] = wv[4];
+            wv[4] = wv[3] + t1;
+            wv[3] = wv[2];
+            wv[2] = wv[1];
+            wv[1] = wv[0];
+            wv[0] = t1 + t2;
+        }
+
+        for (j = 0; j < 8; j++) {
+            ctx->h[j] += wv[j];
+        }
+#else
+        PACK32(&sub_block[ 0], &w[ 0]); PACK32(&sub_block[ 4], &w[ 1]);
+        PACK32(&sub_block[ 8], &w[ 2]); PACK32(&sub_block[12], &w[ 3]);
+        PACK32(&sub_block[16], &w[ 4]); PACK32(&sub_block[20], &w[ 5]);
+        PACK32(&sub_block[24], &w[ 6]); PACK32(&sub_block[28], &w[ 7]);
+        PACK32(&sub_block[32], &w[ 8]); PACK32(&sub_block[36], &w[ 9]);
+        PACK32(&sub_block[40], &w[10]); PACK32(&sub_block[44], &w[11]);
+        PACK32(&sub_block[48], &w[12]); PACK32(&sub_block[52], &w[13]);
+        PACK32(&sub_block[56], &w[14]); PACK32(&sub_block[60], &w[15]);
+
+        SHA256_SCR(16); SHA256_SCR(17); SHA256_SCR(18); SHA256_SCR(19);
+        SHA256_SCR(20); SHA256_SCR(21); SHA256_SCR(22); SHA256_SCR(23);
+        SHA256_SCR(24); SHA256_SCR(25); SHA256_SCR(26); SHA256_SCR(27);
+        SHA256_SCR(28); SHA256_SCR(29); SHA256_SCR(30); SHA256_SCR(31);
+        SHA256_SCR(32); SHA256_SCR(33); SHA256_SCR(34); SHA256_SCR(35);
+        SHA256_SCR(36); SHA256_SCR(37); SHA256_SCR(38); SHA256_SCR(39);
+        SHA256_SCR(40); SHA256_SCR(41); SHA256_SCR(42); SHA256_SCR(43);
+        SHA256_SCR(44); SHA256_SCR(45); SHA256_SCR(46); SHA256_SCR(47);
+        SHA256_SCR(48); SHA256_SCR(49); SHA256_SCR(50); SHA256_SCR(51);
+        SHA256_SCR(52); SHA256_SCR(53); SHA256_SCR(54); SHA256_SCR(55);
+        SHA256_SCR(56); SHA256_SCR(57); SHA256_SCR(58); SHA256_SCR(59);
+        SHA256_SCR(60); SHA256_SCR(61); SHA256_SCR(62); SHA256_SCR(63);
+
+        wv[0] = ctx->h[0]; wv[1] = ctx->h[1];
+        wv[2] = ctx->h[2]; wv[3] = ctx->h[3];
+        wv[4] = ctx->h[4]; wv[5] = ctx->h[5];
+        wv[6] = ctx->h[6]; wv[7] = ctx->h[7];
+
+        SHA256_EXP(0,1,2,3,4,5,6,7, 0); SHA256_EXP(7,0,1,2,3,4,5,6, 1);
+        SHA256_EXP(6,7,0,1,2,3,4,5, 2); SHA256_EXP(5,6,7,0,1,2,3,4, 3);
+        SHA256_EXP(4,5,6,7,0,1,2,3, 4); SHA256_EXP(3,4,5,6,7,0,1,2, 5);
+        SHA256_EXP(2,3,4,5,6,7,0,1, 6); SHA256_EXP(1,2,3,4,5,6,7,0, 7);
+        SHA256_EXP(0,1,2,3,4,5,6,7, 8); SHA256_EXP(7,0,1,2,3,4,5,6, 9);
+        SHA256_EXP(6,7,0,1,2,3,4,5,10); SHA256_EXP(5,6,7,0,1,2,3,4,11);
+        SHA256_EXP(4,5,6,7,0,1,2,3,12); SHA256_EXP(3,4,5,6,7,0,1,2,13);
+        SHA256_EXP(2,3,4,5,6,7,0,1,14); SHA256_EXP(1,2,3,4,5,6,7,0,15);
+        SHA256_EXP(0,1,2,3,4,5,6,7,16); SHA256_EXP(7,0,1,2,3,4,5,6,17);
+        SHA256_EXP(6,7,0,1,2,3,4,5,18); SHA256_EXP(5,6,7,0,1,2,3,4,19);
+        SHA256_EXP(4,5,6,7,0,1,2,3,20); SHA256_EXP(3,4,5,6,7,0,1,2,21);
+        SHA256_EXP(2,3,4,5,6,7,0,1,22); SHA256_EXP(1,2,3,4,5,6,7,0,23);
+        SHA256_EXP(0,1,2,3,4,5,6,7,24); SHA256_EXP(7,0,1,2,3,4,5,6,25);
+        SHA256_EXP(6,7,0,1,2,3,4,5,26); SHA256_EXP(5,6,7,0,1,2,3,4,27);
+        SHA256_EXP(4,5,6,7,0,1,2,3,28); SHA256_EXP(3,4,5,6,7,0,1,2,29);
+        SHA256_EXP(2,3,4,5,6,7,0,1,30); SHA256_EXP(1,2,3,4,5,6,7,0,31);
+        SHA256_EXP(0,1,2,3,4,5,6,7,32); SHA256_EXP(7,0,1,2,3,4,5,6,33);
+        SHA256_EXP(6,7,0,1,2,3,4,5,34); SHA256_EXP(5,6,7,0,1,2,3,4,35);
+        SHA256_EXP(4,5,6,7,0,1,2,3,36); SHA256_EXP(3,4,5,6,7,0,1,2,37);
+        SHA256_EXP(2,3,4,5,6,7,0,1,38); SHA256_EXP(1,2,3,4,5,6,7,0,39);
+        SHA256_EXP(0,1,2,3,4,5,6,7,40); SHA256_EXP(7,0,1,2,3,4,5,6,41);
+        SHA256_EXP(6,7,0,1,2,3,4,5,42); SHA256_EXP(5,6,7,0,1,2,3,4,43);
+        SHA256_EXP(4,5,6,7,0,1,2,3,44); SHA256_EXP(3,4,5,6,7,0,1,2,45);
+        SHA256_EXP(2,3,4,5,6,7,0,1,46); SHA256_EXP(1,2,3,4,5,6,7,0,47);
+        SHA256_EXP(0,1,2,3,4,5,6,7,48); SHA256_EXP(7,0,1,2,3,4,5,6,49);
+        SHA256_EXP(6,7,0,1,2,3,4,5,50); SHA256_EXP(5,6,7,0,1,2,3,4,51);
+        SHA256_EXP(4,5,6,7,0,1,2,3,52); SHA256_EXP(3,4,5,6,7,0,1,2,53);
+        SHA256_EXP(2,3,4,5,6,7,0,1,54); SHA256_EXP(1,2,3,4,5,6,7,0,55);
+        SHA256_EXP(0,1,2,3,4,5,6,7,56); SHA256_EXP(7,0,1,2,3,4,5,6,57);
+        SHA256_EXP(6,7,0,1,2,3,4,5,58); SHA256_EXP(5,6,7,0,1,2,3,4,59);
+        SHA256_EXP(4,5,6,7,0,1,2,3,60); SHA256_EXP(3,4,5,6,7,0,1,2,61);
+        SHA256_EXP(2,3,4,5,6,7,0,1,62); SHA256_EXP(1,2,3,4,5,6,7,0,63);
+
+        ctx->h[0] += wv[0]; ctx->h[1] += wv[1];
+        ctx->h[2] += wv[2]; ctx->h[3] += wv[3];
+        ctx->h[4] += wv[4]; ctx->h[5] += wv[5];
+        ctx->h[6] += wv[6]; ctx->h[7] += wv[7];
+#endif /* !UNROLL_LOOPS */
+    }
+}
+
+void sha256(const unsigned char *message, unsigned int len, unsigned char *digest)
+{
+    sha256_ctx ctx;
+
+    sha256_init(&ctx);
+    sha256_update(&ctx, message, len);
+    sha256_final(&ctx, digest);
+}
+
+void sha256_init(sha256_ctx *ctx)
+{
+#ifndef UNROLL_LOOPS
+    int i;
+    for (i = 0; i < 8; i++) {
+        ctx->h[i] = sha256_h0[i];
+    }
+#else
+    ctx->h[0] = sha256_h0[0]; ctx->h[1] = sha256_h0[1];
+    ctx->h[2] = sha256_h0[2]; ctx->h[3] = sha256_h0[3];
+    ctx->h[4] = sha256_h0[4]; ctx->h[5] = sha256_h0[5];
+    ctx->h[6] = sha256_h0[6]; ctx->h[7] = sha256_h0[7];
+#endif /* !UNROLL_LOOPS */
+
+    ctx->len = 0;
+    ctx->tot_len = 0;
+}
+
+void sha256_update(sha256_ctx *ctx, const unsigned char *message,
+                   unsigned int len)
+{
+    unsigned int block_nb;
+    unsigned int new_len, rem_len, tmp_len;
+    const unsigned char *shifted_message;
+
+    tmp_len = SHA256_BLOCK_SIZE - ctx->len;
+    rem_len = len < tmp_len ? len : tmp_len;
+
+    memcpy(&ctx->block[ctx->len], message, rem_len);
+
+    if (ctx->len + len < SHA256_BLOCK_SIZE) {
+        ctx->len += len;
+        return;
+    }
+
+    new_len = len - rem_len;
+    block_nb = new_len / SHA256_BLOCK_SIZE;
+
+    shifted_message = message + rem_len;
+
+    sha256_transf(ctx, ctx->block, 1);
+    sha256_transf(ctx, shifted_message, block_nb);
+
+    rem_len = new_len % SHA256_BLOCK_SIZE;
+
+    memcpy(ctx->block, &shifted_message[block_nb << 6],
+           rem_len);
+
+    ctx->len = rem_len;
+    ctx->tot_len += (block_nb + 1) << 6;
+}
+
+void sha256_final(sha256_ctx *ctx, unsigned char *digest)
+{
+    unsigned int block_nb;
+    unsigned int pm_len;
+    unsigned int len_b;
+
+#ifndef UNROLL_LOOPS
+    int i;
+#endif
+
+    block_nb = (1 + ((SHA256_BLOCK_SIZE - 9)
+                     < (ctx->len % SHA256_BLOCK_SIZE)));
+
+    len_b = (ctx->tot_len + ctx->len) << 3;
+    pm_len = block_nb << 6;
+
+    memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+    ctx->block[ctx->len] = 0x80;
+    UNPACK32(len_b, ctx->block + pm_len - 4);
+
+    sha256_transf(ctx, ctx->block, block_nb);
+
+#ifndef UNROLL_LOOPS
+    for (i = 0 ; i < 8; i++) {
+        UNPACK32(ctx->h[i], &digest[i << 2]);
+    }
+#else
+   UNPACK32(ctx->h[0], &digest[ 0]);
+   UNPACK32(ctx->h[1], &digest[ 4]);
+   UNPACK32(ctx->h[2], &digest[ 8]);
+   UNPACK32(ctx->h[3], &digest[12]);
+   UNPACK32(ctx->h[4], &digest[16]);
+   UNPACK32(ctx->h[5], &digest[20]);
+   UNPACK32(ctx->h[6], &digest[24]);
+   UNPACK32(ctx->h[7], &digest[28]);
+#endif /* !UNROLL_LOOPS */
+}
+
+/* SHA-512 functions */
+
+void sha512_transf(sha512_ctx *ctx, const unsigned char *message,
+                   unsigned int block_nb)
+{
+    uint64 w[80];
+    uint64 wv[8];
+    uint64 t1, t2;
+    const unsigned char *sub_block;
+    int i, j;
+
+    for (i = 0; i < (int) block_nb; i++) {
+        sub_block = message + (i << 7);
+
+#ifndef UNROLL_LOOPS
+        for (j = 0; j < 16; j++) {
+            PACK64(&sub_block[j << 3], &w[j]);
+        }
+
+        for (j = 16; j < 80; j++) {
+            SHA512_SCR(j);
+        }
+
+        for (j = 0; j < 8; j++) {
+            wv[j] = ctx->h[j];
+        }
+
+        for (j = 0; j < 80; j++) {
+            t1 = wv[7] + SHA512_F2(wv[4]) + CH(wv[4], wv[5], wv[6])
+                + sha512_k[j] + w[j];
+            t2 = SHA512_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
+            wv[7] = wv[6];
+            wv[6] = wv[5];
+            wv[5] = wv[4];
+            wv[4] = wv[3] + t1;
+            wv[3] = wv[2];
+            wv[2] = wv[1];
+            wv[1] = wv[0];
+            wv[0] = t1 + t2;
+        }
+
+        for (j = 0; j < 8; j++) {
+            ctx->h[j] += wv[j];
+        }
+#else
+        PACK64(&sub_block[  0], &w[ 0]); PACK64(&sub_block[  8], &w[ 1]);
+        PACK64(&sub_block[ 16], &w[ 2]); PACK64(&sub_block[ 24], &w[ 3]);
+        PACK64(&sub_block[ 32], &w[ 4]); PACK64(&sub_block[ 40], &w[ 5]);
+        PACK64(&sub_block[ 48], &w[ 6]); PACK64(&sub_block[ 56], &w[ 7]);
+        PACK64(&sub_block[ 64], &w[ 8]); PACK64(&sub_block[ 72], &w[ 9]);
+        PACK64(&sub_block[ 80], &w[10]); PACK64(&sub_block[ 88], &w[11]);
+        PACK64(&sub_block[ 96], &w[12]); PACK64(&sub_block[104], &w[13]);
+        PACK64(&sub_block[112], &w[14]); PACK64(&sub_block[120], &w[15]);
+
+        SHA512_SCR(16); SHA512_SCR(17); SHA512_SCR(18); SHA512_SCR(19);
+        SHA512_SCR(20); SHA512_SCR(21); SHA512_SCR(22); SHA512_SCR(23);
+        SHA512_SCR(24); SHA512_SCR(25); SHA512_SCR(26); SHA512_SCR(27);
+        SHA512_SCR(28); SHA512_SCR(29); SHA512_SCR(30); SHA512_SCR(31);
+        SHA512_SCR(32); SHA512_SCR(33); SHA512_SCR(34); SHA512_SCR(35);
+        SHA512_SCR(36); SHA512_SCR(37); SHA512_SCR(38); SHA512_SCR(39);
+        SHA512_SCR(40); SHA512_SCR(41); SHA512_SCR(42); SHA512_SCR(43);
+        SHA512_SCR(44); SHA512_SCR(45); SHA512_SCR(46); SHA512_SCR(47);
+        SHA512_SCR(48); SHA512_SCR(49); SHA512_SCR(50); SHA512_SCR(51);
+        SHA512_SCR(52); SHA512_SCR(53); SHA512_SCR(54); SHA512_SCR(55);
+        SHA512_SCR(56); SHA512_SCR(57); SHA512_SCR(58); SHA512_SCR(59);
+        SHA512_SCR(60); SHA512_SCR(61); SHA512_SCR(62); SHA512_SCR(63);
+        SHA512_SCR(64); SHA512_SCR(65); SHA512_SCR(66); SHA512_SCR(67);
+        SHA512_SCR(68); SHA512_SCR(69); SHA512_SCR(70); SHA512_SCR(71);
+        SHA512_SCR(72); SHA512_SCR(73); SHA512_SCR(74); SHA512_SCR(75);
+        SHA512_SCR(76); SHA512_SCR(77); SHA512_SCR(78); SHA512_SCR(79);
+
+        wv[0] = ctx->h[0]; wv[1] = ctx->h[1];
+        wv[2] = ctx->h[2]; wv[3] = ctx->h[3];
+        wv[4] = ctx->h[4]; wv[5] = ctx->h[5];
+        wv[6] = ctx->h[6]; wv[7] = ctx->h[7];
+
+        j = 0;
+
+        do {
+            SHA512_EXP(0,1,2,3,4,5,6,7,j); j++;
+            SHA512_EXP(7,0,1,2,3,4,5,6,j); j++;
+            SHA512_EXP(6,7,0,1,2,3,4,5,j); j++;
+            SHA512_EXP(5,6,7,0,1,2,3,4,j); j++;
+            SHA512_EXP(4,5,6,7,0,1,2,3,j); j++;
+            SHA512_EXP(3,4,5,6,7,0,1,2,j); j++;
+            SHA512_EXP(2,3,4,5,6,7,0,1,j); j++;
+            SHA512_EXP(1,2,3,4,5,6,7,0,j); j++;
+        } while (j < 80);
+
+        ctx->h[0] += wv[0]; ctx->h[1] += wv[1];
+        ctx->h[2] += wv[2]; ctx->h[3] += wv[3];
+        ctx->h[4] += wv[4]; ctx->h[5] += wv[5];
+        ctx->h[6] += wv[6]; ctx->h[7] += wv[7];
+#endif /* !UNROLL_LOOPS */
+    }
+}
+
+void sha512(const unsigned char *message, unsigned int len,
+            unsigned char *digest)
+{
+    sha512_ctx ctx;
+
+    sha512_init(&ctx);
+    sha512_update(&ctx, message, len);
+    sha512_final(&ctx, digest);
+}
+
+void sha512_init(sha512_ctx *ctx)
+{
+#ifndef UNROLL_LOOPS
+    int i;
+    for (i = 0; i < 8; i++) {
+        ctx->h[i] = sha512_h0[i];
+    }
+#else
+    ctx->h[0] = sha512_h0[0]; ctx->h[1] = sha512_h0[1];
+    ctx->h[2] = sha512_h0[2]; ctx->h[3] = sha512_h0[3];
+    ctx->h[4] = sha512_h0[4]; ctx->h[5] = sha512_h0[5];
+    ctx->h[6] = sha512_h0[6]; ctx->h[7] = sha512_h0[7];
+#endif /* !UNROLL_LOOPS */
+
+    ctx->len = 0;
+    ctx->tot_len = 0;
+}
+
+void sha512_update(sha512_ctx *ctx, const unsigned char *message,
+                   unsigned int len)
+{
+    unsigned int block_nb;
+    unsigned int new_len, rem_len, tmp_len;
+    const unsigned char *shifted_message;
+
+    tmp_len = SHA512_BLOCK_SIZE - ctx->len;
+    rem_len = len < tmp_len ? len : tmp_len;
+
+    memcpy(&ctx->block[ctx->len], message, rem_len);
+
+    if (ctx->len + len < SHA512_BLOCK_SIZE) {
+        ctx->len += len;
+        return;
+    }
+
+    new_len = len - rem_len;
+    block_nb = new_len / SHA512_BLOCK_SIZE;
+
+    shifted_message = message + rem_len;
+
+    sha512_transf(ctx, ctx->block, 1);
+    sha512_transf(ctx, shifted_message, block_nb);
+
+    rem_len = new_len % SHA512_BLOCK_SIZE;
+
+    memcpy(ctx->block, &shifted_message[block_nb << 7],
+           rem_len);
+
+    ctx->len = rem_len;
+    ctx->tot_len += (block_nb + 1) << 7;
+}
+
+void sha512_final(sha512_ctx *ctx, unsigned char *digest)
+{
+    unsigned int block_nb;
+    unsigned int pm_len;
+    unsigned int len_b;
+
+#ifndef UNROLL_LOOPS
+    int i;
+#endif
+
+    block_nb = 1 + ((SHA512_BLOCK_SIZE - 17)
+                     < (ctx->len % SHA512_BLOCK_SIZE));
+
+    len_b = (ctx->tot_len + ctx->len) << 3;
+    pm_len = block_nb << 7;
+
+    memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+    ctx->block[ctx->len] = 0x80;
+    UNPACK32(len_b, ctx->block + pm_len - 4);
+
+    sha512_transf(ctx, ctx->block, block_nb);
+
+#ifndef UNROLL_LOOPS
+    for (i = 0 ; i < 8; i++) {
+        UNPACK64(ctx->h[i], &digest[i << 3]);
+    }
+#else
+    UNPACK64(ctx->h[0], &digest[ 0]);
+    UNPACK64(ctx->h[1], &digest[ 8]);
+    UNPACK64(ctx->h[2], &digest[16]);
+    UNPACK64(ctx->h[3], &digest[24]);
+    UNPACK64(ctx->h[4], &digest[32]);
+    UNPACK64(ctx->h[5], &digest[40]);
+    UNPACK64(ctx->h[6], &digest[48]);
+    UNPACK64(ctx->h[7], &digest[56]);
+#endif /* !UNROLL_LOOPS */
+}
+
+/* SHA-384 functions */
+
+void sha384(const unsigned char *message, unsigned int len,
+            unsigned char *digest)
+{
+    sha384_ctx ctx;
+
+    sha384_init(&ctx);
+    sha384_update(&ctx, message, len);
+    sha384_final(&ctx, digest);
+}
+
+void sha384_init(sha384_ctx *ctx)
+{
+#ifndef UNROLL_LOOPS
+    int i;
+    for (i = 0; i < 8; i++) {
+        ctx->h[i] = sha384_h0[i];
+    }
+#else
+    ctx->h[0] = sha384_h0[0]; ctx->h[1] = sha384_h0[1];
+    ctx->h[2] = sha384_h0[2]; ctx->h[3] = sha384_h0[3];
+    ctx->h[4] = sha384_h0[4]; ctx->h[5] = sha384_h0[5];
+    ctx->h[6] = sha384_h0[6]; ctx->h[7] = sha384_h0[7];
+#endif /* !UNROLL_LOOPS */
+
+    ctx->len = 0;
+    ctx->tot_len = 0;
+}
+
+void sha384_update(sha384_ctx *ctx, const unsigned char *message,
+                   unsigned int len)
+{
+    unsigned int block_nb;
+    unsigned int new_len, rem_len, tmp_len;
+    const unsigned char *shifted_message;
+
+    tmp_len = SHA384_BLOCK_SIZE - ctx->len;
+    rem_len = len < tmp_len ? len : tmp_len;
+
+    memcpy(&ctx->block[ctx->len], message, rem_len);
+
+    if (ctx->len + len < SHA384_BLOCK_SIZE) {
+        ctx->len += len;
+        return;
+    }
+
+    new_len = len - rem_len;
+    block_nb = new_len / SHA384_BLOCK_SIZE;
+
+    shifted_message = message + rem_len;
+
+    sha512_transf(ctx, ctx->block, 1);
+    sha512_transf(ctx, shifted_message, block_nb);
+
+    rem_len = new_len % SHA384_BLOCK_SIZE;
+
+    memcpy(ctx->block, &shifted_message[block_nb << 7],
+           rem_len);
+
+    ctx->len = rem_len;
+    ctx->tot_len += (block_nb + 1) << 7;
+}
+
+void sha384_final(sha384_ctx *ctx, unsigned char *digest)
+{
+    unsigned int block_nb;
+    unsigned int pm_len;
+    unsigned int len_b;
+
+#ifndef UNROLL_LOOPS
+    int i;
+#endif
+
+    block_nb = (1 + ((SHA384_BLOCK_SIZE - 17)
+                     < (ctx->len % SHA384_BLOCK_SIZE)));
+
+    len_b = (ctx->tot_len + ctx->len) << 3;
+    pm_len = block_nb << 7;
+
+    memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+    ctx->block[ctx->len] = 0x80;
+    UNPACK32(len_b, ctx->block + pm_len - 4);
+
+    sha512_transf(ctx, ctx->block, block_nb);
+
+#ifndef UNROLL_LOOPS
+    for (i = 0 ; i < 6; i++) {
+        UNPACK64(ctx->h[i], &digest[i << 3]);
+    }
+#else
+    UNPACK64(ctx->h[0], &digest[ 0]);
+    UNPACK64(ctx->h[1], &digest[ 8]);
+    UNPACK64(ctx->h[2], &digest[16]);
+    UNPACK64(ctx->h[3], &digest[24]);
+    UNPACK64(ctx->h[4], &digest[32]);
+    UNPACK64(ctx->h[5], &digest[40]);
+#endif /* !UNROLL_LOOPS */
+}
+
+/* SHA-224 functions */
+
+void sha224(const unsigned char *message, unsigned int len,
+            unsigned char *digest)
+{
+    sha224_ctx ctx;
+
+    sha224_init(&ctx);
+    sha224_update(&ctx, message, len);
+    sha224_final(&ctx, digest);
+}
+
+void sha224_init(sha224_ctx *ctx)
+{
+#ifndef UNROLL_LOOPS
+    int i;
+    for (i = 0; i < 8; i++) {
+        ctx->h[i] = sha224_h0[i];
+    }
+#else
+    ctx->h[0] = sha224_h0[0]; ctx->h[1] = sha224_h0[1];
+    ctx->h[2] = sha224_h0[2]; ctx->h[3] = sha224_h0[3];
+    ctx->h[4] = sha224_h0[4]; ctx->h[5] = sha224_h0[5];
+    ctx->h[6] = sha224_h0[6]; ctx->h[7] = sha224_h0[7];
+#endif /* !UNROLL_LOOPS */
+
+    ctx->len = 0;
+    ctx->tot_len = 0;
+}
+
+void sha224_update(sha224_ctx *ctx, const unsigned char *message,
+                   unsigned int len)
+{
+    unsigned int block_nb;
+    unsigned int new_len, rem_len, tmp_len;
+    const unsigned char *shifted_message;
+
+    tmp_len = SHA224_BLOCK_SIZE - ctx->len;
+    rem_len = len < tmp_len ? len : tmp_len;
+
+    memcpy(&ctx->block[ctx->len], message, rem_len);
+
+    if (ctx->len + len < SHA224_BLOCK_SIZE) {
+        ctx->len += len;
+        return;
+    }
+
+    new_len = len - rem_len;
+    block_nb = new_len / SHA224_BLOCK_SIZE;
+
+    shifted_message = message + rem_len;
+
+    sha256_transf(ctx, ctx->block, 1);
+    sha256_transf(ctx, shifted_message, block_nb);
+
+    rem_len = new_len % SHA224_BLOCK_SIZE;
+
+    memcpy(ctx->block, &shifted_message[block_nb << 6],
+           rem_len);
+
+    ctx->len = rem_len;
+    ctx->tot_len += (block_nb + 1) << 6;
+}
+
+void sha224_final(sha224_ctx *ctx, unsigned char *digest)
+{
+    unsigned int block_nb;
+    unsigned int pm_len;
+    unsigned int len_b;
+
+#ifndef UNROLL_LOOPS
+    int i;
+#endif
+
+    block_nb = (1 + ((SHA224_BLOCK_SIZE - 9)
+                     < (ctx->len % SHA224_BLOCK_SIZE)));
+
+    len_b = (ctx->tot_len + ctx->len) << 3;
+    pm_len = block_nb << 6;
+
+    memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+    ctx->block[ctx->len] = 0x80;
+    UNPACK32(len_b, ctx->block + pm_len - 4);
+
+    sha256_transf(ctx, ctx->block, block_nb);
+
+#ifndef UNROLL_LOOPS
+    for (i = 0 ; i < 7; i++) {
+        UNPACK32(ctx->h[i], &digest[i << 2]);
+    }
+#else
+   UNPACK32(ctx->h[0], &digest[ 0]);
+   UNPACK32(ctx->h[1], &digest[ 4]);
+   UNPACK32(ctx->h[2], &digest[ 8]);
+   UNPACK32(ctx->h[3], &digest[12]);
+   UNPACK32(ctx->h[4], &digest[16]);
+   UNPACK32(ctx->h[5], &digest[20]);
+   UNPACK32(ctx->h[6], &digest[24]);
+#endif /* !UNROLL_LOOPS */
+}
+
+#ifdef TEST_VECTORS
+
+/* FIPS 180-2 Validation tests */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void test(const char *vector, unsigned char *digest,
+          unsigned int digest_size)
+{
+    char output[2 * SHA512_DIGEST_SIZE + 1];
+    int i;
+
+    output[2 * digest_size] = '\0';
+
+    for (i = 0; i < (int) digest_size ; i++) {
+       sprintf(output + 2 * i, "%02x", digest[i]);
+    }
+
+    printf("H: %s\n", output);
+    if (strcmp(vector, output)) {
+        fprintf(stderr, "Test failed.\n");
+        exit(EXIT_FAILURE);
+    }
+}
+
+int main(void)
+{
+    static const char *vectors[4][3] =
+    {   /* SHA-224 */
+        {
+        "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7",
+        "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525",
+        "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67",
+        },
+        /* SHA-256 */
+        {
+        "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+        "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1",
+        "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0",
+        },
+        /* SHA-384 */
+        {
+        "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed"
+        "8086072ba1e7cc2358baeca134c825a7",
+        "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712"
+        "fcc7c71a557e2db966c3e9fa91746039",
+        "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b"
+        "07b8b3dc38ecc4ebae97ddd87f3d8985",
+        },
+        /* SHA-512 */
+        {
+        "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a"
+        "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
+        "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018"
+        "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909",
+        "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb"
+        "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"
+        }
+    };
+
+    static const char message1[] = "abc";
+    static const char message2a[] = "abcdbcdecdefdefgefghfghighijhi"
+                                    "jkijkljklmklmnlmnomnopnopq";
+    static const char message2b[] = "abcdefghbcdefghicdefghijdefghijkefghij"
+                                    "klfghijklmghijklmnhijklmnoijklmnopjklm"
+                                    "nopqklmnopqrlmnopqrsmnopqrstnopqrstu";
+    unsigned char *message3;
+    unsigned int message3_len = 1000000;
+    unsigned char digest[SHA512_DIGEST_SIZE];
+
+    message3 = malloc(message3_len);
+    if (message3 == NULL) {
+        fprintf(stderr, "Can't allocate memory\n");
+        return -1;
+    }
+    memset(message3, 'a', message3_len);
+
+    printf("SHA-2 FIPS 180-2 Validation tests\n\n");
+    printf("SHA-224 Test vectors\n");
+
+    sha224((const unsigned char *) message1, strlen(message1), digest);
+    test(vectors[0][0], digest, SHA224_DIGEST_SIZE);
+    sha224((const unsigned char *) message2a, strlen(message2a), digest);
+    test(vectors[0][1], digest, SHA224_DIGEST_SIZE);
+    sha224(message3, message3_len, digest);
+    test(vectors[0][2], digest, SHA224_DIGEST_SIZE);
+    printf("\n");
+
+    printf("SHA-256 Test vectors\n");
+
+    sha256((const unsigned char *) message1, strlen(message1), digest);
+    test(vectors[1][0], digest, SHA256_DIGEST_SIZE);
+    sha256((const unsigned char *) message2a, strlen(message2a), digest);
+    test(vectors[1][1], digest, SHA256_DIGEST_SIZE);
+    sha256(message3, message3_len, digest);
+    test(vectors[1][2], digest, SHA256_DIGEST_SIZE);
+    printf("\n");
+
+    printf("SHA-384 Test vectors\n");
+
+    sha384((const unsigned char *) message1, strlen(message1), digest);
+    test(vectors[2][0], digest, SHA384_DIGEST_SIZE);
+    sha384((const unsigned char *)message2b, strlen(message2b), digest);
+    test(vectors[2][1], digest, SHA384_DIGEST_SIZE);
+    sha384(message3, message3_len, digest);
+    test(vectors[2][2], digest, SHA384_DIGEST_SIZE);
+    printf("\n");
+
+    printf("SHA-512 Test vectors\n");
+
+    sha512((const unsigned char *) message1, strlen(message1), digest);
+    test(vectors[3][0], digest, SHA512_DIGEST_SIZE);
+    sha512((const unsigned char *) message2b, strlen(message2b), digest);
+    test(vectors[3][1], digest, SHA512_DIGEST_SIZE);
+    sha512(message3, message3_len, digest);
+    test(vectors[3][2], digest, SHA512_DIGEST_SIZE);
+    printf("\n");
+
+    printf("All tests passed.\n");
+
+    return 0;
+}
+
+#endif /* TEST_VECTORS */
+
diff --git a/vendor/sha2/sha2.h b/vendor/sha2/sha2.h
new file mode 100644 (file)
index 0000000..60f52e3
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * FIPS 180-2 SHA-224/256/384/512 implementation
+ * Last update: 02/02/2007
+ * Issue date:  04/30/2005
+ *
+ * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SHA2_H
+#define SHA2_H
+
+#define SHA224_DIGEST_SIZE ( 224 / 8)
+#define SHA256_DIGEST_SIZE ( 256 / 8)
+#define SHA384_DIGEST_SIZE ( 384 / 8)
+#define SHA512_DIGEST_SIZE ( 512 / 8)
+
+#define SHA256_BLOCK_SIZE  ( 512 / 8)
+#define SHA512_BLOCK_SIZE  (1024 / 8)
+#define SHA384_BLOCK_SIZE  SHA512_BLOCK_SIZE
+#define SHA224_BLOCK_SIZE  SHA256_BLOCK_SIZE
+
+#ifndef SHA2_TYPES
+#define SHA2_TYPES
+typedef unsigned char uint8;
+typedef unsigned int  uint32;
+typedef unsigned long long uint64;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+    unsigned int tot_len;
+    unsigned int len;
+    unsigned char block[2 * SHA256_BLOCK_SIZE];
+    uint32 h[8];
+} sha256_ctx;
+
+typedef struct {
+    unsigned int tot_len;
+    unsigned int len;
+    unsigned char block[2 * SHA512_BLOCK_SIZE];
+    uint64 h[8];
+} sha512_ctx;
+
+typedef sha512_ctx sha384_ctx;
+typedef sha256_ctx sha224_ctx;
+
+void sha224_init(sha224_ctx *ctx);
+void sha224_update(sha224_ctx *ctx, const unsigned char *message,
+                   unsigned int len);
+void sha224_final(sha224_ctx *ctx, unsigned char *digest);
+void sha224(const unsigned char *message, unsigned int len,
+            unsigned char *digest);
+
+void sha256_init(sha256_ctx * ctx);
+void sha256_update(sha256_ctx *ctx, const unsigned char *message,
+                   unsigned int len);
+void sha256_final(sha256_ctx *ctx, unsigned char *digest);
+void sha256(const unsigned char *message, unsigned int len,
+            unsigned char *digest);
+
+void sha384_init(sha384_ctx *ctx);
+void sha384_update(sha384_ctx *ctx, const unsigned char *message,
+                   unsigned int len);
+void sha384_final(sha384_ctx *ctx, unsigned char *digest);
+void sha384(const unsigned char *message, unsigned int len,
+            unsigned char *digest);
+
+void sha512_init(sha512_ctx *ctx);
+void sha512_update(sha512_ctx *ctx, const unsigned char *message,
+                   unsigned int len);
+void sha512_final(sha512_ctx *ctx, unsigned char *digest);
+void sha512(const unsigned char *message, unsigned int len,
+            unsigned char *digest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !SHA2_H */
+
diff --git a/vendor/utfcpp/utf8.h b/vendor/utfcpp/utf8.h
new file mode 100644 (file)
index 0000000..c2c85d6
--- /dev/null
@@ -0,0 +1,38 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include "utf8/checked.h"
+#include "utf8/unchecked.h"
+
+#if __cplusplus >= 201103L // C++ 11 or later
+#include "utf8/cpp11.h"
+#endif // C++ 11 or later
+
+#endif // header guard
diff --git a/vendor/utfcpp/utf8/checked.h b/vendor/utfcpp/utf8/checked.h
new file mode 100644 (file)
index 0000000..c31861e
--- /dev/null
@@ -0,0 +1,324 @@
+// Copyright 2006-2016 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include "core.h"
+#include <stdexcept>
+
+namespace utf8
+{
+    // Base for the exceptions that may be thrown from the library
+    class exception : public ::std::exception {
+    };
+
+    // Exceptions that may be thrown from the library functions.
+    class invalid_code_point : public exception {
+        uint32_t cp;
+    public:
+        invalid_code_point(uint32_t codepoint) : cp(codepoint) {}
+        virtual const char* what() const throw() { return "Invalid code point"; }
+        uint32_t code_point() const {return cp;}
+    };
+
+    class invalid_utf8 : public exception {
+        uint8_t u8;
+    public:
+        invalid_utf8 (uint8_t u) : u8(u) {}
+        virtual const char* what() const throw() { return "Invalid UTF-8"; }
+        uint8_t utf8_octet() const {return u8;}
+    };
+
+    class invalid_utf16 : public exception {
+        uint16_t u16;
+    public:
+        invalid_utf16 (uint16_t u) : u16(u) {}
+        virtual const char* what() const throw() { return "Invalid UTF-16"; }
+        uint16_t utf16_word() const {return u16;}
+    };
+
+    class not_enough_room : public exception {
+    public:
+        virtual const char* what() const throw() { return "Not enough space"; }
+    };
+
+    /// The library API - functions intended to be called by the users
+
+    template <typename octet_iterator>
+    octet_iterator append(uint32_t cp, octet_iterator result)
+    {
+        if (!utf8::internal::is_code_point_valid(cp))
+            throw invalid_code_point(cp);
+
+        if (cp < 0x80)                        // one octet
+            *(result++) = static_cast<uint8_t>(cp);
+        else if (cp < 0x800) {                // two octets
+            *(result++) = static_cast<uint8_t>((cp >> 6)            | 0xc0);
+            *(result++) = static_cast<uint8_t>((cp & 0x3f)          | 0x80);
+        }
+        else if (cp < 0x10000) {              // three octets
+            *(result++) = static_cast<uint8_t>((cp >> 12)           | 0xe0);
+            *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f)   | 0x80);
+            *(result++) = static_cast<uint8_t>((cp & 0x3f)          | 0x80);
+        }
+        else {                                // four octets
+            *(result++) = static_cast<uint8_t>((cp >> 18)           | 0xf0);
+            *(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f)  | 0x80);
+            *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f)   | 0x80);
+            *(result++) = static_cast<uint8_t>((cp & 0x3f)          | 0x80);
+        }
+        return result;
+    }
+
+    template <typename octet_iterator, typename output_iterator>
+    output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
+    {
+        while (start != end) {
+            octet_iterator sequence_start = start;
+            internal::utf_error err_code = utf8::internal::validate_next(start, end);
+            switch (err_code) {
+                case internal::UTF8_OK :
+                    for (octet_iterator it = sequence_start; it != start; ++it)
+                        *out++ = *it;
+                    break;
+                case internal::NOT_ENOUGH_ROOM:
+                    out = utf8::append (replacement, out);
+                    start = end;
+                    break;
+                case internal::INVALID_LEAD:
+                    out = utf8::append (replacement, out);
+                    ++start;
+                    break;
+                case internal::INCOMPLETE_SEQUENCE:
+                case internal::OVERLONG_SEQUENCE:
+                case internal::INVALID_CODE_POINT:
+                    out = utf8::append (replacement, out);
+                    ++start;
+                    // just one replacement mark for the sequence
+                    while (start != end && utf8::internal::is_trail(*start))
+                        ++start;
+                    break;
+            }
+        }
+        return out;
+    }
+
+    template <typename octet_iterator, typename output_iterator>
+    inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
+    {
+        static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);
+        return utf8::replace_invalid(start, end, out, replacement_marker);
+    }
+
+    template <typename octet_iterator>
+    uint32_t next(octet_iterator& it, octet_iterator end)
+    {
+        uint32_t cp = 0;
+        internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);
+        switch (err_code) {
+            case internal::UTF8_OK :
+                break;
+            case internal::NOT_ENOUGH_ROOM :
+                throw not_enough_room();
+            case internal::INVALID_LEAD :
+            case internal::INCOMPLETE_SEQUENCE :
+            case internal::OVERLONG_SEQUENCE :
+                throw invalid_utf8(*it);
+            case internal::INVALID_CODE_POINT :
+                throw invalid_code_point(cp);
+        }
+        return cp;
+    }
+
+    template <typename octet_iterator>
+    uint32_t peek_next(octet_iterator it, octet_iterator end)
+    {
+        return utf8::next(it, end);
+    }
+
+    template <typename octet_iterator>
+    uint32_t prior(octet_iterator& it, octet_iterator start)
+    {
+        // can't do much if it == start
+        if (it == start)
+            throw not_enough_room();
+
+        octet_iterator end = it;
+        // Go back until we hit either a lead octet or start
+        while (utf8::internal::is_trail(*(--it)))
+            if (it == start)
+                throw invalid_utf8(*it); // error - no lead byte in the sequence
+        return utf8::peek_next(it, end);
+    }
+
+    template <typename octet_iterator, typename distance_type>
+    void advance (octet_iterator& it, distance_type n, octet_iterator end)
+    {
+        const distance_type zero(0);
+        if (n < zero) {
+            // backward
+            for (distance_type i = n; i < zero; ++i)
+                utf8::prior(it, end);
+        } else {
+            // forward
+            for (distance_type i = zero; i < n; ++i)
+                utf8::next(it, end);
+        }
+    }
+
+    template <typename octet_iterator>
+    typename std::iterator_traits<octet_iterator>::difference_type
+    distance (octet_iterator first, octet_iterator last)
+    {
+        typename std::iterator_traits<octet_iterator>::difference_type dist;
+        for (dist = 0; first < last; ++dist)
+            utf8::next(first, last);
+        return dist;
+    }
+
+    template <typename u16bit_iterator, typename octet_iterator>
+    octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
+    {
+        while (start != end) {
+            uint32_t cp = utf8::internal::mask16(*start++);
+            // Take care of surrogate pairs first
+            if (utf8::internal::is_lead_surrogate(cp)) {
+                if (start != end) {
+                    uint32_t trail_surrogate = utf8::internal::mask16(*start++);
+                    if (utf8::internal::is_trail_surrogate(trail_surrogate))
+                        cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
+                    else
+                        throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
+                }
+                else
+                    throw invalid_utf16(static_cast<uint16_t>(cp));
+
+            }
+            // Lone trail surrogate
+            else if (utf8::internal::is_trail_surrogate(cp))
+                throw invalid_utf16(static_cast<uint16_t>(cp));
+
+            result = utf8::append(cp, result);
+        }
+        return result;
+    }
+
+    template <typename u16bit_iterator, typename octet_iterator>
+    u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
+    {
+        while (start < end) {
+            uint32_t cp = utf8::next(start, end);
+            if (cp > 0xffff) { //make a surrogate pair
+                *result++ = static_cast<uint16_t>((cp >> 10)   + internal::LEAD_OFFSET);
+                *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
+            }
+            else
+                *result++ = static_cast<uint16_t>(cp);
+        }
+        return result;
+    }
+
+    template <typename octet_iterator, typename u32bit_iterator>
+    octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
+    {
+        while (start != end)
+            result = utf8::append(*(start++), result);
+
+        return result;
+    }
+
+    template <typename octet_iterator, typename u32bit_iterator>
+    u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
+    {
+        while (start < end)
+            (*result++) = utf8::next(start, end);
+
+        return result;
+    }
+
+    // The iterator class
+    template <typename octet_iterator>
+    class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
+      octet_iterator it;
+      octet_iterator range_start;
+      octet_iterator range_end;
+      public:
+      iterator () {}
+      explicit iterator (const octet_iterator& octet_it,
+                         const octet_iterator& rangestart,
+                         const octet_iterator& rangeend) :
+               it(octet_it), range_start(rangestart), range_end(rangeend)
+      {
+          if (it < range_start || it > range_end)
+              throw std::out_of_range("Invalid utf-8 iterator position");
+      }
+      // the default "big three" are OK
+      octet_iterator base () const { return it; }
+      uint32_t operator * () const
+      {
+          octet_iterator temp = it;
+          return utf8::next(temp, range_end);
+      }
+      bool operator == (const iterator& rhs) const
+      {
+          if (range_start != rhs.range_start || range_end != rhs.range_end)
+              throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
+          return (it == rhs.it);
+      }
+      bool operator != (const iterator& rhs) const
+      {
+          return !(operator == (rhs));
+      }
+      iterator& operator ++ ()
+      {
+          utf8::next(it, range_end);
+          return *this;
+      }
+      iterator operator ++ (int)
+      {
+          iterator temp = *this;
+          utf8::next(it, range_end);
+          return temp;
+      }
+      iterator& operator -- ()
+      {
+          utf8::prior(it, range_start);
+          return *this;
+      }
+      iterator operator -- (int)
+      {
+          iterator temp = *this;
+          utf8::prior(it, range_start);
+          return temp;
+      }
+    }; // class iterator
+
+} // namespace utf8
+
+#endif //header guard
+
diff --git a/vendor/utfcpp/utf8/core.h b/vendor/utfcpp/utf8/core.h
new file mode 100644 (file)
index 0000000..e007ca1
--- /dev/null
@@ -0,0 +1,321 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include <iterator>
+
+namespace utf8
+{
+    // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
+    // You may need to change them to match your system.
+    // These typedefs have the same names as ones from cstdint, or boost/cstdint
+    typedef unsigned char   uint8_t;
+    typedef unsigned short  uint16_t;
+    typedef unsigned int    uint32_t;
+
+// Helper code - not intended to be directly called by the library users. May be changed at any time
+namespace internal
+{
+    // Unicode constants
+    // Leading (high) surrogates: 0xd800 - 0xdbff
+    // Trailing (low) surrogates: 0xdc00 - 0xdfff
+    const uint16_t LEAD_SURROGATE_MIN  = 0xd800u;
+    const uint16_t LEAD_SURROGATE_MAX  = 0xdbffu;
+    const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
+    const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
+    const uint16_t LEAD_OFFSET         = 0xd7c0u;       // LEAD_SURROGATE_MIN - (0x10000 >> 10)
+    const uint32_t SURROGATE_OFFSET    = 0xfca02400u;   // 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN
+
+    // Maximum valid value for a Unicode code point
+    const uint32_t CODE_POINT_MAX      = 0x0010ffffu;
+
+    template<typename octet_type>
+    inline uint8_t mask8(octet_type oc)
+    {
+        return static_cast<uint8_t>(0xff & oc);
+    }
+    template<typename u16_type>
+    inline uint16_t mask16(u16_type oc)
+    {
+        return static_cast<uint16_t>(0xffff & oc);
+    }
+    template<typename octet_type>
+    inline bool is_trail(octet_type oc)
+    {
+        return ((utf8::internal::mask8(oc) >> 6) == 0x2);
+    }
+
+    template <typename u16>
+    inline bool is_lead_surrogate(u16 cp)
+    {
+        return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);
+    }
+
+    template <typename u16>
+    inline bool is_trail_surrogate(u16 cp)
+    {
+        return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
+    }
+
+    template <typename u16>
+    inline bool is_surrogate(u16 cp)
+    {
+        return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
+    }
+
+    template <typename u32>
+    inline bool is_code_point_valid(u32 cp)
+    {
+        return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));
+    }
+
+    template <typename octet_iterator>
+    inline typename std::iterator_traits<octet_iterator>::difference_type
+    sequence_length(octet_iterator lead_it)
+    {
+        uint8_t lead = utf8::internal::mask8(*lead_it);
+        if (lead < 0x80)
+            return 1;
+        else if ((lead >> 5) == 0x6)
+            return 2;
+        else if ((lead >> 4) == 0xe)
+            return 3;
+        else if ((lead >> 3) == 0x1e)
+            return 4;
+        else
+            return 0;
+    }
+
+    template <typename octet_difference_type>
+    inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)
+    {
+        if (cp < 0x80) {
+            if (length != 1) 
+                return true;
+        }
+        else if (cp < 0x800) {
+            if (length != 2) 
+                return true;
+        }
+        else if (cp < 0x10000) {
+            if (length != 3) 
+                return true;
+        }
+
+        return false;
+    }
+
+    enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
+
+    /// Helper for get_sequence_x
+    template <typename octet_iterator>
+    utf_error increase_safely(octet_iterator& it, octet_iterator end)
+    {
+        if (++it == end)
+            return NOT_ENOUGH_ROOM;
+
+        if (!utf8::internal::is_trail(*it))
+            return INCOMPLETE_SEQUENCE;
+
+        return UTF8_OK;
+    }
+
+    #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;}    
+
+    /// get_sequence_x functions decode utf-8 sequences of the length x
+    template <typename octet_iterator>
+    utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)
+    {
+        if (it == end)
+            return NOT_ENOUGH_ROOM;
+
+        code_point = utf8::internal::mask8(*it);
+
+        return UTF8_OK;
+    }
+
+    template <typename octet_iterator>
+    utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)
+    {
+        if (it == end) 
+            return NOT_ENOUGH_ROOM;
+
+        code_point = utf8::internal::mask8(*it);
+
+        UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+        code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);
+
+        return UTF8_OK;
+    }
+
+    template <typename octet_iterator>
+    utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)
+    {
+        if (it == end)
+            return NOT_ENOUGH_ROOM;
+            
+        code_point = utf8::internal::mask8(*it);
+
+        UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+        code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
+
+        UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+        code_point += (*it) & 0x3f;
+
+        return UTF8_OK;
+    }
+
+    template <typename octet_iterator>
+    utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)
+    {
+        if (it == end)
+           return NOT_ENOUGH_ROOM;
+
+        code_point = utf8::internal::mask8(*it);
+
+        UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+        code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
+
+        UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+        code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;
+
+        UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+        code_point += (*it) & 0x3f;
+
+        return UTF8_OK;
+    }
+
+    #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR
+
+    template <typename octet_iterator>
+    utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)
+    {
+        if (it == end)
+            return NOT_ENOUGH_ROOM;
+
+        // Save the original value of it so we can go back in case of failure
+        // Of course, it does not make much sense with i.e. stream iterators
+        octet_iterator original_it = it;
+
+        uint32_t cp = 0;
+        // Determine the sequence length based on the lead octet
+        typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
+        const octet_difference_type length = utf8::internal::sequence_length(it);
+
+        // Get trail octets and calculate the code point
+        utf_error err = UTF8_OK;
+        switch (length) {
+            case 0:
+                return INVALID_LEAD;
+            case 1:
+                err = utf8::internal::get_sequence_1(it, end, cp);
+                break;
+            case 2:
+                err = utf8::internal::get_sequence_2(it, end, cp);
+            break;
+            case 3:
+                err = utf8::internal::get_sequence_3(it, end, cp);
+            break;
+            case 4:
+                err = utf8::internal::get_sequence_4(it, end, cp);
+            break;
+        }
+
+        if (err == UTF8_OK) {
+            // Decoding succeeded. Now, security checks...
+            if (utf8::internal::is_code_point_valid(cp)) {
+                if (!utf8::internal::is_overlong_sequence(cp, length)){
+                    // Passed! Return here.
+                    code_point = cp;
+                    ++it;
+                    return UTF8_OK;
+                }
+                else
+                    err = OVERLONG_SEQUENCE;
+            }
+            else 
+                err = INVALID_CODE_POINT;
+        }
+
+        // Failure branch - restore the original value of the iterator
+        it = original_it;
+        return err;
+    }
+
+    template <typename octet_iterator>
+    inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
+        uint32_t ignored;
+        return utf8::internal::validate_next(it, end, ignored);
+    }
+
+} // namespace internal
+
+    /// The library API - functions intended to be called by the users
+
+    // Byte order mark
+    const uint8_t bom[] = {0xef, 0xbb, 0xbf};
+
+    template <typename octet_iterator>
+    octet_iterator find_invalid(octet_iterator start, octet_iterator end)
+    {
+        octet_iterator result = start;
+        while (result != end) {
+            utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);
+            if (err_code != internal::UTF8_OK)
+                return result;
+        }
+        return result;
+    }
+
+    template <typename octet_iterator>
+    inline bool is_valid(octet_iterator start, octet_iterator end)
+    {
+        return (utf8::find_invalid(start, end) == end);
+    }
+
+    template <typename octet_iterator>
+    inline bool starts_with_bom (octet_iterator it, octet_iterator end)
+    {
+        return (
+            ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&
+            ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&
+            ((it != end) && (utf8::internal::mask8(*it))   == bom[2])
+           );
+    }  
+} // namespace utf8
+
+#endif // header guard
+
+
diff --git a/vendor/utfcpp/utf8/cpp11.h b/vendor/utfcpp/utf8/cpp11.h
new file mode 100644 (file)
index 0000000..d93961b
--- /dev/null
@@ -0,0 +1,103 @@
+// Copyright 2018 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_a184c22c_d012_11e8_a8d5_f2801f1b9fd1
+#define UTF8_FOR_CPP_a184c22c_d012_11e8_a8d5_f2801f1b9fd1
+
+#include "checked.h"
+#include <string>
+
+namespace utf8
+{
+
+    inline void append(char32_t cp, std::string& s)
+    {
+        append(uint32_t(cp), std::back_inserter(s));
+    }
+
+    inline std::string utf16to8(const std::u16string& s)
+    {
+        std::string result;
+        utf16to8(s.begin(), s.end(), std::back_inserter(result));
+        return result;
+    }
+
+    inline std::u16string utf8to16(const std::string& s)
+    {
+        std::u16string result;
+        utf8to16(s.begin(), s.end(), std::back_inserter(result));
+        return result;
+    }
+
+    inline std::string utf32to8(const std::u32string& s)
+    {
+        std::string result;
+        utf32to8(s.begin(), s.end(), std::back_inserter(result));
+        return result;
+    }
+
+    inline std::u32string utf8to32(const std::string& s)
+    {
+        std::u32string result;
+        utf8to32(s.begin(), s.end(), std::back_inserter(result));
+        return result;
+    }
+
+    inline std::size_t find_invalid(const std::string& s)
+    {
+        std::string::const_iterator invalid = find_invalid(s.begin(), s.end());
+        return (invalid == s.end()) ? std::string::npos : (invalid - s.begin());
+    }
+
+    inline bool is_valid(const std::string& s)
+    {
+        return is_valid(s.begin(), s.end());
+    }
+
+    inline std::string replace_invalid(const std::string& s, char32_t replacement)
+    {
+        std::string result;
+        replace_invalid(s.begin(), s.end(), std::back_inserter(result), replacement);
+        return result;
+    }
+
+    inline std::string replace_invalid(const std::string& s)
+    {
+        std::string result;
+        replace_invalid(s.begin(), s.end(), std::back_inserter(result));
+        return result;
+    }
+
+    inline bool starts_with_bom(const std::string& s)
+    {
+        return starts_with_bom(s.begin(), s.end());
+    }
+} // namespace utf8
+
+#endif // header guard
+
diff --git a/vendor/utfcpp/utf8/unchecked.h b/vendor/utfcpp/utf8/unchecked.h
new file mode 100644 (file)
index 0000000..c78419f
--- /dev/null
@@ -0,0 +1,229 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include "core.h"
+
+namespace utf8
+{
+    namespace unchecked 
+    {
+        template <typename octet_iterator>
+        octet_iterator append(uint32_t cp, octet_iterator result)
+        {
+            if (cp < 0x80)                        // one octet
+                *(result++) = static_cast<uint8_t>(cp);
+            else if (cp < 0x800) {                // two octets
+                *(result++) = static_cast<uint8_t>((cp >> 6)          | 0xc0);
+                *(result++) = static_cast<uint8_t>((cp & 0x3f)        | 0x80);
+            }
+            else if (cp < 0x10000) {              // three octets
+                *(result++) = static_cast<uint8_t>((cp >> 12)         | 0xe0);
+                *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
+                *(result++) = static_cast<uint8_t>((cp & 0x3f)        | 0x80);
+            }
+            else {                                // four octets
+                *(result++) = static_cast<uint8_t>((cp >> 18)         | 0xf0);
+                *(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f)| 0x80);
+                *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
+                *(result++) = static_cast<uint8_t>((cp & 0x3f)        | 0x80);
+            }
+            return result;
+        }
+
+        template <typename octet_iterator>
+        uint32_t next(octet_iterator& it)
+        {
+            uint32_t cp = utf8::internal::mask8(*it);
+            typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it);
+            switch (length) {
+                case 1:
+                    break;
+                case 2:
+                    it++;
+                    cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);
+                    break;
+                case 3:
+                    ++it; 
+                    cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
+                    ++it;
+                    cp += (*it) & 0x3f;
+                    break;
+                case 4:
+                    ++it;
+                    cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);                
+                    ++it;
+                    cp += (utf8::internal::mask8(*it) << 6) & 0xfff;
+                    ++it;
+                    cp += (*it) & 0x3f; 
+                    break;
+            }
+            ++it;
+            return cp;
+        }
+
+        template <typename octet_iterator>
+        uint32_t peek_next(octet_iterator it)
+        {
+            return utf8::unchecked::next(it);
+        }
+
+        template <typename octet_iterator>
+        uint32_t prior(octet_iterator& it)
+        {
+            while (utf8::internal::is_trail(*(--it))) ;
+            octet_iterator temp = it;
+            return utf8::unchecked::next(temp);
+        }
+
+        template <typename octet_iterator, typename distance_type>
+        void advance (octet_iterator& it, distance_type n)
+        {
+            const distance_type zero(0);
+            if (n < zero) {
+                // backward
+                for (distance_type i = n; i < zero; ++i)
+                    utf8::unchecked::prior(it);
+            } else {
+                // forward
+                for (distance_type i = zero; i < n; ++i)
+                    utf8::unchecked::next(it);
+            }
+        }
+
+        template <typename octet_iterator>
+        typename std::iterator_traits<octet_iterator>::difference_type
+        distance (octet_iterator first, octet_iterator last)
+        {
+            typename std::iterator_traits<octet_iterator>::difference_type dist;
+            for (dist = 0; first < last; ++dist) 
+                utf8::unchecked::next(first);
+            return dist;
+        }
+
+        template <typename u16bit_iterator, typename octet_iterator>
+        octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
+        {
+            while (start != end) {
+                uint32_t cp = utf8::internal::mask16(*start++);
+            // Take care of surrogate pairs first
+                if (utf8::internal::is_lead_surrogate(cp)) {
+                    uint32_t trail_surrogate = utf8::internal::mask16(*start++);
+                    cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
+                }
+                result = utf8::unchecked::append(cp, result);
+            }
+            return result;
+        }
+
+        template <typename u16bit_iterator, typename octet_iterator>
+        u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
+        {
+            while (start < end) {
+                uint32_t cp = utf8::unchecked::next(start);
+                if (cp > 0xffff) { //make a surrogate pair
+                    *result++ = static_cast<uint16_t>((cp >> 10)   + internal::LEAD_OFFSET);
+                    *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
+                }
+                else
+                    *result++ = static_cast<uint16_t>(cp);
+            }
+            return result;
+        }
+
+        template <typename octet_iterator, typename u32bit_iterator>
+        octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
+        {
+            while (start != end)
+                result = utf8::unchecked::append(*(start++), result);
+
+            return result;
+        }
+
+        template <typename octet_iterator, typename u32bit_iterator>
+        u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
+        {
+            while (start < end)
+                (*result++) = utf8::unchecked::next(start);
+
+            return result;
+        }
+
+        // The iterator class
+        template <typename octet_iterator>
+          class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { 
+            octet_iterator it;
+            public:
+            iterator () {}
+            explicit iterator (const octet_iterator& octet_it): it(octet_it) {}
+            // the default "big three" are OK
+            octet_iterator base () const { return it; }
+            uint32_t operator * () const
+            {
+                octet_iterator temp = it;
+                return utf8::unchecked::next(temp);
+            }
+            bool operator == (const iterator& rhs) const 
+            { 
+                return (it == rhs.it);
+            }
+            bool operator != (const iterator& rhs) const
+            {
+                return !(operator == (rhs));
+            }
+            iterator& operator ++ () 
+            {
+                ::std::advance(it, utf8::internal::sequence_length(it));
+                return *this;
+            }
+            iterator operator ++ (int)
+            {
+                iterator temp = *this;
+                ::std::advance(it, utf8::internal::sequence_length(it));
+                return temp;
+            }  
+            iterator& operator -- ()
+            {
+                utf8::unchecked::prior(it);
+                return *this;
+            }
+            iterator operator -- (int)
+            {
+                iterator temp = *this;
+                utf8::unchecked::prior(it);
+                return temp;
+            }
+          }; // class iterator
+
+    } // namespace utf8::unchecked
+} // namespace utf8 
+
+
+#endif // header guard
+
index f53ca0701fdc963b535f866175d9cceeea720fd6..4b875ca61c70ba738b164be0eb573c4120cdd36c 100644 (file)
@@ -1,3 +1,2 @@
-inspircd_version.h\r
-inspircd_config.h\r
+config.h\r
 inspircd.rc\r
index c704f1df05e746421c5e5a978b37e8838227d388..be6617b8b638d43b679b6c249d74c5bb92b28ce5 100644 (file)
@@ -2,25 +2,28 @@ 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}/../")
 
+# Build with multiple processes
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+
 # Use our own NSIS template
 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}")
+string(REGEX REPLACE ".*InspIRCd-([^\"]+).*" "\\1" VERSION_FULL "${VERSIONSH}")
 
 if(MSVC)
        # Without /SAFESEH:NO old libraries compiled with VS 2010 or older won't link correctly to VS2012 (eg, extra module libs)
@@ -41,7 +44,6 @@ endif(MSVC)
 file(GLOB INSPIRCD_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
 "${INSPIRCD_BASE}/win/inspircd_win32wrapper.cpp"
 "${INSPIRCD_BASE}/win/win32service.cpp" "${INSPIRCD_BASE}/src/*.cpp"
-"${INSPIRCD_BASE}/src/modes/*.cpp"
 "${INSPIRCD_BASE}/src/socketengines/socketengine_select.cpp"
 "${INSPIRCD_BASE}/src/threadengines/threadengine_win32.cpp")
 list(SORT INSPIRCD_SOURCES)
@@ -56,8 +58,7 @@ if(MSVC)
 endif(MSVC)
 
 configure_file("${INSPIRCD_BASE}/win/inspircd.rc.cmake" "${INSPIRCD_BASE}/win/inspircd.rc")
-configure_file("${INSPIRCD_BASE}/win/inspircd_version.h.cmake" "${INSPIRCD_BASE}/win/inspircd_version.h")
-configure_file("${INSPIRCD_BASE}/win/inspircd_config.h.cmake" "${INSPIRCD_BASE}/win/inspircd_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)
@@ -79,10 +80,10 @@ file(GLOB_RECURSE EXAMPLE_LOCALES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIR
 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
@@ -90,10 +91,10 @@ if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
 
        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/LICENSE.txt")
 
        set(CPACK_GENERATOR "NSIS")
@@ -103,7 +104,7 @@ if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
        set(CPACK_NSIS_MUI_ICON "${INSPIRCD_BASE}/win\\\\inspircd.ico")
        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_URL_INFO_ABOUT "https://www.inspircd.org")
        set(CPACK_NSIS_COMPRESSOR "/SOLID zlib")
 
        include(CPack)
index 0b74cc98f43a9f9d6f3a80c4413e56a8158c625a..2e6abd7842b76a37cad9ebbc37b87e6c5d7017ad 100644 (file)
@@ -43,7 +43,7 @@
 \r
 ;--- Component support macros: ---\r
 ; The code for the add/remove functionality is from:\r
-;   http://nsis.sourceforge.net/Add/Remove_Functionality\r
+;   https://nsis.sourceforge.io/Add/Remove_Functionality\r
 ; It has been modified slightly and extended to provide\r
 ; inter-component dependencies.\r
 Var AR_SecFlags\r
index 908cd39202595be1a6b7aa29b1672ef1fd9bde98..437ff7e4f27d476395ab7cc8ab9f2ea28153ebbb 100644 (file)
@@ -1,9 +1,9 @@
 Building InspIRCd for Windows:\r
 \r
 Prerequisites:\r
-       Visual Studio 2010 or newer (http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products)\r
-       CMake 2.8 or newer (http://www.cmake.org/)\r
-       If building the installer, NSIS http://nsis.sourceforge.net/\r
+       Visual Studio 2015 or newer (https://www.visualstudio.com)\r
+       CMake 2.8 or newer (https://cmake.org)\r
+       If building the installer, NSIS (https://nsis.sourceforge.io)\r
 \r
 Configuring:\r
        First copy any extra modules from extras (such as m_mysql) to the modules directory that you want to build.\r
@@ -53,4 +53,4 @@ Building the installer:
        Locate the PACKAGE project on Visual Studio's Solution Explorer and build it. This will generate an InspIRCd-x.x.x.exe\r
        installer in the build directory which is ready to be distributed.\r
        \r
-       If you are building using NMake Makefiles or do not want to build the installer in Visual Studio, simply use "cpack".
\ No newline at end of file
+       If you are building using NMake Makefiles or do not want to build the installer in Visual Studio, simply use "cpack".\r
index ba52ad5d2dec3a719ec7d408fd704ee8705005eb..36a6d8e82a67a38d996e5ebbc574c1db7f53be45 100644 (file)
@@ -1,8 +1,8 @@
 101            ICON            "inspircd.ico"\r
 \r
 1 VERSIONINFO\r
-  FILEVERSION    @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@\r
-  PRODUCTVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@\r
+  FILEVERSION    @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@\r
+  PRODUCTVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@\r
   FILEFLAGSMASK  0x3fL\r
 #ifdef _DEBUG\r
   FILEFLAGS 0x1L\r
@@ -17,14 +17,14 @@ BEGIN
     BEGIN\r
         BLOCK "040904b0"\r
         BEGIN\r
-            VALUE "Comments", "InspIRCd @MAJOR_VERSION@.@MINOR_VERSION@ IRC Daemon"\r
+            VALUE "Comments", "InspIRCd @VERSION_MAJOR@.@VERSION_MINOR@ IRC Daemon"\r
             VALUE "CompanyName", "InspIRCd Development Team"\r
             VALUE "FileDescription", "InspIRCd"\r
             VALUE "FileVersion", "@FULL_VERSION@"\r
             VALUE "InternalName", "InspIRCd"\r
-            VALUE "LegalCopyright", "Copyright (c) 2015 InspIRCd Development Team"\r
+            VALUE "LegalCopyright", "Copyright (C) InspIRCd Development Team"\r
             VALUE "OriginalFilename", "inspircd.exe"\r
-            VALUE "ProductName", "InspIRCd - The Inspire IRC Daemon"\r
+            VALUE "ProductName", "InspIRCd - Internet Relay Chat Daemon"\r
             VALUE "ProductVersion", "@FULL_VERSION@"\r
         END\r
     END\r
diff --git a/win/inspircd_config.h.cmake b/win/inspircd_config.h.cmake
deleted file mode 100644 (file)
index f98cd39..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef INSPIRCD_CONFIG_H\r
-#define INSPIRCD_CONFIG_H\r
-\r
-#define CONFIG_PATH "@CONF_PATH@"\r
-#define MOD_PATH "@MODULE_PATH@"\r
-#define DATA_PATH "@DATA_PATH@"\r
-#define LOG_PATH "@LOG_PATH@"\r
-#define MAXBUF 514\r
-\r
-#include "inspircd_win32wrapper.h"\r
-#include "threadengines/threadengine_win32.h"\r
-\r
-#endif
\ No newline at end of file
index 3987083172f5c2ae8c4c729f4498c95e6ed90d3b..2093bf3a8ed09d3c72e69903ac61ed8cf6aa503d 100644 (file)
@@ -16,6 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+
 #include <windows.h>
 #include <exception>
 #include <new>
diff --git a/win/inspircd_version.h.cmake b/win/inspircd_version.h.cmake
deleted file mode 100644 (file)
index 1aec12b..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#define BRANCH "@MAJOR_VERSION@.@MINOR_VERSION@"\r
-#define VERSION "@FULL_VERSION@"\r
-#define REVISION "0"\r
-#define SYSTEM "@CMAKE_SYSTEM@"
\ No newline at end of file
index d66797f139a5a09665ea38b50fa88358401a7ba9..fc739f9bd17916ecb8c083d097efa3dec461094d 100644 (file)
@@ -22,6 +22,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+
 #include "inspircd_win32wrapper.h"
 #include "inspircd.h"
 #include "configreader.h"
 #include <errno.h>
 #include <assert.h>
 
-CoreExport const char *insp_inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
-{
-
-       if (af == AF_INET)
-       {
-               struct sockaddr_in in;
-               memset(&in, 0, sizeof(in));
-               in.sin_family = AF_INET;
-               memcpy(&in.sin_addr, src, sizeof(struct in_addr));
-               if (getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST) == 0)
-                       return dst;
-       }
-       else if (af == AF_INET6)
-       {
-               struct sockaddr_in6 in;
-               memset(&in, 0, sizeof(in));
-               in.sin6_family = AF_INET6;
-               memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
-               if (getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST) == 0)
-                       return dst;
-       }
-       return NULL;
-}
-
-CoreExport int insp_inet_pton(int af, const char *src, void *dst)
-{
-       int address_length;
-       sockaddr_storage sa;
-       sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&sa);
-       sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(&sa);
-
-       switch (af)
-       {
-               case AF_INET:
-                       address_length = sizeof(sockaddr_in);
-                       break;
-               case AF_INET6:
-                       address_length = sizeof(sockaddr_in6);
-                       break;
-               default:
-                       return -1;
-       }
-
-       if (!WSAStringToAddress(static_cast<LPSTR>(const_cast<char *>(src)), af, NULL, reinterpret_cast<LPSOCKADDR>(&sa), &address_length))
-       {
-               switch (af)
-               {
-                       case AF_INET:
-                               memcpy(dst, &sin->sin_addr, sizeof(in_addr));
-                               break;
-                       case AF_INET6:
-                               memcpy(dst, &sin6->sin6_addr, sizeof(in6_addr));
-                               break;
-               }
-               return 1;
-       }
-       
-       return 0;
-}
-
 CoreExport DIR * opendir(const char * path)
 {
        std::string search_path = std::string(path) + "\\*.*";
@@ -163,7 +104,7 @@ int getopt_long(int ___argc, char *const *___argv, const char *__shortopts, cons
 //                                     optind++;               // Trash this next argument, we won't be needing it.
                                        par = ___argv[optind-1];
                                }
-                       }                       
+                       }
 
                        // increment the argument for next time
 //                     optind++;
@@ -189,9 +130,9 @@ int getopt_long(int ___argc, char *const *___argv, const char *__shortopts, cons
                        {
                                if (__longopts[i].val == -1 || par == 0)
                                        return 1;
-                               
+
                                return __longopts[i].val;
-                       }                       
+                       }
                        break;
                }
        }
index d7ceb73aa46170a437f9468c9f958825e75bc20e..a6ca0da6e7754b81c9110559f16bfde95e95b595 100644 (file)
  */
 
 
+#pragma once
+
 /* Windows Port
    Wrapper Functions/Definitions
    By Burlex */
-
-#ifndef INSPIRCD_WIN32WRAPPER_H
-#define INSPIRCD_WIN32WRAPPER_H
-
 /*
  * Starting with PSAPI version 2 for Windows 7 and Windows Server 2008 R2, this function is defined as K32GetProcessMemoryInfo in Psapi.h and exported
  * in Kernel32.lib and Kernel32.dll. However, you should always call this function as GetProcessMemoryInfo. To ensure correct resolution of symbols
  * for programs that will run on earlier versions of Windows, add Psapi.lib to the TARGETLIBS macro and compile the program with PSAPI_VERSION=1.
- * 
+ *
  * We do this before anything to make sure it's done.
  */
 #define PSAPI_VERSION 1
@@ -45,9 +43,6 @@
 #define VC_EXTRALEAN
 #define WIN32_LEAN_AND_MEAN
 
-/* They just have to be *different*, don't they. */
-#define PATH_MAX MAX_PATH
-
 /* Macros for exporting symbols - dependant on what is being compiled */
 
 #ifdef DLL_BUILD
 #include <sys/stat.h>
 #include <direct.h>
 #include <process.h>
+#include <io.h>
+
+#define F_OK            0       /* test for existence of file */
+#define X_OK            (1<<0)  /* test for execute or search permission */
+#define W_OK            (1<<1)  /* test for write permission */
+#define R_OK            (1<<2)  /* test for read permission */
+
+// Windows defines these already.
+#undef ERROR
+#undef min
+#undef max
 
 /* strcasecmp is not defined on windows by default */
 #define strcasecmp _stricmp
 
 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);
-
-/* Convert struct to formatted (xxx.xxx.xxx.xxx) string */
-CoreExport const char * insp_inet_ntop(int af, const void * src, char * dst, socklen_t cnt);
-
-/* inet_pton/ntop require at least NT 6.0 */
-#define inet_pton insp_inet_pton
-#define inet_ntop insp_inet_ntop
-
-/* Safe printf functions aren't defined in VC++ releases older than v14 */
-#if _MSC_VER <= 1800
-#define snprintf _snprintf
-#define vsnprintf _vsnprintf
-#endif
-
-/* Unix-style sleep (argument is in seconds) */
-__inline void sleep(int seconds) { Sleep(seconds * 1000); }
-
 /* _popen, _pclose */
 #define popen _popen
 #define pclose _pclose
 
-/* IPV4 only convert string to address struct */
-__inline int inet_aton(const char *cp, struct in_addr *addr)
-{ 
-       addr->s_addr = inet_addr(cp);
-       return (addr->s_addr == INADDR_NONE) ? 0 : 1;
-};
-
 /* getopt() wrapper */
 #define no_argument            0
 #define required_argument      1
@@ -139,14 +119,6 @@ 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);
 CoreExport void closedir(DIR * handle);
@@ -181,18 +153,10 @@ CoreExport void closedir(DIR * handle);
 // warning C4706: assignment within conditional expression
 #pragma warning(disable:4706)
 
-// warning C4355: 'this' : used in base member initializer list
-// This warning is disabled by default since VC2012
-#if _MSC_VER < 1700
-#pragma warning(disable:4355)
-#endif
-
 /* Shared memory allocation functions */
 void * ::operator new(size_t iSize);
 void ::operator delete(void * ptr);
 
-#define DISABLE_WRITEV
-
 #include <exception>
 
 class CWin32Exception : public std::exception
@@ -208,5 +172,37 @@ private:
        DWORD dwErrorCode;
 };
 
-#endif
+// 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;
+}
+
+// This wrapper is just so we don't need to do #ifdef _WIN32 everywhere in the socket code. It is
+// not actually used and does not need to be the same size as sockaddr_un on UNIX systems.
+struct sockaddr_un
+{
+       ADDRESS_FAMILY sun_family;
+       char sun_path[6];
+};
index 97792cc2998b958faaa9e96b6d61c2f366ad3990..159cd1012ee96b9ae3147ba78b4275e7aecb63b5 100644 (file)
@@ -1,14 +1,14 @@
-@echo off\r
-\r
-echo This program will generate SSL certificates for m_ssl_gnutls.so\r
-echo Ensure certtool.exe is in your system path. It can be downloaded\r
-echo at ftp://ftp.gnu.org/gnu/gnutls/w32/. If you do not know the answer\r
-echo to one of the questions just press enter.\r
-echo.\r
-\r
-pause\r
-\r
-certtool --generate-privkey --outfile conf/key.pem\r
-certtool --generate-self-signed --load-privkey conf/key.pem --outfile conf/cert.pem\r
-\r
+@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
index 5778d944adda691fd572bf0c8cb4c0d28b648f50..094078aabd88c936acf87163d5373553205655e8 100644 (file)
@@ -2,33 +2,49 @@
 # so copy the file out of extra/\r
 file(COPY "${INSPIRCD_BASE}/src/modules/extra/m_regex_stdlib.cpp" DESTINATION "${INSPIRCD_BASE}/src/modules/")\r
 \r
-file(GLOB INSPIRCD_MODULES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIRCD_BASE}/src/commands/*.cpp" "${INSPIRCD_BASE}/src/modules/*.cpp")\r
+file(GLOB INSPIRCD_MODULES "${INSPIRCD_BASE}/src/coremods/core_*" "${INSPIRCD_BASE}/src/modules/m_*")\r
 list(SORT INSPIRCD_MODULES)\r
 \r
 add_definitions("-DDLL_BUILD")\r
 \r
+file(GLOB INSPIRCD_VENDORS "${INSPIRCD_BASE}/vendor/**")\r
+foreach(INSPIRCD_VENDOR ${INSPIRCD_VENDORS})\r
+       if(NOT IS_DIRECTORY ${INSPIRCD_VENDOR})\r
+               list(REMOVE_ITEM INSPIRCD_VENDORS ${INSPIRCD_VENDOR})\r
+       endif()\r
+endforeach()\r
+\r
 foreach(MODULE_NAME ${INSPIRCD_MODULES})\r
-       string(REGEX REPLACE "^.*[/\\](.*).cpp$" "\\1.so" SO_NAME ${MODULE_NAME})\r
-       add_library(${SO_NAME} MODULE ${MODULE_NAME})\r
-       set_target_properties(${SO_NAME} PROPERTIES PREFIX "" SUFFIX "")\r
+       if(IS_DIRECTORY "${MODULE_NAME}")\r
+               string(REGEX REPLACE "^.*[/\\](.*)$" "\\1" BASE_NAME ${MODULE_NAME})\r
+       else(IS_DIRECTORY "${MODULE_NAME}")\r
+               string(REGEX REPLACE "^.*[/\\](.*).cpp$" "\\1" BASE_NAME ${MODULE_NAME})\r
+       endif(IS_DIRECTORY "${MODULE_NAME}")\r
+       set(SO_NAME "${BASE_NAME}.so")\r
+\r
+       if(IS_DIRECTORY "${MODULE_NAME}")\r
+               file(GLOB MODULES_SUBDIR_SRCS "${MODULE_NAME}/*.cpp")\r
+               list(SORT MODULES_SUBDIR_SRCS)\r
+               add_library(${SO_NAME} MODULE ${MODULES_SUBDIR_SRCS})\r
+       else(IS_DIRECTORY "${MODULE_NAME}")\r
+               add_library(${SO_NAME} MODULE ${MODULE_NAME})\r
+       endif(IS_DIRECTORY "${MODULE_NAME}")\r
+\r
+       # Generate the module and set its linker flags, also set it to depend on the main executable to be built beforehand\r
        target_link_libraries(${SO_NAME} inspircd)\r
        add_dependencies(${SO_NAME} inspircd)\r
+       target_include_directories(${SO_NAME} PRIVATE ${INSPIRCD_VENDORS})\r
        if(MSVC)\r
                target_link_libraries(${SO_NAME} win32_memory)\r
                add_dependencies(${SO_NAME} win32_memory)\r
        endif(MSVC)\r
-       install(TARGETS ${SO_NAME} DESTINATION modules)\r
-endforeach(MODULE_NAME ${INSPIRCD_MODULES})\r
 \r
-file(GLOB INSPIRCD_MODULES_SPANNINGTREE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIRCD_BASE}/src/modules/m_spanningtree/*.cpp")\r
-list(SORT INSPIRCD_MODULES_SPANNINGTREE)\r
-\r
-add_library(m_spanningtree.so MODULE ${INSPIRCD_MODULES_SPANNINGTREE})\r
-set_target_properties(m_spanningtree.so PROPERTIES PREFIX "" SUFFIX "")\r
-target_link_libraries(m_spanningtree.so inspircd)\r
-add_dependencies(m_spanningtree.so inspircd)\r
-if(MSVC)\r
-       target_link_libraries(m_spanningtree.so win32_memory)\r
-       add_dependencies(m_spanningtree.so win32_memory)\r
-endif(MSVC)\r
-install(TARGETS m_spanningtree.so DESTINATION modules)
\ No newline at end of file
+       set_target_properties(${SO_NAME} PROPERTIES\r
+               PREFIX ""\r
+               SUFFIX ""\r
+               COMPILE_DEFINITIONS "MODNAME=\"${BASE_NAME}\""\r
+       )\r
+\r
+       # Set the module to be installed to the module directory\r
+       install(TARGETS ${SO_NAME} DESTINATION ${MODULE_DIR})\r
+endforeach(MODULE_NAME ${INSPIRCD_MODULES})\r
index c34e9957d318d6bfaa020fbb690e856733e088d2..448829a1d9dcaf4de169144337cb670f5a30cbe9 100644 (file)
@@ -17,7 +17,7 @@
  */
 
 
-#include "inspircd_config.h"
+#include "config.h"
 #include "inspircd.h"
 #include "exitcodes.h"
 #include <windows.h>
@@ -280,7 +280,7 @@ int main(int argc, char* argv[])
                }
                else
                {
-                       return EXIT_STATUS_INTERNAL;
+                       return EXIT_STATUS_SERVICE;
                }
        }
        return 0;
index e4500be131e897448996d6de3f5e492310f537aa..d8177eabcec3e1c8a19809a7bc4a2ed8412d9fb4 100644 (file)
@@ -15,7 +15,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+
 #pragma once
+
 #ifdef _WIN32
 
 /* Hook for win32service.cpp to exit properly with the service specific error code */